diff options
Diffstat (limited to 'tools/lz/uncomp.c')
-rw-r--r-- | tools/lz/uncomp.c | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/tools/lz/uncomp.c b/tools/lz/uncomp.c new file mode 100644 index 000000000..3544cd93b --- /dev/null +++ b/tools/lz/uncomp.c @@ -0,0 +1,92 @@ +#include "proto.h" + +struct command * get_commands_from_file (const unsigned char * data, unsigned short * restrict size, unsigned short * restrict slack) { + struct command * result = malloc(*size * sizeof(struct command)); + unsigned short remaining = *size; + struct command * current = result; + const unsigned char * rp = data; + while (1) { + if (!(remaining --)) goto error; + current -> command = *rp >> 5; + current -> count = *(rp ++) & 31; + if (current -> command == 7) { + current -> command = current -> count >> 2; + current -> count = (current -> count & 3) << 8; + if (current -> command == 7) { + // long commands inside long commands are not allowed, but if the count is 0x300 here, it means that the original byte was 0xff + if (current -> count == 0x300) break; + goto error; + } + if (!(remaining --)) goto error; + current -> count |= *(rp ++); + } + current -> count ++; + switch (current -> command) { + case 0: + if (remaining <= current -> count) goto error; + current -> value = rp - data; + rp += current -> count; + remaining -= current -> count; + case 3: + break; + case 1: case 2: { + unsigned char p; + if (remaining <= current -> command) goto error; + current -> value = 0; + for (p = 0; p < current -> command; p ++) current -> value |= *(rp ++) << (p << 3); + remaining -= current -> command; + } break; + default: + if (!(remaining --)) goto error; + if ((current -> value = *(rp ++)) & 128) + current -> value = 127 - current -> value; + else { + if (!(remaining --)) goto error; + current -> value = (current -> value << 8) | *(rp ++); + } + } + current ++; + } + if (slack) *slack = *size - (rp - data); + *size = current - result; + return realloc(result, *size * sizeof(struct command)); + error: + free(result); + return NULL; +} + +unsigned char * get_uncompressed_data (const struct command * commands, const unsigned char * compressed, unsigned short * size) { + const struct command * limit = commands + *size; + unsigned char * result = malloc(MAX_FILE_SIZE + MAX_COMMAND_COUNT); + unsigned char * current = result; + unsigned short p; + for (; commands < limit; commands ++) { + switch (commands -> command) { + case 0: + memcpy(current, compressed + commands -> value, commands -> count); + current += commands -> count; + break; + case 1: case 2: + for (p = 0; p < commands -> count; p ++) *(current ++) = commands -> value >> ((p % commands -> command) << 3); + break; + case 3: + memset(current, 0, commands -> count); + current += commands -> count; + break; + default: { + const unsigned char * ref = ((commands -> value < 0) ? current : result) + commands -> value; + for (p = 0; p < commands -> count; p ++) { + current[p] = ref[(commands -> command == 6) ? -(int) p : p]; + if (commands -> command == 5) current[p] = bit_flipping_table[current[p]]; + } + current += commands -> count; + } + } + if ((current - result) > MAX_FILE_SIZE) { + free(result); + return NULL; + } + } + *size = current - result; + return result; +} |