src/util/text-codec.c (view raw)
1/* Copyright (c) 2013-2016 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/text-codec.h>
7
8#include <mgba-util/string.h>
9#include <mgba-util/table.h>
10#include <mgba-util/vfs.h>
11
12struct TextCodecNode {
13 uint8_t* leaf;
14 size_t leafLength;
15 struct Table children;
16};
17
18static void _cleanTree(void* value) {
19 struct TextCodecNode* node = value;
20 if (node->leaf) {
21 free(node->leaf);
22 }
23 TableDeinit(&node->children);
24 free(node);
25}
26
27static struct TextCodecNode* _createNode(void) {
28 struct TextCodecNode* node = malloc(sizeof(*node));
29 node->leaf = NULL;
30 node->leafLength = 0;
31 TableInit(&node->children, 32, _cleanTree);
32 return node;
33}
34
35static void _insertLeaf(struct TextCodecNode* node, uint8_t* word, size_t wordLength, uint8_t* output, size_t outputLength) {
36 if (!wordLength) {
37 node->leafLength = outputLength;
38 node->leaf = malloc(outputLength);
39 memcpy(node->leaf, output, outputLength);
40 return;
41 }
42 struct TextCodecNode* subnode = TableLookup(&node->children, word[0]);
43 if (!subnode) {
44 subnode = _createNode();
45 TableInsert(&node->children, word[0], subnode);
46 }
47 _insertLeaf(subnode, &word[1], wordLength - 1, output, outputLength);
48}
49
50bool TextCodecLoadTBL(struct TextCodec* codec, struct VFile* vf, bool createReverse) {
51 codec->forwardRoot = _createNode();
52 if (createReverse) {
53 codec->reverseRoot = _createNode();
54 } else {
55 codec->reverseRoot = NULL;
56 }
57
58 char lineBuffer[128];
59 uint8_t wordBuffer[5];
60 ssize_t length;
61 while ((length = vf->readline(vf, lineBuffer, sizeof(lineBuffer))) > 0) {
62 memset(wordBuffer, 0, sizeof(wordBuffer));
63 if (lineBuffer[length - 1] == '\n' || lineBuffer[length - 1] == '\r') {
64 --length;
65 }
66 if (!length) {
67 continue;
68 }
69 if (lineBuffer[length - 1] == '\r') {
70 --length;
71 }
72 if (!length) {
73 continue;
74 }
75 size_t i;
76 for (i = 0; i < sizeof(wordBuffer) - 1 && i < (size_t) length; ++i) {
77 if (!hex8(&lineBuffer[i * 2], &wordBuffer[i])) {
78 break;
79 }
80 }
81 if (!i) {
82 uint8_t value;
83 if (!hex8(lineBuffer, &value)) {
84 switch (lineBuffer[0]) {
85 case '*':
86 lineBuffer[0] = '\n';
87 break;
88 case '\\':
89 lineBuffer[0] = '\x1E';
90 break;
91 case '/':
92 lineBuffer[0] = '\x1F';
93 break;
94 default:
95 return false;
96 }
97 size_t start = 1;
98 if (lineBuffer[1] == '=') {
99 start = 2;
100 }
101 for (i = 0; i < sizeof(wordBuffer) - 1; ++i) {
102 if (!hex8(&lineBuffer[start + i * 2], &wordBuffer[i])) {
103 break;
104 }
105 }
106 if (i == 0) {
107 return false;
108 }
109 _insertLeaf(codec->forwardRoot, wordBuffer, i, (uint8_t*) lineBuffer, 1);
110 if (codec->reverseRoot) {
111 _insertLeaf(codec->reverseRoot, (uint8_t*) lineBuffer, 1, wordBuffer, i);
112 }
113 }
114 } else {
115 if (lineBuffer[i * 2] != '=') {
116 return false;
117 }
118 size_t offset = i * 2 + 1;
119 _insertLeaf(codec->forwardRoot, wordBuffer, i, (uint8_t*) &lineBuffer[offset], length - offset);
120 if (codec->reverseRoot) {
121 _insertLeaf(codec->reverseRoot, (uint8_t*) &lineBuffer[offset], length - offset, wordBuffer, i);
122 }
123 }
124 }
125 return length == 0;
126}
127
128void TextCodecDeinit(struct TextCodec* codec) {
129 if (codec->forwardRoot) {
130 _cleanTree(codec->forwardRoot);
131 codec->forwardRoot = NULL;
132 }
133 if (codec->reverseRoot) {
134 _cleanTree(codec->reverseRoot);
135 codec->reverseRoot = NULL;
136 }
137}
138
139void TextCodecStartDecode(struct TextCodec* codec, struct TextCodecIterator* iter) {
140 iter->root = codec->forwardRoot;
141 iter->current = iter->root;
142}
143
144void TextCodecStartEncode(struct TextCodec* codec, struct TextCodecIterator* iter) {
145 iter->root = codec->reverseRoot;
146 iter->current = iter->root;
147}
148
149static ssize_t _TextCodecFinishInternal(struct TextCodecNode* node, uint8_t* output, size_t outputLength) {
150 if (outputLength > node->leafLength) {
151 outputLength = node->leafLength;
152 }
153 if (node->leafLength == 0) {
154 return -1;
155 }
156 memcpy(output, node->leaf, outputLength);
157 return node->leafLength;
158}
159
160ssize_t TextCodecAdvance(struct TextCodecIterator* iter, uint8_t byte, uint8_t* output, size_t outputLength) {
161 struct TextCodecNode* node = TableLookup(&iter->current->children, byte);
162 if (!node) {
163 ssize_t size = _TextCodecFinishInternal(iter->current, output, outputLength);
164 if (size < 0) {
165 size = 0;
166 }
167 output += size;
168 outputLength -= size;
169 if (!outputLength) {
170 return size;
171 }
172 if (iter->current == iter->root) {
173 return -1;
174 }
175 iter->current = iter->root;
176 ssize_t newSize = TextCodecAdvance(iter, byte, output, outputLength);
177 if (newSize < 0 && size > 0) {
178 return size;
179 }
180 return newSize + size;
181 }
182 if (TableSize(&node->children) == 0) {
183 iter->current = iter->root;
184 return _TextCodecFinishInternal(node, output, outputLength);
185 }
186 iter->current = node;
187 return 0;
188}
189
190ssize_t TextCodecFinish(struct TextCodecIterator* iter, uint8_t* output, size_t outputLength) {
191 struct TextCodecNode* node = iter->current;
192 iter->current = iter->root;
193 if (node->leafLength == 0) {
194 return 0;
195 }
196 return _TextCodecFinishInternal(node, output, outputLength);
197}