/* * MVCOMP unpacker * Copyright (C) 2024 Mateusz Viste * * MIT License. * * This is a simple example implementation, not a production-ready tool. * * https://mvcomp.sourceforge.io */ #include #include #include /* unpacks an MVCOMP-compressed token to dst. returns length of uncompressed data. */ static unsigned short mvcomp_depack(unsigned char *dst, unsigned short buff16) { static unsigned char rawwords; /* number of uncompressible words */ unsigned char *dst_start = dst; /* token format is LLLL OOOO OOOO OOOO, where: * OOOO OOOO OOOO is the back reference offset (number of bytes-1 to rewind) * LLLL is the number of bytes (-1) that have to be copied from the offset. * * However, if LLLL is zero then the token's format is different: * 0000 RRRR BBBB BBBB * * The above form occurs when uncompressible data is encountered: * BBBB BBBB is the literal value of a byte to be copied * RRRR is the number of RAW (uncompressible) WORDS that follow (possibly 0) */ /* raw word? */ if (rawwords != 0) { unsigned short *dst16 = (void *)dst; *dst16 = buff16; dst += 2; rawwords--; /* literal byte? */ } else if ((buff16 & 0xF000) == 0) { *dst = buff16 & 0xff; dst++; rawwords = buff16 >> 8; /* number of RAW words that are about to follow */ /* else it's a backreference */ } else { unsigned char *src = dst - (buff16 & 0x0FFF) - 1; buff16 >>= 12; for (;;) { *dst = *src; dst++; src++; if (buff16 == 0) break; buff16--; } } return((unsigned short)(dst - dst_start)); } int main (int argc, char **argv) { FILE *in, *out; unsigned long bytes_in = 0, bytes_out = 0; unsigned char buff[1024 * 8]; unsigned char *buffptr = buff; if (argc != 3) { puts("usage: mvucomp file.mvc file.out"); return(1); } in = fopen(argv[2], "rb"); if (in != NULL) { fclose(in); puts("error: output file already exists"); return(1); } in = fopen(argv[1], "rb"); if (in == NULL) { puts("failed to open input file"); return(1); } out = fopen(argv[2], "wb"); if (out == NULL) { puts("failed to open output file"); fclose(in); return(1); } for (;;) { unsigned short token; unsigned short len; if (fread(&token, 1, 2, in) != 2) break; bytes_in += 2; len = mvcomp_depack(buffptr, token); if (fwrite(buffptr, 1, len, out) != len) { fprintf(stderr, "ERROR: fwrite() to destination failed - %s\n", strerror(errno)); break; } bytes_out += len; buffptr += len; /* rewind the buffer */ if ((buffptr - buff) > 6 * 1024) { memcpy(buff, buff + 2048, (size_t)((buffptr - buff) - 2048)); buffptr -= 2048; } } fclose(out); fclose(in); printf("%s (%lu bytes) -> %s (%lu bytes)\n", argv[1], bytes_in, argv[2], bytes_out); return(0); }