// // PAL DOS compress format (YJ_1) library // // Author: Lou Yihua // // Copyright 2006 - 2007 Lou Yihua // // This file is part of PAL library. // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // // Ported to C from C++ and modified for compatibility with Big-Endian // by Wei Mingzhi . // TODO: fix YJ_2 for big-endian #include "common.h" #ifndef PAL_WIN95 typedef struct _TreeNode { unsigned char value; unsigned char leaf; unsigned short level; unsigned int weight; struct _TreeNode *parent; struct _TreeNode *left; struct _TreeNode *right; } TreeNode; typedef struct _TreeNodeList { TreeNode *node; struct _TreeNodeList *next; } TreeNodeList; typedef struct _YJ_1_FILEHEADER { unsigned int Signature; // 'YJ_1' unsigned int UncompressedLength; // size before compression unsigned int CompressedLength; // size after compression unsigned short BlockCount; // number of blocks unsigned char Unknown; unsigned char HuffmanTreeLength; // length of huffman tree } YJ_1_FILEHEADER, *PYJ_1_FILEHEADER; typedef struct _YJ_1_BLOCKHEADER { unsigned short UncompressedLength; // maximum 0x4000 unsigned short CompressedLength; // including the header unsigned short LZSSRepeatTable[4]; unsigned char LZSSOffsetCodeLengthTable[4]; unsigned char LZSSRepeatCodeLengthTable[3]; unsigned char CodeCountCodeLengthTable[3]; unsigned char CodeCountTable[2]; } YJ_1_BLOCKHEADER, *PYJ_1_BLOCKHEADER; static unsigned int get_bits( const void *src, unsigned int *bitptr, unsigned int count ) { unsigned char *temp = ((unsigned char *)src) + ((*bitptr >> 4) << 1); unsigned int bptr = *bitptr & 0xf; unsigned short mask; *bitptr += count; if (count > 16 - bptr) { count = count + bptr - 16; mask = 0xffff >> bptr; return (((temp[0] | (temp[1] << 8)) & mask) << count) | ((temp[2] | (temp[3] << 8)) >> (16 - count)); } else return (((unsigned short)((temp[0] | (temp[1] << 8)) << bptr)) >> (16 - count)); } static unsigned short get_loop( const void *src, unsigned int *bitptr, PYJ_1_BLOCKHEADER header ) { if (get_bits(src, bitptr, 1)) return header->CodeCountTable[0]; else { unsigned int temp = get_bits(src, bitptr, 2); if (temp) return get_bits(src, bitptr, header->CodeCountCodeLengthTable[temp - 1]); else return header->CodeCountTable[1]; } } static unsigned short get_count( const void *src, unsigned int *bitptr, PYJ_1_BLOCKHEADER header ) { unsigned short temp; if ((temp = get_bits(src, bitptr, 2)) != 0) { if (get_bits(src, bitptr, 1)) return get_bits(src, bitptr, header->LZSSRepeatCodeLengthTable[temp - 1]); else return SWAP16(header->LZSSRepeatTable[temp]); } else return SWAP16(header->LZSSRepeatTable[0]); } INT Decompress( LPCVOID Source, LPVOID Destination, INT DestSize ) { PYJ_1_FILEHEADER hdr = (PYJ_1_FILEHEADER)Source; unsigned char *src = (unsigned char *)Source; unsigned char *dest; unsigned int i; TreeNode *root, *node; if (Source == NULL) return -1; if (SWAP32(hdr->Signature) != 0x315f4a59) return -1; if (SWAP32(hdr->UncompressedLength) > (unsigned int)DestSize) return -1; do { unsigned short tree_len = ((unsigned short)hdr->HuffmanTreeLength) * 2; unsigned int bitptr = 0; unsigned char *flag = (unsigned char *)src + 16 + tree_len; if ((node = root = (TreeNode *)malloc(sizeof(TreeNode) * (tree_len + 1))) == NULL) return -1; root[0].leaf = 0; root[0].value = 0; root[0].left = root + 1; root[0].right = root + 2; for (i = 1; i <= tree_len; i++) { root[i].leaf = !get_bits(flag, &bitptr, 1); root[i].value = src[15 + i]; if (root[i].leaf) root[i].left = root[i].right = NULL; else { root[i].left = root + (root[i].value << 1) + 1; root[i].right = root[i].left + 1; } } src += 16 + tree_len + (((tree_len & 0xf) ? (tree_len >> 4) + 1 : (tree_len >> 4)) << 1); } while (0); dest = (unsigned char *)Destination; for (i = 0; i < SWAP16(hdr->BlockCount); i++) { unsigned int bitptr; PYJ_1_BLOCKHEADER header; header = (PYJ_1_BLOCKHEADER)src; src += 4; if (!SWAP16(header->CompressedLength)) { unsigned short hul = SWAP16(header->UncompressedLength); while (hul--) { *dest++ = *src++; } continue; } src += 20; bitptr = 0; for (;;) { unsigned short loop; if ((loop = get_loop(src, &bitptr, header)) == 0) break; while (loop--) { node = root; for(; !node->leaf;) { if (get_bits(src, &bitptr, 1)) node = node->right; else node = node->left; } *dest++ = node->value; } if ((loop = get_loop(src, &bitptr, header)) == 0) break; while (loop--) { unsigned int pos, count; count = get_count(src, &bitptr, header); pos = get_bits(src, &bitptr, 2); pos = get_bits(src, &bitptr, header->LZSSOffsetCodeLengthTable[pos]); while (count--) { *dest = *(dest - pos); dest++; } } } src = ((unsigned char *)header) + SWAP16(header->CompressedLength); } free(root); return SWAP32(hdr->UncompressedLength); } #else typedef struct _TreeNode { unsigned short weight; unsigned short value; struct _TreeNode* parent; struct _TreeNode* left; struct _TreeNode* right; } TreeNode; typedef struct _Tree { TreeNode* node; TreeNode** list; } Tree; static unsigned char data1[0x100] = { 0x3f,0x0b,0x17,0x03,0x2f,0x0a,0x16,0x00,0x2e,0x09,0x15,0x02,0x2d,0x01,0x08,0x00, 0x3e,0x07,0x14,0x03,0x2c,0x06,0x13,0x00,0x2b,0x05,0x12,0x02,0x2a,0x01,0x04,0x00, 0x3d,0x0b,0x11,0x03,0x29,0x0a,0x10,0x00,0x28,0x09,0x0f,0x02,0x27,0x01,0x08,0x00, 0x3c,0x07,0x0e,0x03,0x26,0x06,0x0d,0x00,0x25,0x05,0x0c,0x02,0x24,0x01,0x04,0x00, 0x3b,0x0b,0x17,0x03,0x23,0x0a,0x16,0x00,0x22,0x09,0x15,0x02,0x21,0x01,0x08,0x00, 0x3a,0x07,0x14,0x03,0x20,0x06,0x13,0x00,0x1f,0x05,0x12,0x02,0x1e,0x01,0x04,0x00, 0x39,0x0b,0x11,0x03,0x1d,0x0a,0x10,0x00,0x1c,0x09,0x0f,0x02,0x1b,0x01,0x08,0x00, 0x38,0x07,0x0e,0x03,0x1a,0x06,0x0d,0x00,0x19,0x05,0x0c,0x02,0x18,0x01,0x04,0x00, 0x37,0x0b,0x17,0x03,0x2f,0x0a,0x16,0x00,0x2e,0x09,0x15,0x02,0x2d,0x01,0x08,0x00, 0x36,0x07,0x14,0x03,0x2c,0x06,0x13,0x00,0x2b,0x05,0x12,0x02,0x2a,0x01,0x04,0x00, 0x35,0x0b,0x11,0x03,0x29,0x0a,0x10,0x00,0x28,0x09,0x0f,0x02,0x27,0x01,0x08,0x00, 0x34,0x07,0x0e,0x03,0x26,0x06,0x0d,0x00,0x25,0x05,0x0c,0x02,0x24,0x01,0x04,0x00, 0x33,0x0b,0x17,0x03,0x23,0x0a,0x16,0x00,0x22,0x09,0x15,0x02,0x21,0x01,0x08,0x00, 0x32,0x07,0x14,0x03,0x20,0x06,0x13,0x00,0x1f,0x05,0x12,0x02,0x1e,0x01,0x04,0x00, 0x31,0x0b,0x11,0x03,0x1d,0x0a,0x10,0x00,0x1c,0x09,0x0f,0x02,0x1b,0x01,0x08,0x00, 0x30,0x07,0x0e,0x03,0x1a,0x06,0x0d,0x00,0x19,0x05,0x0c,0x02,0x18,0x01,0x04,0x00 }; static unsigned char data2[0x10] = { 0x08,0x05,0x06,0x04,0x07,0x05,0x06,0x03,0x07,0x05,0x06,0x04,0x07,0x04,0x05,0x03 }; static void adjust_tree(Tree tree, unsigned short value) { TreeNode* node = tree.list[value]; TreeNode tmp; TreeNode* tmp1; TreeNode* temp; while(node->value != 0x280) { temp = node + 1; while(node->weight == temp->weight) temp++; temp--; if (temp != node) { tmp1 = node->parent; node->parent = temp->parent; temp->parent = tmp1; if (node->value > 0x140) { node->left->parent = temp; node->right->parent = temp; } else tree.list[node->value] = temp; if (temp->value > 0x140) { temp->left->parent = node; temp->right->parent = node; } else tree.list[temp->value] = node; tmp = *node; *node = *temp; *temp = tmp; node = temp; } node->weight++; node = node->parent; } node->weight++; } static int build_tree(Tree *tree) { int i, ptr; TreeNode** list; TreeNode* node; if ((tree->list = list = (TreeNode **)malloc(sizeof(TreeNode*) * 321)) == NULL) return 0; if ((tree->node = node = (TreeNode *)malloc(sizeof(TreeNode) * 641)) == NULL) { free(list); return 0; } memset(list, 0, 321 * sizeof(TreeNode*)); memset(node, 0, 641 * sizeof(TreeNode)); for(i = 0; i <= 0x140; i++) list[i] = node + i; for(i = 0; i <= 0x280; i++) { node[i].value = i; node[i].weight = 1; } tree->node[0x280].parent = tree->node + 0x280; for(i = 0, ptr = 0x141; ptr <= 0x280; i += 2, ptr++) { node[ptr].left = node + i; node[ptr].right = node + i + 1; node[i].parent = node[i + 1].parent = node + ptr; node[ptr].weight = node[i].weight + node[i + 1].weight; } return 1; } #pragma pack(1) typedef struct _BitField { unsigned char b0: 1; unsigned char b1: 1; unsigned char b2: 1; unsigned char b3: 1; unsigned char b4: 1; unsigned char b5: 1; unsigned char b6: 1; unsigned char b7: 1; } BitField; #pragma pack() static int bt(const void* data, unsigned int pos) { BitField* bit = (BitField*)((unsigned char*)data + (pos >> 3)); switch(pos & 0x7) { case 0: return bit->b0; case 1: return bit->b1; case 2: return bit->b2; case 3: return bit->b3; case 4: return bit->b4; case 5: return bit->b5; case 6: return bit->b6; case 7: return bit->b7; } return 0; } static void bit(void* data, unsigned int pos, int set) { BitField* bit = (BitField*)((unsigned char*)data + (pos >> 3)); switch(pos & 0x7) { case 0: bit->b0 = set; break; case 1: bit->b1 = set; break; case 2: bit->b2 = set; break; case 3: bit->b3 = set; break; case 4: bit->b4 = set; break; case 5: bit->b5 = set; break; case 6: bit->b6 = set; break; case 7: bit->b7 = set; break; } } INT Decompress( LPCVOID Source, LPVOID Destination, INT DestSize ) { unsigned int len = 0, ptr = 0, Length = 0; unsigned char* src = (unsigned char*)Source + 4; unsigned char* dest; Tree tree; TreeNode* node; if (Source == NULL) return -1; if (!build_tree(&tree)) return -1; Length = SWAP32(*((unsigned int*)Source)); if (Length > DestSize) return -1; dest = (unsigned char*)Destination; while (1) { unsigned short val; node = tree.node + 0x280; while(node->value > 0x140) { if (bt(src, ptr)) node = node->right; else node = node->left; ptr++; } val = node->value; if (tree.node[0x280].weight == 0x8000) { int i; for(i = 0; i < 0x141; i++) if (tree.list[i]->weight & 0x1) adjust_tree(tree, i); for(i = 0; i <= 0x280; i++) tree.node[i].weight >>= 1; } adjust_tree(tree, val); if (val > 0xff) { int i; unsigned int temp, tmp, pos; unsigned char* pre; for(i = 0, temp = 0; i < 8; i++, ptr++) temp |= (unsigned int)bt(src, ptr) << i; tmp = temp & 0xff; for(; i < data2[tmp & 0xf] + 6; i++, ptr++) temp |= (unsigned int)bt(src, ptr) << i; temp >>= data2[tmp & 0xf]; pos = (temp & 0x3f) | ((unsigned int)data1[tmp] << 6); if (pos == 0xfff) break; pre = dest - pos - 1; for(i = 0; i < val - 0xfd; i++) *dest++ = *pre++; len += val - 0xfd; } else { *dest++ = (unsigned char)val; len++; } } free(tree.list); free(tree.node); return Length; } #endif