summaryrefslogtreecommitdiff
path: root/tools/libdol2asm/data/string.py
blob: 23a475988aecc9309d883c6220d19f6483c65c30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
from dataclasses import dataclass, field
from typing import List

from ..builder import AsyncBuilder
from .. import util
from ..types import *
from .base import *
from .identifier import *

def string_to_cstr(data):
    return "".join(data)


def escape_char(v):
    if v == "\n":
        return "\\n"
    elif v == "\t":
        return "\\t"
    elif v == "\v":
        return "\\v"
    elif v == "\b":
        return "\\b"
    elif v == "\r":
        return "\\r"
    elif v == "\f":
        return "\\f"
    elif v == "\a":
        return "\\a"
    elif v == "\\":
        return "\\\\"
    elif v == "\"":
        return "\\\""
    elif ord(v) < 32:
        return "\"\"\\x%02X\"\"" % ord(v)
    else:
        return v


def escape_char_hard(v):
    return "\"\"\\x%02X\"\"" % v


def escape_string(data):
    return [escape_char(x) for x in data]


def escape_char_hex(v):
    if v == 0:
        return "\\0"
    return "\\x%02X" % v


def escape_full_string(data):
    return [escape_char_hex(x) for x in data]


@dataclass(eq=False)
class String(ArbitraryData):
    encoding: str = None
    decoded_string: str = None
    string_base: "StringBase" = None

    def array_type(self):
        return self.element_type()

    def asm_reference(self, addr):
        if self.string_base:
            return self.string_base.asm_reference(addr)
        else:
            return super().asm_reference(addr)

    def cpp_reference(self, accessor, addr):
        if self.string_base:
            return self.string_base.cpp_reference(accessor, addr)
        else:
            return super().cpp_reference(accessor, addr)

    async def export_declaration(self, exporter, builder: AsyncBuilder, force_active=True):
        if force_active:
            await builder.write("#pragma push")
            await builder.write("#pragma force_active on")
        sjis = self.decoded_string.encode("shift_jisx0213")
        if 0x5c in sjis:
            await builder.write("// MWCC ignores mapping of some japanese characters using the ")
            await builder.write("// byte 0x5C (ASCII '\\'). This is why this string is hex-encoded.")
            data = escape_full_string(sjis)
        else:
            data = escape_string(self.decoded_string)

        await builder.write_nonewline("SECTION_DEAD ")
        if self.is_static:
            await self.export_static(builder)
        await builder.write_nonewline(self.array_type().decl(self.identifier.label))
        await String.export_string(builder, data)

        if self.padding > 0:
            assert len(self.padding_data) == self.padding
            assert self.padding_data[-1] == 0
            data = escape_full_string(self.padding_data[:-1])
            await builder.write("/* @stringBase0 padding */")
            await builder.write_nonewline("SECTION_DEAD ")
            await builder.write_nonewline("static ")
            await builder.write_nonewline(self.array_type().decl(f"pad_{self.end:08X}"))
            await String.export_string(builder, data)
        if force_active:
            await builder.write("#pragma pop")

    @staticmethod
    async def export_string(builder: AsyncBuilder, data: List[str]):
        if len(data) < 32:
            await builder.write(f" = \"{string_to_cstr(data)}\";")
        else:
            await builder.write(f" = ")
            data_chunks = util.chunks(data, 48)

            lines = []
            for chunk in data_chunks:
                lines += [f"    \"{string_to_cstr(chunk)}\""]
            lines[-1] += ";"

            for line in lines:
                await builder.write(line)


@dataclass(eq=False)
class StringBase(ArbitraryData):
    strings: List[Symbol] = field(default_factory=list, repr=False)

    def valid_reference(self, addr):
        return addr >= self.start and addr < self.end

    def array_type(self):
        return self.element_type()

    @property
    def is_static(self):
        # having @stringBase0 as satatic would remove the forward reference to the lcf generated symbol,
        # this will not compile correctly. 
        return False


    def set_mlts(self, module: int, library: str, translation_unit: str, section: str):
        super().set_mlts(module, library, translation_unit, section)
        for string in self.strings:
            string.set_mlts(module, library, translation_unit, section)

    async def export_declaration(self, exporter, builder: AsyncBuilder):
        pass
        """
        await builder.write("#pragma push")
        await builder.write("#pragma force_active on")
        for string in self.strings:
            # if the @stringBase0 is static (which it will almost always be), setup 
            # so that the sub-strings are static.
            if self.is_static:
                string.reference_count.make_static()
            await string.export_declaration(exporter, builder)

        if self.padding > 0:
            assert len(self.padding_data) == self.padding
            assert self.padding_data[-1] == 0
            data = escape_full_string(self.padding_data[:-1])
            await builder.write("/* @stringBase0 padding */")
            await builder.write_nonewline("SECTION_DEAD ")
            await builder.write_nonewline("static ")
            await builder.write_nonewline(self.array_type().decl(f"pad_{self.end:08X}"))
            await String.export_string(builder, data)
        await builder.write("#pragma pop")
        """

    @staticmethod
    def create(symbol, strings, data, padding_data):
        return StringBase(
            Identifier("stringBase", symbol.addr, symbol.name),
            symbol.addr,
            symbol.size,
            data=data,
            data_type=PointerType(ConstType(CHAR)),
            padding=len(padding_data),
            padding_data=padding_data,
            strings=strings)