//short[] freq = new short[0x10000]; //We need to know how often each character combination occurs to determine best bit amounts. //short[] clen = new short[0x100]; //How many chars each char has associated with it. //short[] clst = new short[0x10000]; //Char list in the order they appear in the text. static public void comptext(byte[] src, byte[] dest) { DateTime c = DateTime.Now;//.Ticks; //Welcome to my Huffman Hamburger! (Scan text, do complex char tables, compress text.) //Scan text to generate frequency table. ushort char1 = 0, char2 = 0; ushort[] freq = new ushort[0x10000]; //We need to know how often each character combination occurs to determine best bit amounts. ushort[] clen = new ushort[0x100]; //How many chars each char has associated with it. ushort[] clst = new ushort[0x10000]; //Char list in the order they appear in the text. int srcEntry = 0; while ((Bits.getInt32(src, srcEntry) != 0) || (srcEntry == 0)) //Set up frequency table and char list (in order displayed in text.) { int srcPos = 0xC300 + Bits.getInt32(src, srcEntry); do { char2 = src[srcPos++]; if (freq[char1 * 0x100 + char2]++ == 0) { clst[char1 * 0x100 + clen[char1]++] = char2; //clen[char1]++;// += 1; } //freq[char1 * 0x100 + char2] += 1; char1 = char2; } while (char1 != 0); //Change to while < textArray?-- No, go through offset list instead. srcEntry += 4; } //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/clst.dmp", Array.ConvertAll<short, byte>(clst, delegate(short item) { return (byte)item; })); byte[] bitLen = new byte[0x10000]; //int[] bitLen = new int[0x10000]; int[] bitCode = new int[0x10000]; int addr2 = 0, chrptlen = 0; byte[] chrTbl = new byte[0x8000]; byte[] chrPtrs = new byte[0x200]; for (int c1 = 0; c1 < 0x100; c1++) { if (clen[c1] == 0) { chrPtrs[(c1 << 1) + 1] = 0x80; continue; } chrptlen = (c1 + 1) << 1; //if (c1 > 5) { continue; } //For testing. //Sort chars by symbol frequency (simple) //See https://en.wikipedia.org/wiki/Sorting_algorithm - Use a Stable one so same-freq chars stay in order. //I pick simple Insertion Sort for now, since we are dealing with small sets. (https://en.wikipedia.org/wiki/Insertion_sort) for (int i = 1; i < clen[c1]; i++) { ushort x = clst[(c1 << 8) + i]; int j = i; while ((j > 0) && (freq[(c1 << 8) + clst[(c1 << 8) + j - 1]] > freq[(c1 << 8) + x])) { clst[(c1 << 8) + j] = clst[(c1 << 8) + j - 1]; j = j - 1; } clst[(c1 << 8) + j] = x; } //Sort chars by node frequency (More advanced) int[] symbSort = new int[0x100]; //Basically points to chars in order to be displayed in data. int[] symbBits = new int[0x100]; int[] nodeHead = new int[0x100]; int[] nodeTail = new int[0x100]; int[] nodeFreq = new int[0x100]; nodeFreq[0] = 0x7FFFFFFF; nodeFreq[1] = 0x7FFFFFFF; //Ensure unused/node2 when there is none. int nodeA = 0, nodeI = 0, symbI = 0; if (clen[c1] > 1) { while ((symbI < clen[c1]) || (nodeA < nodeI - 1)) { int symfreq1 = freq[(c1 << 8) + clst[(c1 << 8) + symbI]]; int symfreq2 = freq[(c1 << 8) + clst[(c1 << 8) + symbI + 1]]; if ((symbI + 1 < clen[c1]) && (symfreq2 <= nodeFreq[nodeA])) //Symbol - Symbol { symbSort[symbI] = symbI + 1; nodeHead[nodeI] = symbI; nodeTail[nodeI] = symbI + 1; nodeFreq[nodeI] = symfreq1 + symfreq2; symbI += 2; } else if ((symbI < clen[c1]) && (symfreq1 <= nodeFreq[nodeA])) // Symbol - Node { symbSort[symbI] = nodeHead[nodeA]; nodeHead[nodeI] = symbI; nodeTail[nodeI] = nodeTail[nodeA]; nodeFreq[nodeI] = symfreq1 + nodeFreq[nodeA]; symbI++; nodeA++; } else if ((nodeA < nodeI - 1) && ((nodeFreq[nodeA + 1] < symfreq1) || ((symbI >= clen[c1])))) // Node - Node { symbSort[nodeTail[nodeA]] = nodeHead[nodeA + 1]; nodeHead[nodeI] = nodeHead[nodeA]; nodeTail[nodeI] = nodeTail[nodeA + 1]; nodeFreq[nodeI] = nodeFreq[nodeA] + nodeFreq[nodeA + 1]; nodeA += 2; } else if (nodeFreq[nodeA] < symfreq1) // Node - Symbol { symbSort[nodeTail[nodeA]] = symbI; nodeHead[nodeI] = nodeHead[nodeA]; nodeTail[nodeI] = symbI; nodeFreq[nodeI] = nodeFreq[nodeA] + symfreq1; symbI++; nodeA++; } symbBits[clst[(c1 << 8) + nodeHead[nodeI++]]] += 1; } } addr2 += (((clen[c1] * 12) + 4) & -8); chrPtrs[(c1 << 1)] = (byte)(addr2 >> 3); chrPtrs[(c1 << 1) + 1] = (byte)(addr2 >> 11); int addr1 = addr2 - 12; byte bitsL = 0; //int val = 0; //int bitnum = (clen[c1] & 1) * 4; int bitC = 0; for (int n = clen[c1]; n > 0; n--) { //List chars chrTbl[(addr1 >> 3)] |= (byte)(clst[(c1 << 8) + nodeHead[nodeA]] << (addr1 & 7)); chrTbl[(addr1 >> 3) + 1] |= (byte)(clst[(c1 << 8) + nodeHead[nodeA]] >> (8 - (addr1 & 7))); addr1 -= 12; //val |= clst[(c1 << 8) + nodeHead[nodeA]] << bitnum; bitnum += 12; //while (bitnum >= 8) { // chrTbl[addr1++] = (byte)val; bitnum -= 8; //} //List the char's tree/flags addr2 += symbBits[clst[(c1 << 8) + nodeHead[nodeA]]]; chrTbl[addr2 >> 3] |= (byte)(1 << (addr2++ & 7)); //Calculate bit lengths for bit code. bitsL += (byte)symbBits[clst[(c1 << 8) + nodeHead[nodeA]]]; //bitLen[clst[(c1 << 8) + nodeHead[nodeA]]] = bitsL; bitLen[(c1 << 8) + clst[(c1 << 8) + nodeHead[nodeA]]] = bitsL; //if (symbBits[clst[(c1 << 8) + nodeHead[nodeA]]] == 0) { bitsL -= 1; } //if (c1 == 0) { Console.WriteLine(bitC.ToString("X8") + " " + bitsL.ToString("X8") + " " + (char)clst[(c1 << 8) + nodeHead[nodeA]]); } //Generate bitCode table. bitCode[(c1 << 8) + clst[(c1 << 8) + nodeHead[nodeA]]] = bitC; while (((bitC >> (bitsL - 1)) & 1) == 1) { bitsL -= 1; bitC ^= 1 << bitsL; } bitC |= 1 << (bitsL - 1); nodeHead[nodeA] = symbSort[nodeHead[nodeA]]; } addr2 = (addr2 + 8) & -8; //Console.WriteLine("\nLetter by node order"); //for (int zz = 0; zz < clen[c1]; zz++) { // Console.Write(clst[(c1 << 8) + nodeHead[nodeA]].ToString("X4") + " "); // //Console.Write(symbBits[clst[(c1 << 8) + nodeHead[nodeA]]].ToString("X4") + " "); // nodeHead[nodeA] = symbSort[nodeHead[nodeA]]; //} //Console.WriteLine("\nsymbSort"); //for (int zz = 0; zz < clen[c1]; zz++) { // Console.Write(symbSort[zz].ToString("X4") + " "); //} } //Finally compress the text. int val = 0, bitnum = 0, ctAddr = 0, cstrstart = 0; byte[] cText = new byte[src.Length]; byte[] txtref1 = new byte[0x200]; int tr1Addr = 0; byte[] txtref2 = new byte[0x8000]; int tr2Addr = 0; srcEntry = 0; char1 = 0; while ((Bits.getInt32(src, srcEntry) != 0) || (srcEntry == 0)) { if ((srcEntry & 0x3FC) == 0) { Bits.setInt32(txtref1, tr1Addr, ctAddr); tr1Addr += 4; Bits.setInt32(txtref1, tr1Addr, tr2Addr); tr1Addr += 4; } cstrstart = ctAddr; int srcPos = 0xC300 + Bits.getInt32(src, srcEntry); val = 0; do { char2 = src[srcPos++]; val |= bitCode[(char1 << 8) + char2] << bitnum; bitnum += bitLen[(char1 << 8) + char2]; while (bitnum >= 8) { cText[ctAddr++] = (byte)val; val >>= 8; bitnum -= 8; } //if (freq[char1 * 0x100 + char2]++ == 0) { // clst[char1 * 0x100 + clen[char1]++] = char2; //clen[char1]++;// += 1; //} //freq[char1 * 0x100 + char2] += 1; //if (srcEntry == 0) { Console.WriteLine(bitCode[(char1 << 8) + char2].ToString("X8") + " " + bitLen[(char1 << 8) + char2].ToString("X8")); } char1 = char2; } while (char1 != 0); //Change to while < textArray?-- No, go through offset list instead. srcEntry += 4; if (bitnum != 0) { cText[ctAddr++] = (byte)val; bitnum = 0; } while ((ctAddr - cstrstart) > 0xFE) { txtref2[tr2Addr++] = 0xFF; cstrstart += 0xFF; } txtref2[tr2Addr++] = (byte)(ctAddr - cstrstart); //cstrstart = ctAddr; } //Now insert everything into the ROM. int insAddr = 0xFA0000; int loc1 = insAddr; Array.Copy(chrTbl, 0, dest, insAddr, addr2 >> 3); insAddr += addr2 >> 3; insAddr = (insAddr + 1) & -2; int loc2 = insAddr; Array.Copy(chrPtrs, 0, dest, insAddr, chrptlen); insAddr += chrptlen; Bits.setInt32(dest, 0x38578, 0x08000000 + insAddr); Bits.setInt32(dest, insAddr, 0x08000000 + loc1); insAddr += 4; Bits.setInt32(dest, insAddr, 0x08000000 + loc2); insAddr += 4; loc1 = insAddr; Array.Copy(cText, 0, dest, insAddr, ctAddr); insAddr += ctAddr; loc2 = insAddr; Array.Copy(txtref2, 0, dest, insAddr, tr2Addr); insAddr += tr2Addr; insAddr = (insAddr + 3) & -4; Bits.setInt32(dest, 0x385DC, 0x08000000 + insAddr); for (int a = 0; a < tr1Addr; a += 8) { Bits.setInt32(dest, insAddr + a, 0x08000000 + Bits.getInt32(txtref1, a) + loc1); Bits.setInt32(dest, insAddr + a + 4, 0x08000000 + Bits.getInt32(txtref1, a + 4) + loc2); } //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/txtromtest.gba", dest); //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/txtref1.dmp", txtref1); //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/txtref2.dmp", txtref2); //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/cText.dmp", cText); //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/chrPtrs.dmp", chrPtrs); //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/chrTbl.dmp", chrTbl); //System.IO.File.WriteAllBytes("C:/Users/Tea/Desktop/bitLen.dmp", bitLen); //DateTime c = DateTime.Now;//.Ticks; Console.WriteLine((DateTime.Now - c).ToString()); }
static public byte[] decompTextOld(byte[] src) { DateTime c = DateTime.Now; int asmpchar = Bits.getInt32(src, 0x38578) - 0x8000000; int asmptext = Bits.getInt32(src, 0x385DC) - 0x8000000; int chardata = Bits.getInt32(src, asmpchar) - 0x08000000; int charpntrs = Bits.getInt32(src, asmpchar + 4) - 0x08000000; byte[] des = new byte[0x800000]; int desEntry = 0, desPos = 0xC300; for (int srcI = 0; srcI < 12461; srcI++) { Bits.setInt32(des, desEntry, desPos - 0xC300); desEntry += 4; int srcInd = srcI; int textTree = Bits.getInt32(src, asmptext + ((srcInd >> 8) << 3)) - 0x08000000; int textLenAddr = Bits.getInt32(src, asmptext + ((srcInd >> 8) << 3) + 4) - 0x08000000; srcInd &= 0xFF; while (srcInd-- != 0) { int cLen; do { cLen = src[textLenAddr++]; textTree += cLen; } while (cLen == 0xFF); } int initChar = 0; textTree <<= 3; do { int charTree = (chardata + Bits.getInt16(src, charpntrs + (initChar << 1))) << 3; int charSlot = charTree - 12; while (((src[charTree >> 3] >> (charTree++ & 7)) & 1) == 0) { if (((src[textTree >> 3] >> (textTree++ & 7)) & 1) == 1) { int depth = 0; while (depth >= 0) { while (((src[charTree >> 3] >> (charTree++ & 7)) & 1) == 0) { depth++; } charSlot -= 12; depth--; } } } initChar = (Bits.getInt16(src, charSlot >> 3) >> (charSlot & 7)) & 0xFFF; des[desPos++] = (byte)initChar; //do { // n=getNextCharacter(argument0) // if n!=0 { // if (n<32 || n>ord('~')) { // if argument2 // { str+='['+string(n)+']'; } // if argument3 && (n==1 || n==3) && (p<17 || p>20) && p!=26 && p!=29 // { n=0 } // } else { str+=chr(n); } // } // p=n //} until n=0 } while (initChar != 0); } Console.WriteLine(DateTime.Now - c + " (Old Text Decompression)"); return(des); }
private void button_Click(object sender, EventArgs e) { //TODO Move decomptext to Form1, compress text on save. // * Function may need refactoring, but it works. String str = tbx.Text; //textBox1.Text; byte[] bytes = new byte[0x200]; int a = 0, b = 0; while (a < str.Length) { if (str[a] == '[') { int num = 0; while (str[++a] != ']') { num = (num * 10) + (byte)(str[a]) - 0x30; } a++; bytes[b++] = (byte)num; } else if (((byte)str[a] == 13) && ((byte)str[a + 1] == 10)) { a += 2; } else { bytes[b++] = (byte)str[a++]; } } b++; //B/c 00 character at end. //byte[] bytes = toRawStrData(textBox1.Text); //int b = bytes.Length + 1; //=0x200 + 1 (NEEDS FIXING) //if (listView1.SelectedIndices.Count != 1) { return; } int srcEntry = theIndex * 4; //int srcEntry = listBox2.SelectedIndex * 4; int neaddr = 0xC300 + Bits.getInt32(txt, srcEntry + 4); int lendif = Bits.getInt32(txt, srcEntry) - Bits.getInt32(txt, srcEntry + 4) + b; int c = srcEntry + 4; while ((Bits.getInt32(txt, c) != 0)) { Bits.setInt32(txt, c, Bits.getInt32(txt, c) + lendif); c += 4; } c = 0xC300 + Bits.getInt32(txt, c - 4) - lendif; while (txt[c++] != 0) { } if (Bits.getInt32(txt, srcEntry + 4) != 0) { Array.Copy(txt, neaddr, txt, 0xC300 + Bits.getInt32(txt, srcEntry + 4), c - neaddr); } int d = 0xC300 + Bits.getInt32(txt, srcEntry); while (b-- > 0) { txt[d] = bytes[d++ - (0xC300 + Bits.getInt32(txt, srcEntry))]; } Comp.comptext(txt, Globals.mainForm.rom); //b=length needed. ; small - big + length //listView1.Invalidate(); }
static public byte[] decompText(byte[] src) //, int srcInd) { // int srcPos) { //return decompTextOld(src); { DateTime c = DateTime.Now; int[] bitcode = new int[0x10000]; byte[] bitLen = new byte[0x10000]; short[] bitChar = new short[0x10000]; //Scan char data to generate data for faster decompression than old method. int asmpchar = Bits.getInt32(src, 0x38578) - 0x8000000; int asmptext = Bits.getInt32(src, 0x385DC) - 0x8000000; int chardata = Bits.getInt32(src, asmpchar) - 0x08000000; int charpntrs = Bits.getInt32(src, asmpchar + 4) - 0x08000000; for (int char1 = 0; char1 < 0x100; char1++) { if (charpntrs == asmpchar) { break; } if (Bits.getInt16(src, charpntrs) == 0x8000) { charpntrs += 2; continue; } int charTree = (chardata + Bits.getInt16(src, charpntrs)) << 3; charpntrs += 2; int charSlot = charTree - 12; byte bits = 0; int bitC = 0; int entry = (char1 << 8); do { while (((src[charTree >> 3] >> (charTree++ & 7)) & 1) == 0) { bits++; } bitChar[entry] = (short)((Bits.getInt16(src, charSlot >> 3) >> (charSlot & 7)) & 0xFFF); charSlot -= 12; bitLen[entry] = bits; if (bits >= 24) { return(decompTextOld(src)); } bitcode[entry] = bitC; while (((bitC >> (bits - 1)) & 1) == 1) { bits -= 1; bitC ^= 1 << bits; } bitC |= 1 << (bits - 1); entry += 1; } while (bits > 0); } //Console.WriteLine(DateTime.Now - c); //c = DateTime.Now; int textTree = 0, textLenAddr = 0; byte[] des = new byte[0x800000]; int desEntry = 0, desPos = 0xC300; for (int srcI = 0; srcI < 12461; srcI++) { Bits.setInt32(des, desEntry, desPos - 0xC300); desEntry += 4; int srcInd = srcI; if ((srcInd & 0xFF) == 0) { textTree = Bits.getInt32(src, asmptext + ((srcInd >> 8) << 3)) - 0x08000000; textLenAddr = Bits.getInt32(src, asmptext + ((srcInd >> 8) << 3) + 4) - 0x08000000; } else { int cLen; do { cLen = src[textLenAddr++]; textTree += cLen; } while (cLen == 0xFF); } int initChar = 0, bitnum = 0, val = 0, textTree2 = textTree; do { while (bitnum < 24) { val |= (int)src[textTree2++] << bitnum; bitnum += 8; } int entry = initChar << 8; while ((val & ((1 << bitLen[entry]) - 1)) != bitcode[entry]) { entry++; } initChar = bitChar[entry]; val >>= bitLen[entry]; bitnum -= bitLen[entry]; des[desPos++] = (byte)initChar; //if (desPos >= 0x10000) { break; } } while (initChar != 0); } Console.WriteLine(DateTime.Now - c + " (Text Decompression)"); return(des); }
void saveFile(int fileID) { if (fileID > 0x16D) { return; } int ovrA = fileID << 5; int srcStart = Bits.getInt32(ovr, ovrA + 4) & 0x1FFFFFF; int srcSize = Bits.getInt32(ovr, ovrA + 8); // & 0x1FFFFFF; int srcEnd = srcStart + srcSize; int desSize = srcSize + (srcSize >> 3) + 7; //Absolute maximum possible size... (Including header and end of data command.) desSize = (desSize + 0x1FF) & -0x200; byte[] src = ram; // new byte[srcSize]; int srcPos = srcStart; byte[] des = new byte[desSize]; int desPos = 0; des[desPos++] = 0x40; des[desPos++] = (byte)srcSize; des[desPos++] = (byte)(srcSize >> 8); des[desPos++] = (byte)(srcSize >> 16); int fCur = 0x80; int fAddr = desPos++; int flags = 0; while (srcPos < srcEnd) { int dist = 0; int len = 0; int winStart = Math.Max(srcStart, srcPos - 0xFFF); for (int i = winStart; i < srcPos; i++) { for (int j = 1; j < 0x10110; j++) { if (src[srcPos + j] == src[i + j]) { //if ((j + 1) >= len) if (j >= len) { dist = i; //len = j + 1; len = j; } } else { break; } } if (src[srcPos] == src[i]) { len += 1; } else { dist += 1; } } if (src[srcPos] != src[dist]) //Insert byte { des[desPos++] = src[srcPos++]; fCur >>= 1; if (fCur == 0) { des[fAddr] = (byte)-flags; fAddr = desPos++; fCur = 0x80; flags = 0; } } dist = srcPos - dist; if (len < 2) { des[desPos++] = src[srcPos]; len = 1; } else if (len < 0x10) { des[desPos++] = (byte)((dist << 4) | len); des[desPos++] = (byte)(dist >> 4); } else if (len < 0x110) { des[desPos++] = (byte)((dist << 4) | 0); des[desPos++] = (byte)(dist >> 4); des[desPos++] = (byte)(len - 0x10); } else // if (len < 0x10110) { des[desPos++] = (byte)((dist << 4) | 1); des[desPos++] = (byte)(dist >> 4); des[desPos++] = (byte)(len - 0x110); des[desPos++] = (byte)((len - 0x110) >> 8); } srcPos += len; if (len > 1) { flags |= fCur; } fCur >>= 1; if (fCur == 0) { des[fAddr] = (byte)-flags; fAddr = desPos++; fCur = 0x80; flags = 0; } } des[desPos++] = 0; des[desPos++] = 0; flags |= fCur; des[fAddr] = (byte)-flags; //Now to save to ROM. Bits.saveFile(@"C:\Users\Tea\Desktop\notoriginal.bin", des); return; int fatA = fileID << 3; int fat1 = Bits.getInt32(fat, fatA); Bits.setInt32(fat, fatA + 4, fat1 + desPos); //int fat2 = Bits.getInt32(fat, fatA + 4); int fat2 = Bits.getInt32(fat, fatA + 8); int size = fat2 - fat1; //For neatness.... while (desPos < size) { des[desPos++] = 0xFF; } if (desPos != size) { MessageBox.Show("About to lose data... Have fun!"); } Bits.saveFilePart(path, fat1, size, des); }