Skip to content

eo_writer

EoWriter

Source code in src/eolib/data/eo_writer.py
  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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
class EoWriter:
    def __init__(self):
        self.data = bytearray()
        self._string_sanitization_mode = False

    def add_byte(self, value: int) -> None:
        """
        Adds a raw byte to the writer data.

        Args:
            value (int): The byte to add.

        Raises:
            ValueError: If the value is above `0xFF`.
        """
        self._check_number_size(value, 0xFF)
        self.data.append(value)

    def add_bytes(self, bytes: bytes) -> None:
        """
        Adds raw bytes to the writer data.

        Args:
            bytes (bytes): The bytes to add.
        """
        self.data.extend(bytes)

    def add_char(self, number: int) -> None:
        """
        Adds an encoded 1-byte integer to the writer data.

        Args:
            number (int): The number to encode and add.

        Raises:
            ValueError: If the value is not below `CHAR_MAX`.
        """
        self._check_number_size(number, CHAR_MAX - 1)
        number_bytes = encode_number(number)
        self._add_bytes_with_length(number_bytes, 1)

    def add_short(self, number: int) -> None:
        """
        Adds an encoded 2-byte integer to the writer data.

        Args:
            number (int): The number to encode and add.

        Raises:
            ValueError: If the value is not below `SHORT_MAX`.
        """
        self._check_number_size(number, SHORT_MAX - 1)
        number_bytes = encode_number(number)
        self._add_bytes_with_length(number_bytes, 2)

    def add_three(self, number: int) -> None:
        """
        Adds an encoded 3-byte integer to the writer data.

        Args:
            number (int): The number to encode and add.

        Raises:
            ValueError: If the value is not below `THREE_MAX`.
        """
        self._check_number_size(number, THREE_MAX - 1)
        number_bytes = encode_number(number)
        self._add_bytes_with_length(number_bytes, 3)

    def add_int(self, number: int) -> None:
        """
        Adds an encoded 4-byte integer to the writer data.

        Args:
            number (int): The number to encode and add.

        Raises:
            ValueError: If the value is not below `INT_MAX`.
        """
        self._check_number_size(number, INT_MAX - 1)
        number_bytes = encode_number(number)
        self._add_bytes_with_length(number_bytes, 4)

    def add_string(self, string: str) -> None:
        """
        Adds a string to the writer data.

        Args:
            string (str): The string to be added.
        """
        string_bytes = self._encode_ansi(string)
        self._sanitize_string(string_bytes)
        self.add_bytes(string_bytes)

    def add_fixed_string(self, string: str, length: int, padded: bool = False) -> None:
        """
        Adds a fixed-length string to the writer data.

        Args:
            string (str): The string to be added.
            length (int): The expected length of the string.
            padded (bool, optional): True if the string should be padded to the length with
                trailing `0xFF` bytes. Defaults to False.
        """
        self._check_string_length(string, length, padded)
        string_bytes = self._encode_ansi(string)
        self._sanitize_string(string_bytes)
        if padded:
            string_bytes = self._add_padding(string_bytes, length)
        self.add_bytes(string_bytes)

    def add_encoded_string(self, string: str) -> None:
        """
        Adds an encoded string to the writer data.

        Args:
            string (str): The string to be encoded and added.
        """
        string_bytes = self._encode_ansi(string)
        self._sanitize_string(string_bytes)
        encode_string(string_bytes)
        self.add_bytes(string_bytes)

    def add_fixed_encoded_string(self, string: str, length: int, padded: bool = False) -> None:
        """
        Adds a fixed-length encoded string to the writer data.

        Args:
            string (str): The string to be encoded and added.
            length (int): The expected length of the string.
            padded (bool, optional): True if the string should be padded to the length with
                trailing `0xFF` bytes. Defaults to False.
        """
        self._check_string_length(string, length, padded)
        string_bytes = self._encode_ansi(string)
        self._sanitize_string(string_bytes)
        if padded:
            string_bytes = self._add_padding(string_bytes, length)
        encode_string(string_bytes)
        self.add_bytes(string_bytes)

    @property
    def string_sanitization_mode(self) -> bool:
        """
        Gets the string sanitization mode for the writer.

        Returns:
            bool: True if string sanitization is enabled.
        """
        return self._string_sanitization_mode

    @string_sanitization_mode.setter
    def string_sanitization_mode(self, string_sanitization_mode: bool) -> None:
        self._string_sanitization_mode = string_sanitization_mode

    def to_bytearray(self) -> bytearray:
        """
        Gets the writer data as a byte array.

        Returns:
            bytearray: A copy of the writer data as a byte array.
        """
        return self.data.copy()

    def __len__(self) -> int:
        """
        Gets the length of the writer data.

        Returns:
            int: The length of the writer data.
        """
        return len(self.data)

    def _add_bytes_with_length(self, bytes: bytes, bytes_length: int) -> None:
        """
        Adds raw bytes with a specified length to the writer data.

        Args:
            bytes (bytes): The bytes to add.
            bytes_length (int): The number of bytes to add.
        """
        self.data.extend(bytes[:bytes_length])

    def _sanitize_string(self, bytes: bytearray) -> None:
        if self.string_sanitization_mode:
            for i in range(len(bytes)):
                if bytes[i] == 0xFF:  # 'ΓΏ'
                    bytes[i] = 0x79  # 'y'

    @staticmethod
    def _check_number_size(number: int, max_value: int) -> None:
        if number > max_value:
            raise ValueError(f"Value {number} exceeds maximum of {max_value}.")

    @staticmethod
    def _add_padding(bytes: bytearray, length: int) -> bytearray:
        if len(bytes) == length:
            return bytes

        result = bytearray(length)
        result[: len(bytes)] = bytes
        result[len(bytes) :] = bytearray([0xFF] * (length - len(bytes)))

        return result

    @staticmethod
    def _check_string_length(string: str, length: int, padded: bool) -> None:
        if padded:
            if length >= len(string):
                return
            raise ValueError(f'Padded string "{string}" is too large for a length of {length}.')

        if len(string) != length:
            raise ValueError(f'String "{string}" does not have expected length of {length}.')

    @staticmethod
    def _encode_ansi(string: str) -> bytearray:
        """
        Encodes string to windows-1252 bytes.

        Args:
            string (str): The string to encode.

        Returns:
            bytearray: The encoded string.
        """
        return bytearray(string, 'windows-1252', 'replace')

data = bytearray() instance-attribute

string_sanitization_mode: bool property writable

Gets the string sanitization mode for the writer.

Returns:

Name Type Description
bool bool

True if string sanitization is enabled.

__init__()

Source code in src/eolib/data/eo_writer.py
7
8
9
def __init__(self):
    self.data = bytearray()
    self._string_sanitization_mode = False

add_byte(value)

Adds a raw byte to the writer data.

Parameters:

Name Type Description Default
value int

The byte to add.

required

Raises:

Type Description
ValueError

If the value is above 0xFF.

Source code in src/eolib/data/eo_writer.py
11
12
13
14
15
16
17
18
19
20
21
22
def add_byte(self, value: int) -> None:
    """
    Adds a raw byte to the writer data.

    Args:
        value (int): The byte to add.

    Raises:
        ValueError: If the value is above `0xFF`.
    """
    self._check_number_size(value, 0xFF)
    self.data.append(value)

add_bytes(bytes)

Adds raw bytes to the writer data.

Parameters:

Name Type Description Default
bytes bytes

The bytes to add.

required
Source code in src/eolib/data/eo_writer.py
24
25
26
27
28
29
30
31
def add_bytes(self, bytes: bytes) -> None:
    """
    Adds raw bytes to the writer data.

    Args:
        bytes (bytes): The bytes to add.
    """
    self.data.extend(bytes)

add_char(number)

Adds an encoded 1-byte integer to the writer data.

Parameters:

Name Type Description Default
number int

The number to encode and add.

required

Raises:

Type Description
ValueError

If the value is not below CHAR_MAX.

Source code in src/eolib/data/eo_writer.py
33
34
35
36
37
38
39
40
41
42
43
44
45
def add_char(self, number: int) -> None:
    """
    Adds an encoded 1-byte integer to the writer data.

    Args:
        number (int): The number to encode and add.

    Raises:
        ValueError: If the value is not below `CHAR_MAX`.
    """
    self._check_number_size(number, CHAR_MAX - 1)
    number_bytes = encode_number(number)
    self._add_bytes_with_length(number_bytes, 1)

add_short(number)

Adds an encoded 2-byte integer to the writer data.

Parameters:

Name Type Description Default
number int

The number to encode and add.

required

Raises:

Type Description
ValueError

If the value is not below SHORT_MAX.

Source code in src/eolib/data/eo_writer.py
47
48
49
50
51
52
53
54
55
56
57
58
59
def add_short(self, number: int) -> None:
    """
    Adds an encoded 2-byte integer to the writer data.

    Args:
        number (int): The number to encode and add.

    Raises:
        ValueError: If the value is not below `SHORT_MAX`.
    """
    self._check_number_size(number, SHORT_MAX - 1)
    number_bytes = encode_number(number)
    self._add_bytes_with_length(number_bytes, 2)

add_three(number)

Adds an encoded 3-byte integer to the writer data.

Parameters:

Name Type Description Default
number int

The number to encode and add.

required

Raises:

Type Description
ValueError

If the value is not below THREE_MAX.

Source code in src/eolib/data/eo_writer.py
61
62
63
64
65
66
67
68
69
70
71
72
73
def add_three(self, number: int) -> None:
    """
    Adds an encoded 3-byte integer to the writer data.

    Args:
        number (int): The number to encode and add.

    Raises:
        ValueError: If the value is not below `THREE_MAX`.
    """
    self._check_number_size(number, THREE_MAX - 1)
    number_bytes = encode_number(number)
    self._add_bytes_with_length(number_bytes, 3)

add_int(number)

Adds an encoded 4-byte integer to the writer data.

Parameters:

Name Type Description Default
number int

The number to encode and add.

required

Raises:

Type Description
ValueError

If the value is not below INT_MAX.

Source code in src/eolib/data/eo_writer.py
75
76
77
78
79
80
81
82
83
84
85
86
87
def add_int(self, number: int) -> None:
    """
    Adds an encoded 4-byte integer to the writer data.

    Args:
        number (int): The number to encode and add.

    Raises:
        ValueError: If the value is not below `INT_MAX`.
    """
    self._check_number_size(number, INT_MAX - 1)
    number_bytes = encode_number(number)
    self._add_bytes_with_length(number_bytes, 4)

add_string(string)

Adds a string to the writer data.

Parameters:

Name Type Description Default
string str

The string to be added.

required
Source code in src/eolib/data/eo_writer.py
89
90
91
92
93
94
95
96
97
98
def add_string(self, string: str) -> None:
    """
    Adds a string to the writer data.

    Args:
        string (str): The string to be added.
    """
    string_bytes = self._encode_ansi(string)
    self._sanitize_string(string_bytes)
    self.add_bytes(string_bytes)

add_fixed_string(string, length, padded=False)

Adds a fixed-length string to the writer data.

Parameters:

Name Type Description Default
string str

The string to be added.

required
length int

The expected length of the string.

required
padded bool

True if the string should be padded to the length with trailing 0xFF bytes. Defaults to False.

False
Source code in src/eolib/data/eo_writer.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def add_fixed_string(self, string: str, length: int, padded: bool = False) -> None:
    """
    Adds a fixed-length string to the writer data.

    Args:
        string (str): The string to be added.
        length (int): The expected length of the string.
        padded (bool, optional): True if the string should be padded to the length with
            trailing `0xFF` bytes. Defaults to False.
    """
    self._check_string_length(string, length, padded)
    string_bytes = self._encode_ansi(string)
    self._sanitize_string(string_bytes)
    if padded:
        string_bytes = self._add_padding(string_bytes, length)
    self.add_bytes(string_bytes)

add_encoded_string(string)

Adds an encoded string to the writer data.

Parameters:

Name Type Description Default
string str

The string to be encoded and added.

required
Source code in src/eolib/data/eo_writer.py
117
118
119
120
121
122
123
124
125
126
127
def add_encoded_string(self, string: str) -> None:
    """
    Adds an encoded string to the writer data.

    Args:
        string (str): The string to be encoded and added.
    """
    string_bytes = self._encode_ansi(string)
    self._sanitize_string(string_bytes)
    encode_string(string_bytes)
    self.add_bytes(string_bytes)

add_fixed_encoded_string(string, length, padded=False)

Adds a fixed-length encoded string to the writer data.

Parameters:

Name Type Description Default
string str

The string to be encoded and added.

required
length int

The expected length of the string.

required
padded bool

True if the string should be padded to the length with trailing 0xFF bytes. Defaults to False.

False
Source code in src/eolib/data/eo_writer.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def add_fixed_encoded_string(self, string: str, length: int, padded: bool = False) -> None:
    """
    Adds a fixed-length encoded string to the writer data.

    Args:
        string (str): The string to be encoded and added.
        length (int): The expected length of the string.
        padded (bool, optional): True if the string should be padded to the length with
            trailing `0xFF` bytes. Defaults to False.
    """
    self._check_string_length(string, length, padded)
    string_bytes = self._encode_ansi(string)
    self._sanitize_string(string_bytes)
    if padded:
        string_bytes = self._add_padding(string_bytes, length)
    encode_string(string_bytes)
    self.add_bytes(string_bytes)

to_bytearray()

Gets the writer data as a byte array.

Returns:

Name Type Description
bytearray bytearray

A copy of the writer data as a byte array.

Source code in src/eolib/data/eo_writer.py
161
162
163
164
165
166
167
168
def to_bytearray(self) -> bytearray:
    """
    Gets the writer data as a byte array.

    Returns:
        bytearray: A copy of the writer data as a byte array.
    """
    return self.data.copy()

__len__()

Gets the length of the writer data.

Returns:

Name Type Description
int int

The length of the writer data.

Source code in src/eolib/data/eo_writer.py
170
171
172
173
174
175
176
177
def __len__(self) -> int:
    """
    Gets the length of the writer data.

    Returns:
        int: The length of the writer data.
    """
    return len(self.data)