all repos — mgba @ 12aa775316589df17edf1685e5448aa2c1160b0e

mGBA Game Boy Advance Emulator

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}