├── SymphoniaUndub_Scripts ├── SymphoniaUndub_Scripts.csproj ├── SymphoniaUndub_Scripts.sln ├── Program.cs ├── Decrypt.cs └── complib.cs └── README.md /SymphoniaUndub_Scripts/SymphoniaUndub_Scripts.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | true 10 | 11 | 12 | 13 | true 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /SymphoniaUndub_Scripts/SymphoniaUndub_Scripts.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30621.155 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SymphoniaUndub_Scripts", "SymphoniaUndub_Scripts.csproj", "{DF8D6F80-090D-4D63-85ED-655F939C81A8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {DF8D6F80-090D-4D63-85ED-655F939C81A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {DF8D6F80-090D-4D63-85ED-655F939C81A8}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {DF8D6F80-090D-4D63-85ED-655F939C81A8}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {DF8D6F80-090D-4D63-85ED-655F939C81A8}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {C823B940-A76D-47FA-94C9-FB0682FF4406} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tales-of-Symphonia-NGC-Undub 2 | Hello all! This is the new home for the Tales of Symphonia GCN Undub patch! Many years ago I created the initial Tales of Symphonia undub patch. There were various issues with it (documented below) with the most glaring one being the omission of restoring skit voices. Alas, I was but a young college student at the time, and I did not yet have the tech know-how to complete this. (There were also a lot less resources available back then!) 3 | 4 | Earlier this year, I took reverse engineering on as a hobby, and a few months ago I decided that I now knew enough to come back and finish this project. I'm happy to say that I've finally accomplished what I originally set out to do all those years ago. All voices are now properly restored, and all known bugs have been fixed. Enjoy! 5 | 6 | # Releases 7 | For releases, please see the release page. 8 | 9 | Current Version: 2.0 BETA 10 | 11 | # Bug Reporting 12 | If you run into any bugs please open an issue on github. Include as many details as you can, and if possible, please include a save file. You may also try to reach me in various other places where you may find me. Thanks!! 13 | 14 | # Version History, Information, and Documentation 15 | 16 | ## Version 1.0 - OLD - Informational Only 17 | I've lost a lot of the WIP intermediary files so this is mostly going on memory. At the time of this patch's creation, editing GCN games was under the limitation that files could not be replaced with larger files. All the afs files were extracted, ahx's were converted to wav's, and to get the JP files to fit within the NA file limit, they were downsampled about 20% and repacked. I honestly do not remember how or what I did with the playable character .snd files (includes random grunts + sometimes actual dialog), it's possible they weren't actually replaced? 18 | 19 | Bugs/Issues: 20 | - No Voiced Skits (despite nulled out audio being replaced with original audio files) 21 | - "Grunts" + dialog for human-type enemies not undubbed 22 | - Issue where one of Presea's lines (the "tutururu" sound) would play on top of one of those grunts - likely others as well 23 | - Possibly/likely other stuff 24 | 25 | ## Version 2.0 BETA 26 | Quite a few changes, most of them tackling bugs or issues with the old version. All known bugs have been resolved. 27 | 28 | - No more downsampled files. GCRebuilder v1.1 by BSV is now used to repackage files, and the file size constraint is no longer there. Audiophiles rejoice! 29 | 30 | - Wrong voice bug. The ID of voice data was shifted down by 1 in the NA release. I'm not 100% sure on the exact cause of this one. I think they are caused by the playable characters having their ID's restored to their JP ID's, shifting it up by one, and enemies were still using the NA ID's. The footsoldier enemy (the two soldiers before the first mini boss at the start of the game,) for example, has ID's that begin right after Presea's. When he tries to play his first voice (basic attack), since it shared an ID with the JP up-shifted last of Presea's voices, they both played. Like I said, not 100% sure, but I believe this to be the case. Not sure if there are other PC's that share a bound with a voiced NPC. 31 | 32 | This has been fixed by adjusting the enemy voice ID's to the up-shifted JP ID's. Which brings us to... 33 | 34 | - Enemy dialog restored! This was probably the hardest thing to accomplish. If you've ever explored the file contents of this game, you may have noticed that there are a lot of individual .snd files in the S/ folder, from v_10_bh04.snd to v_67_hum25.snd, and so on. This is actually the enemy voice data. But these files are unused. I think whoever built the game left them in the filesystem by mistake. Through a lot of debugging and exploration, I finally discovered that the actual sound data, without headers, is actually inside btlvbank.dat. But the header is where the ID's are. Through even more painful debugging, I finally found the headers were inside of the btlenemy.dat entries, but those entries were compressed. I found the decompression routine in the game code, and directly copied the decompiled code from Ghidra into a C# project. I managed to successfully decompress one of the entries, and lo and behold, the header data! The only problem now is having to recompress the data after modifying it. I started analyzing the compression algorithm some more, attempted to piece together what it was doing, add comments + adjust variables, etc., when I noticed the compression looked similar to LZSS. (I haven't worked with compression before, so this was new!) Speaking with colleagues, I was pointed in the direction of compto. Went to take a look at it, and thankfully, that was indeed the compression being used! 35 | 36 | With this new piece of knowledge in hand, I had everything needed to accomplish this. Since the Japanese btlvbank voices were also different sizes, I had to rebuild that as well. I opted to use the vanilla Japanese btlvbank file, and instead just replaced the entire voice portion of the btlenemy entries with the Japanese one. There is one last file, btlusual, which has two relevant pointer tables at the end - one for btlenemy entries, and one for btlvbank. The pointer table for vbank was straight up replaced w/ the Japanese one, while the btlenemy one was rebuilt from scratch based on how my script rebuilt the file. Check github repo for code used to rebuild this. 37 | 38 | Doing all of these changes fixes the overlapping ID issue, as everything should be using the Japanese ID now, and also restores the enemy voices that were not undubbed in the previous version. 39 | 40 | - And lastly... Skit voices have been restored! This took two days of debugging and documenting code, and after going through the entire routine that loads the voice files and picks and loads the correct one, not seeing any differences between the JP and NA versions of the game, I finally stepped out to the function that called it, and right there, was an extra function call that was not present in the JP version. A quick peek inside it shows that it looks like a function that edits the volume. So after all this time, the problem was that they decided that nulling out the audio file wasn't enough, they had to go and mute it, too! NOPing out the bl instruction effectively fixes this. 41 | 42 | ![image](https://user-images.githubusercontent.com/6155506/134822118-a568d578-94e1-4b00-a0c8-5a40b4e53fc2.png) 43 | 44 | I also added a little logo during boot. I hope it's not too intrusive! 45 | 46 | # To-do for future releases: 47 | 48 | 1. Sub the ending video 49 | 2. Look into the viability of merging both discs into one and removing the disc swap 50 | 3. Restore sound test menu option that was removed from the NA release 51 | 4. Add subtitles to battle voices 52 | 53 | I've spent a lot of time on this and I'm tired of it. :) The only item on the list I'll probably do is the first one. I'll see if anyone unearths any bugs or issues in the next few months. Hopefully I can get that done and just include it in the next update, either just removing the beta tag if no issues, or with bug fixes for any issues identified. 54 | 55 | Thanks all. <3 56 | Julian 57 | -------------------------------------------------------------------------------- /SymphoniaUndub_Scripts/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace SymphoniaUndub_Scripts 5 | { 6 | class Program 7 | { 8 | // Gamecube is big endian... grr! 9 | public static UInt32 BigEndianReadUInt32(BinaryReader br) 10 | { 11 | var data = br.ReadBytes(4); 12 | Array.Reverse(data); 13 | return BitConverter.ToUInt32(data, 0); 14 | } 15 | 16 | static void Main(string[] args) 17 | { 18 | // replace this with directory you're using to store files 19 | // as you can see, my old Symph files were in an old backup of an old backup XD 20 | string pwd = @"D:\BackUp\backupC\btlenemy"; 21 | 22 | // You will need the Japanese and English btlenemy and btlusual. 23 | // The code expects the following files in this directory: 24 | // NABTLenemy.dat 25 | // NABTLusual.dat 26 | // JPBTLenemy.dat 27 | // JPBTLusual.dat 28 | 29 | // Need to get pointers: 30 | // NA Ptrs btlusual 0xE3B40 - 0x3F0 -- 252 times/FC 31 | // JP Ptrs btlusual 0xE2D20 - 0x3F0 -- 252 times/FC 32 | 33 | FileStream nafs = new FileStream($@"{pwd}\NABTLusual.dat", FileMode.Open); 34 | BinaryReader nabr = new BinaryReader(nafs); 35 | nabr.BaseStream.Seek(0xE3B40, SeekOrigin.Begin); 36 | 37 | FileStream jpfs = new FileStream($@"{pwd}\JPBTLusual.dat", FileMode.Open); 38 | BinaryReader jpbr = new BinaryReader(jpfs); 39 | jpbr.BaseStream.Seek(0xE2D20, SeekOrigin.Begin); 40 | 41 | UInt32[] NA_Pointers = new UInt32[252]; 42 | UInt32[] JP_Pointers = new UInt32[252]; 43 | 44 | // read pointers (big endian) 45 | for (int i = 0; i < 252; i++) 46 | { 47 | NA_Pointers[i] = BigEndianReadUInt32(nabr); 48 | JP_Pointers[i] = BigEndianReadUInt32(jpbr); 49 | } 50 | 51 | 52 | // open btlenemy's to start parsing data 53 | nafs = new FileStream($@"{pwd}\NABTLenemy.dat", FileMode.Open); 54 | nabr = new BinaryReader(nafs); 55 | jpfs = new FileStream($@"{pwd}\JPBTLenemy.dat", FileMode.Open); 56 | jpbr = new BinaryReader(jpfs); 57 | 58 | 59 | // loop through btlenemy NA --> write enc to file --> decrypt --> write decrypt to file 60 | // loop through btlenemy JP --> write enc to file --> decrypt --> write decrypt to file 61 | for (int i = 0; i < 251; i++) 62 | { 63 | // get pointers 64 | UInt32 base_na_ptr = NA_Pointers[i]; 65 | UInt32 base_jp_ptr = JP_Pointers[i]; 66 | 67 | // get size (end pointer - current pointer) --- last pointer points to EOF so this is OK 68 | int na_size = (int)(NA_Pointers[i + 1] - NA_Pointers[i]); 69 | int jp_size = (int)(JP_Pointers[i + 1] - JP_Pointers[i]); 70 | 71 | // seek to pointer 72 | nabr.BaseStream.Seek(base_na_ptr, SeekOrigin.Begin); 73 | jpbr.BaseStream.Seek(base_jp_ptr, SeekOrigin.Begin); 74 | 75 | // write files into individual files 76 | BinaryWriter nabw = new BinaryWriter(File.Open($@"{pwd}\{i}_NA.bin", System.IO.FileMode.Create)); 77 | BinaryWriter jpbw = new BinaryWriter(File.Open($@"{pwd}\{i}_JP.bin", System.IO.FileMode.Create)); 78 | 79 | nabw.Write(nabr.ReadBytes((int)na_size)); 80 | jpbw.Write(jpbr.ReadBytes((int)jp_size)); 81 | 82 | nabw.Flush(); 83 | jpbw.Flush(); 84 | nabw.Close(); 85 | jpbw.Close(); 86 | 87 | // decompress the output file 88 | complib.DecodeFile(@$"{pwd}\{i}_NA.bin", @$"{pwd}\{i}_NA_dec.bin", 0, 3, true); 89 | complib.DecodeFile(@$"{pwd}\{i}_JP.bin", @$"{pwd}\{i}_JP_dec.bin", 0, 3, true); 90 | } 91 | 92 | int found_count = 0; 93 | // loop through an index 94 | for (int i = 0; i < 251; i++) 95 | { 96 | nafs = new FileStream($@"{pwd}\{i}_NA_dec.bin", FileMode.Open); 97 | nabr = new BinaryReader(nafs); 98 | jpfs = new FileStream($@"{pwd}\{i}_JP_dec.bin", FileMode.Open); 99 | jpbr = new BinaryReader(jpfs); 100 | 101 | // load NA, load JP 102 | // read pointer to voice data at 0x1E4 (big endian) 103 | nabr.BaseStream.Seek(0x1E4, SeekOrigin.Begin); 104 | jpbr.BaseStream.Seek(0x1E4, SeekOrigin.Begin); 105 | 106 | UInt32 na_ptr = BigEndianReadUInt32(nabr); 107 | UInt32 jp_ptr = BigEndianReadUInt32(jpbr); 108 | 109 | if (na_ptr == 0 && jp_ptr == 0) 110 | { 111 | // if pointer is 0, no voice data - continue to next loop iteration 112 | Console.WriteLine($"Enemy id {i} - No Voice Data"); 113 | continue; 114 | } 115 | found_count++; 116 | Console.WriteLine($"Enemy id {i} - Voice Data Found -- #{found_count}"); 117 | 118 | // seek to voice data 119 | //nabr.BaseStream.Seek(na_ptr, SeekOrigin.Begin); 120 | jpbr.BaseStream.Seek(jp_ptr, SeekOrigin.Begin); 121 | 122 | // write out a modified NA decrypted file 123 | BinaryWriter nabw = new BinaryWriter(File.Open($@"{pwd}\{i}_NA_dec_modified.bin", System.IO.FileMode.Create)); 124 | nabr.BaseStream.Seek(0, SeekOrigin.Begin); 125 | // Write all data up to voice data portion 126 | nabw.Write(nabr.ReadBytes((int)(na_ptr))); 127 | // Voice data is last piece of the file. (I didn't look everywhere so it could be there may be some exceptions..???) 128 | // Write the voice section to EOF of JP file into the modified NA file 129 | nabw.Write(jpbr.ReadBytes((int)(jpbr.BaseStream.Length - jpbr.BaseStream.Position))); 130 | 131 | // this is old code from when I was trying to do a more selective replacement instead of just taking the entire JP section 132 | 133 | // at 0xB and 0xC there are two bytes that make up a pointer to a table with voice details (and pointers to vbank) 134 | /*// read A bytes 135 | nabr.ReadBytes(0xA); 136 | jpbr.ReadBytes(0xA); 137 | 138 | // get 1st byte of pointer 139 | byte na_1 = nabr.ReadByte(); 140 | byte jp_1 = jpbr.ReadByte(); 141 | // read A bytes 142 | //nabr.ReadBytes(0xA); 143 | //jpbr.ReadBytes(0xA); 144 | 145 | // get 2nd byte of pointer 146 | byte na_2 = nabr.ReadByte(); 147 | byte jp_2 = jpbr.ReadByte(); 148 | 149 | // shift first pointer left and add 2nd. I guess I could have done a readuin16, but this is big endian again 150 | // and I originally have the wrong second byte and it wasn't consecutive. 151 | int na_table_ptr = (na_1 << 8) + na_2; 152 | int jp_table_ptr = (jp_1 << 8) + jp_2; 153 | 154 | 155 | BinaryWriter nabw = new BinaryWriter(File.Open($@"{pwd}\{i}_NA_dec_modified.bin", System.IO.FileMode.Create)); 156 | jpbr.BaseStream.Seek(0, SeekOrigin.Begin); 157 | nabw.Write(jpbr.ReadBytes((int)(na_ptr + na_table_ptr))); 158 | 159 | nabr.BaseStream.Seek(na_ptr + na_table_ptr, SeekOrigin.Begin); 160 | jpbr.BaseStream.Seek(jp_ptr + jp_table_ptr, SeekOrigin.Begin); 161 | 162 | // read Uint32 -- if FFFF --> break 163 | while (true) 164 | { 165 | UInt32 na_voice_id = nabr.ReadUInt32(); 166 | UInt32 jp_voice_id = jpbr.ReadUInt32(); 167 | //nabw.Write(na_voice_id); 168 | nabw.Write(na_voice_id); 169 | if (na_voice_id == 0xFFFFFFFF) 170 | { 171 | 172 | // check if na and jp same - if not, error 173 | if (na_voice_id != jp_voice_id) 174 | { 175 | Console.WriteLine("ERROR - MISMATCH ON NUMBER OF VOICES // Enemy id {i}"); 176 | } 177 | // end point can break anyway 178 | break; 179 | } 180 | else 181 | { 182 | // not FFFF, read 0x1C bytes to copy 183 | nabw.Write(jpbr.ReadBytes(0x1C)); 184 | nabr.ReadBytes(0x1C); 185 | } 186 | } 187 | // after breaking out, we just need to write rest of the binary 188 | nabw.Write(nabr.ReadBytes((int)(nabr.BaseStream.Length - nabr.BaseStream.Position))); 189 | 190 | */ 191 | nabw.Flush(); 192 | nabw.Close(); 193 | } 194 | 195 | // loop back thru, rebuild NA btlenemy and btlusual 196 | // note I already replaced the btlvbank pointer table in btlusual, before running this script 197 | BinaryWriter enemybw = new BinaryWriter(File.Open($@"{pwd}\BTLenemy.dat", System.IO.FileMode.Create)); 198 | BinaryWriter usualbw = new BinaryWriter(File.Open($@"{pwd}\BTLusual.dat", System.IO.FileMode.Open)); 199 | // pointer to btlenemy pointer table in NA 200 | usualbw.BaseStream.Seek(0xE3B40, SeekOrigin.Begin); 201 | 202 | for (int i = 0; i < 251; i++) 203 | { 204 | UInt32 pos = (UInt32)enemybw.BaseStream.Position; 205 | 206 | // little endian grrr fadjshiufasdfsa convert to big endian 207 | byte[] pos_bytes = BitConverter.GetBytes(pos); 208 | Array.Reverse(pos_bytes); 209 | usualbw.Write(pos_bytes); 210 | 211 | string file = $@"{pwd}\{i}_NA_dec.bin"; 212 | if (File.Exists($@"{pwd}\{i}_NA_dec_modified.bin")) 213 | { 214 | file = $@"{pwd}\{i}_NA_dec_modified.bin"; 215 | } 216 | 217 | // encode file 218 | complib.EncodeFile(file, $@"{pwd}\{i}_NA_modified_enc.bin",0,3,true); 219 | 220 | nafs = new FileStream($@"{pwd}\{i}_NA_modified_enc.bin", FileMode.Open); 221 | nabr = new BinaryReader(nafs); 222 | enemybw.Write(nabr.ReadBytes((int)nabr.BaseStream.Length)); 223 | 224 | // write out rest of the 0x10 line with 0's 225 | while (enemybw.BaseStream.Position % 0x10 != 0) 226 | { 227 | enemybw.Write(new byte[] { 0x0 }); 228 | } 229 | // also write another 0x10 line of 0's - not doing this caused some issues 230 | enemybw.Write(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); 231 | enemybw.Flush(); 232 | usualbw.Flush(); 233 | } 234 | 235 | // write out the EOF pointer 236 | UInt32 pos2 = (UInt32)enemybw.BaseStream.Position; 237 | 238 | // little endian grrr fadjshiufasdfsa convert to big endian 239 | byte[] pos_bytes2 = BitConverter.GetBytes(pos2); 240 | Array.Reverse(pos_bytes2); 241 | usualbw.Write(pos_bytes2); 242 | 243 | enemybw.Flush(); 244 | enemybw.Close(); 245 | usualbw.Flush(); 246 | usualbw.Close(); 247 | 248 | // old code from when I was using my own decrypt code 249 | // note this required adding the "unsafe" modifier in the method declaration 250 | 251 | /*string infile = $@"D:\BackUp\backupC\footsoldier_JP.bin"; 252 | string outfile = $@"D:\BackUp\backupC\lzss_footsoldier_JP.bin"; 253 | 254 | // need to adjust this based on size 255 | byte[] p1 = new byte[0x100000]; 256 | byte[] p4 = new byte[0x10000]; 257 | string filepath = $@"D:\BackUp\backupC\footsoldier_JP.bin"; 258 | //FileStream fs = new FileStream(filepath, FileMode.Open); 259 | // BinaryWriter bw = new BinaryWriter(File.Open(@"D:\BackUp\backupC\footsoldier_JP_dec2.bin", System.IO.FileMode.Create)); 260 | //BinaryWriter bw2 = new BinaryWriter(File.Open(@"D:\BackUp\backupC\footsoldier_JP_dec_buff.bin", System.IO.FileMode.Create)); 261 | complib.DecodeFile(infile, outfile, 0, 0, true); 262 | 263 | //BinaryReader br = new BinaryReader(fs); 264 | //br.ReadBytes(9); 265 | /*int filesize = (int)br.BaseStream.Length - 9; 266 | byte[] file = br.ReadBytes(filesize); 267 | fixed (byte* param1 = p1, param4 = p4, param2 = file) 268 | { 269 | byte* param3 = param2 + filesize; 270 | Decrypt.DoDecrypt(param1, param2, param3, param4); 271 | bw.Write(p1); 272 | bw.Flush(); 273 | bw2.Write(p4); 274 | bw2.Flush(); 275 | }*/ 276 | } 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /SymphoniaUndub_Scripts/Decrypt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace SymphoniaUndub_Scripts 6 | { 7 | public static class Decrypt 8 | { 9 | // This code was originally taken from the Ghidra decompilation of the game's decompression routine. 10 | // I started commenting, renaming variables, and decoupling variables that were used for multiple things. 11 | // Part way through, I learned this was the compto compression, so I stopped work on this. 12 | 13 | // I think it's interesting so I left it here! 14 | 15 | 16 | public static unsafe void DoDecrypt(byte* output, byte* ptr_btlenemy, byte* ptr_end, byte* buffer) 17 | { 18 | //p1 is actual destination - dont care about p4 19 | //p2 is source btlenemy (after first 9 bytes) 20 | //p3 is end of btlenemy only used as end condition 21 | //p4 is stack/buffer byte[] 22 | 23 | uint flag_bits; 24 | byte* new_ptr; 25 | uint bottom; 26 | uint top; 27 | int index; 28 | int loop_counter; 29 | uint encode_value; 30 | uint uVar12; 31 | int char_to_write; 32 | byte first_value; 33 | byte second_value; 34 | uint uVar16; 35 | int end_index; 36 | int offset; 37 | uint uloop_counter; 38 | 39 | index = 0; 40 | end_index = 0x1fd; 41 | do 42 | { 43 | index = index + 8; 44 | end_index = end_index + -1; 45 | } while (end_index != 0); 46 | end_index = 0xfef - index; 47 | if (index < 0xfef) 48 | { 49 | do 50 | { 51 | end_index = end_index + -1; 52 | } while (end_index != 0); 53 | } 54 | char_to_write = '\0'; 55 | offset = 0; 56 | loop_counter = 0x80; 57 | do 58 | { 59 | // writing initial "nonsense" into buffer 60 | *(int*)(buffer + offset) = char_to_write; 61 | *(int*)(buffer + offset + 1) = 0; 62 | *(int*)(buffer + offset + 2) = char_to_write; 63 | *(int*)(buffer + offset + 3) = 0; 64 | *(int*)(buffer + offset + 4) = char_to_write; 65 | *(int*)(buffer + offset + 5) = 0; 66 | *(int*)(buffer + offset + 6) = char_to_write; 67 | char_to_write = char_to_write + '\x01'; 68 | *(int*)(buffer + offset + 7) = 0; 69 | *(int*)(buffer + offset + 8) = char_to_write; 70 | *(int*)(buffer + offset + 9) = 0; 71 | *(int*)(buffer + offset + 10) = char_to_write; 72 | *(int*)(buffer + offset + 0xb) = 0; 73 | *(int*)(buffer + offset + 0xc) = char_to_write; 74 | *(int*)(buffer + offset + 0xd) = 0; 75 | *(int*)(buffer + offset + 0xe) = char_to_write; 76 | *(int*)(buffer + offset + 0xf) = 0; 77 | char_to_write = char_to_write + '\x01'; 78 | offset = offset + 0x10; 79 | loop_counter = loop_counter + -1; 80 | } while (loop_counter != 0); 81 | char_to_write = '\0'; 82 | loop_counter = 0x20; 83 | do 84 | { 85 | *(int*)(buffer + offset) = char_to_write; 86 | 87 | *(int*)(buffer + offset + 1) = 0xff; 88 | *(int*)(buffer + offset + 2) = char_to_write; 89 | *(int*)(buffer + offset + 3) = 0xff; 90 | *(int*)(buffer + offset + 4) = char_to_write; 91 | *(int*)(buffer + offset + 5) = 0xff; 92 | *(int*)(buffer + offset + 6) = char_to_write; 93 | char_to_write = char_to_write + '\x01'; 94 | *(int*)(buffer + offset + 7) = char_to_write; 95 | *(int*)(buffer + offset + 8) = 0xff; 96 | *(int*)(buffer + offset + 9) = char_to_write; 97 | *(int*)(buffer + offset + 10) = 0xff; 98 | *(int*)(buffer + offset + 0xb) = char_to_write; 99 | *(int*)(buffer + offset + 0xc) = 0xff; 100 | *(int*)(buffer + offset + 0xd) = char_to_write; 101 | char_to_write = char_to_write + '\x01'; 102 | *(int*)(buffer + offset + 0xe) = char_to_write; 103 | *(int*)(buffer + offset + 0xf) = 0xff; 104 | *(int*)(buffer + offset + 0x10) = char_to_write; 105 | *(int*)(buffer + offset + 0x11) = 0xff; 106 | *(int*)(buffer + offset + 0x12) = char_to_write; 107 | *(int*)(buffer + offset + 0x13) = 0xff; 108 | *(int*)(buffer + offset + 0x14) = char_to_write; 109 | char_to_write = char_to_write + '\x01'; 110 | *(int*)(buffer + offset + 0x15) = char_to_write; 111 | *(int*)(buffer + offset + 0x16) = 0xff; 112 | *(int*)(buffer + offset + 0x17) = char_to_write; 113 | *(int*)(buffer + offset + 0x18) = 0xff; 114 | *(int*)(buffer + offset + 0x19) = char_to_write; 115 | *(int*)(buffer + offset + 0x1a) = 0xff; 116 | *(int*)(buffer + offset + 0x1b) = char_to_write; 117 | char_to_write = char_to_write + '\x01'; 118 | *(int*)(buffer + offset + 0x1c) = char_to_write; 119 | *(int*)(buffer + offset + 0x1d) = 0xff; 120 | *(int*)(buffer + offset + 0x1e) = char_to_write; 121 | *(int*)(buffer + offset + 0x1f) = 0xff; 122 | *(int*)(buffer + offset + 0x20) = char_to_write; 123 | *(int*)(buffer + offset + 0x21) = 0xff; 124 | *(int*)(buffer + offset + 0x22) = char_to_write; 125 | char_to_write = char_to_write + '\x01'; 126 | *(int*)(buffer + offset + 0x23) = char_to_write; 127 | *(int*)(buffer + offset + 0x24) = 0xff; 128 | *(int*)(buffer + offset + 0x25) = char_to_write; 129 | *(int*)(buffer + offset + 0x26) = 0xff; 130 | *(int*)(buffer + offset + 0x27) = char_to_write; 131 | *(int*)(buffer + offset + 0x28) = 0xff; 132 | *(int*)(buffer + offset + 0x29) = char_to_write; 133 | char_to_write = char_to_write + '\x01'; 134 | *(int*)(buffer + offset + 0x2a) = char_to_write; 135 | *(int*)(buffer + offset + 0x2b) = 0xff; 136 | *(int*)(buffer + offset + 0x2c) = char_to_write; 137 | *(int*)(buffer + offset + 0x2d) = 0xff; 138 | *(int*)(buffer + offset + 0x2e) = char_to_write; 139 | *(int*)(buffer + offset + 0x2f) = 0xff; 140 | *(int*)(buffer + offset + 0x30) = char_to_write; 141 | char_to_write = char_to_write + '\x01'; 142 | *(int*)(buffer + offset + 0x31) = char_to_write; 143 | *(int*)(buffer + offset + 0x32) = 0xff; 144 | *(int*)(buffer + offset + 0x33) = char_to_write; 145 | *(int*)(buffer + offset + 0x34) = 0xff; 146 | *(int*)(buffer + offset + 0x35) = char_to_write; 147 | *(int*)(buffer + offset + 0x36) = 0xff; 148 | *(int*)(buffer + offset + 0x37) = char_to_write; 149 | char_to_write = char_to_write + '\x01'; 150 | offset = offset + 0x38; 151 | loop_counter = loop_counter + -1; 152 | } while (loop_counter != 0); 153 | 154 | uVar12 = 0xfef; 155 | // flag bits tells which logic we do - init 0 156 | flag_bits = 0; 157 | // finished setup 158 | // now we decrypt 159 | while (ptr_btlenemy < ptr_end) 160 | { 161 | flag_bits = flag_bits >> 1; 162 | new_ptr = ptr_btlenemy; 163 | if ((flag_bits & 0x100) == 0) 164 | { 165 | new_ptr = ptr_btlenemy + 1; 166 | flag_bits = (uint)(*ptr_btlenemy | 0xff00); //assign new flag bits 167 | } 168 | if ((flag_bits & 1) == 0) 169 | { 170 | first_value = *new_ptr; 171 | second_value = new_ptr[1]; 172 | ptr_btlenemy = new_ptr + 2; 173 | bottom = (uint)(second_value & 0xf); 174 | top = (uint)((second_value & 0xf0) << 4); 175 | encode_value = first_value | top; 176 | if (bottom < 0xF) 177 | { 178 | top = 0; 179 | if (bottom > 5) 180 | { 181 | // copy data out of buffer 182 | uloop_counter = bottom + 2 >> 3; // this will be either 2 (E) or 1 (others) 183 | do 184 | { 185 | byte output_value; 186 | uVar16 = encode_value + top; 187 | top = top + 8; 188 | output_value = *(byte*)(buffer + (uVar16 & 0xfff)); 189 | *(byte*)(buffer + uVar12) = output_value; 190 | *output = output_value; 191 | output_value = *(byte*)(buffer + (uVar16 + 1 & 0xfff)); 192 | uVar12 = uVar12 + 1 & 0xfff; 193 | *(byte*)(buffer + uVar12) = output_value; 194 | uVar12 = uVar12 + 1 & 0xfff; 195 | output[1] = output_value; 196 | output_value = *(byte*)(buffer + (uVar16 + 2 & 0xfff)); 197 | *(byte*)(buffer + uVar12) = output_value; 198 | uVar12 = uVar12 + 1 & 0xfff; 199 | output[2] = output_value; 200 | output_value = *(byte*)(buffer + (uVar16 + 3 & 0xfff)); 201 | *(byte*)(buffer + uVar12) = output_value; 202 | uVar12 = uVar12 + 1 & 0xfff; 203 | output[3] = output_value; 204 | output_value = *(byte*)(buffer + (uVar16 + 4 & 0xfff)); 205 | *(byte*)(buffer + uVar12) = output_value; 206 | uVar12 = uVar12 + 1 & 0xfff; 207 | output[4] = output_value; 208 | output_value = *(byte*)(buffer + (uVar16 + 5 & 0xfff)); 209 | *(byte*)(buffer + uVar12) = output_value; 210 | uVar12 = uVar12 + 1 & 0xfff; 211 | output[5] = output_value; 212 | output_value = *(byte*)(buffer + (uVar16 + 6 & 0xfff)); 213 | *(byte*)(buffer + uVar12) = output_value; 214 | uVar12 = uVar12 + 1 & 0xfff; 215 | output[6] = output_value; 216 | output_value = *(byte*)(buffer + (uVar16 + 7 & 0xfff)); 217 | *(byte*)(buffer + uVar12) = output_value; 218 | uVar12 = uVar12 + 1 & 0xfff; 219 | output[7] = output_value; 220 | output = output + 8; 221 | uloop_counter = uloop_counter - 1; 222 | } while (uloop_counter != 0); 223 | } 224 | loop_counter = (int)(bottom + 3 - top); 225 | if (top <= bottom + 2) 226 | { 227 | // write to.. buffer and output at same time? 228 | do 229 | { 230 | bottom = encode_value + top; 231 | top = top + 1; 232 | first_value = *(byte*)(buffer + (bottom & 0xfff)); 233 | *(byte*)(buffer + uVar12) = first_value; 234 | uVar12 = uVar12 + 1 & 0xfff; 235 | *output = first_value; 236 | output = output + 1; 237 | loop_counter = loop_counter + -1; 238 | } while (loop_counter != 0); 239 | } 240 | } 241 | else 242 | { 243 | if (encode_value < 0x100) 244 | { 245 | first_value = *ptr_btlenemy; 246 | encode_value = encode_value + 0x12; 247 | ptr_btlenemy = new_ptr + 3; 248 | } 249 | else 250 | { 251 | encode_value = (top >> 8) + 2; 252 | } 253 | top = 0; 254 | if (8 < encode_value + 1) 255 | { 256 | bottom = encode_value >> 3; 257 | do 258 | { 259 | *(byte*)(buffer + uVar12) = first_value; 260 | uVar12 = uVar12 + 1 & 0xfff; 261 | top = top + 8; 262 | *output = first_value; 263 | *(byte*)(buffer + uVar12) = first_value; 264 | uVar12 = uVar12 + 1 & 0xfff; 265 | output[1] = first_value; 266 | *(byte*)(buffer + uVar12) = first_value; 267 | uVar12 = uVar12 + 1 & 0xfff; 268 | output[2] = first_value; 269 | *(byte*)(buffer + uVar12) = first_value; 270 | uVar12 = uVar12 + 1 & 0xfff; 271 | output[3] = first_value; 272 | *(byte*)(buffer + uVar12) = first_value; 273 | uVar12 = uVar12 + 1 & 0xfff; 274 | output[4] = first_value; 275 | *(byte*)(buffer + uVar12) = first_value; 276 | uVar12 = uVar12 + 1 & 0xfff; 277 | output[5] = first_value; 278 | *(byte*)(buffer + uVar12) = first_value; 279 | uVar12 = uVar12 + 1 & 0xfff; 280 | output[6] = first_value; 281 | *(byte*)(buffer + uVar12) = first_value; 282 | uVar12 = uVar12 + 1 & 0xfff; 283 | output[7] = first_value; 284 | output = output + 8; 285 | bottom = bottom - 1; 286 | } while (bottom != 0); 287 | } 288 | loop_counter = (int)((encode_value + 1) - top); 289 | if (top <= encode_value) 290 | { 291 | do 292 | { 293 | *(byte*)(buffer + uVar12) = first_value; 294 | uVar12 = uVar12 + 1 & 0xfff; 295 | *output = first_value; 296 | output = output + 1; 297 | loop_counter = loop_counter + -1; 298 | } while (loop_counter != 0); 299 | } 300 | } 301 | } 302 | else 303 | { 304 | // uvar1 ends in 1 we go here 305 | first_value = *new_ptr; 306 | ptr_btlenemy = new_ptr + 1; 307 | *(byte*)(buffer + uVar12) = first_value; 308 | uVar12 = uVar12 + 1 & 0xfff; 309 | *output = first_value; 310 | output = output + 1; 311 | } 312 | } 313 | return; 314 | } 315 | 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /SymphoniaUndub_Scripts/complib.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | // Complib taken from https://github.com/AdmiralCurtiss/compto-sharp 5 | // Thank you AdmiralCurtiss!!! 6 | 7 | namespace SymphoniaUndub_Scripts 8 | { 9 | class LzState 10 | { 11 | public int F; 12 | public int T; 13 | public ulong textsize; 14 | public ulong codesize; 15 | public ulong printcount; 16 | public byte[] text_buf = new byte[complib.N + complib.MF - 1]; 17 | public int match_position; 18 | public int match_length; 19 | public int[] lson = new int[complib.N + 1]; 20 | public int[] rson = new int[complib.N + 257]; 21 | public int[] dad = new int[complib.N + 1]; 22 | } 23 | 24 | public static class complib 25 | { 26 | public const int SUCCESS = 0; 27 | public const int ERROR_FILE_IN = -1; 28 | public const int ERROR_FILE_OUT = -2; 29 | public const int ERROR_MALLOC = -3; 30 | public const int ERROR_BAD_INPUT = -4; 31 | public const int ERROR_UNKNOWN_VERSION = -5; 32 | public const int ERROR_FILES_MISMATCH = -6; 33 | 34 | public const int N = 0x1000; 35 | public const int NIL = N; 36 | public const int MF = 0x12; 37 | public const int MAX_DUP = (0x100 + 0x12); 38 | 39 | private static System.IO.StreamWriter profilef = null; 40 | 41 | private static LzState LzStateCreate() 42 | { 43 | LzState State = new LzState(); 44 | State.textsize = 0; 45 | State.codesize = 0; 46 | State.printcount = 0; 47 | return State; 48 | } 49 | 50 | private static void LzStateDelete(LzState State) 51 | { 52 | State = null; 53 | } 54 | 55 | private static string GetErrorString(int error) 56 | { 57 | switch (error) 58 | { 59 | case SUCCESS: return "Success"; 60 | case ERROR_FILE_IN: return "Error with input file"; 61 | case ERROR_FILE_OUT: return "Error with output file"; 62 | case ERROR_MALLOC: return "Malloc failure"; 63 | case ERROR_BAD_INPUT: return "Bad Input"; 64 | case ERROR_UNKNOWN_VERSION: return "Unknown version"; 65 | case ERROR_FILES_MISMATCH: return "Mismatch"; 66 | default: return "Unknown error"; 67 | } 68 | } 69 | 70 | private static void FillTextBuffer(LzState State) 71 | { 72 | int n, p; 73 | for (n = 0, p = 0; n != 0x100; n++, p += 8) { State.text_buf[p + 6] = State.text_buf[p + 4] = State.text_buf[p + 2] = State.text_buf[p + 0] = (byte)n; State.text_buf[p + 7] = State.text_buf[p + 5] = State.text_buf[p + 3] = State.text_buf[p + 1] = 0; } 74 | for (n = 0; n != 0x100; n++, p += 7) { State.text_buf[p + 6] = State.text_buf[p + 4] = State.text_buf[p + 2] = State.text_buf[p + 0] = (byte)n; State.text_buf[p + 5] = State.text_buf[p + 3] = State.text_buf[p + 1] = 0xff; } 75 | while (p != N) State.text_buf[p++] = 0; 76 | } 77 | 78 | private static int PrepareVersion(LzState State, int version) 79 | { 80 | if (State != null) State.T = 2; 81 | switch (version) 82 | { 83 | case 0: break; 84 | case 1: if (State != null) State.F = 0x12; break; 85 | case 3: if (State != null) State.F = 0x11; break; 86 | default: return ERROR_UNKNOWN_VERSION; 87 | } 88 | return SUCCESS; 89 | } 90 | 91 | private static void InitTree(LzState State) 92 | { 93 | int i; 94 | for (i = N + 1; i <= N + 256; i++) State.rson[i] = NIL; 95 | for (i = 0; i < N; i++) State.dad[i] = NIL; 96 | } 97 | 98 | private static void InsertNode(LzState State, int r) 99 | { 100 | int i, p, cmp; 101 | int key; 102 | 103 | cmp = 1; key = r; p = N + 1 + State.text_buf[key]; 104 | State.rson[r] = State.lson[r] = NIL; State.match_length = 0; 105 | 106 | while (true) 107 | { 108 | if (cmp >= 0) 109 | { 110 | if (State.rson[p] != NIL) p = State.rson[p]; 111 | else { State.rson[p] = r; State.dad[r] = p; return; } 112 | } 113 | else 114 | { 115 | if (State.lson[p] != NIL) p = State.lson[p]; 116 | else { State.lson[p] = r; State.dad[r] = p; return; } 117 | } 118 | 119 | for (i = 1; i < State.F; i++) if ((cmp = State.text_buf[key + i] - State.text_buf[p + i]) != 0) break; 120 | 121 | if (i > State.match_length) 122 | { 123 | State.match_position = p; 124 | if ((State.match_length = i) >= State.F) break; 125 | } 126 | } 127 | 128 | State.dad[r] = State.dad[p]; State.lson[r] = State.lson[p]; State.rson[r] = State.rson[p]; 129 | State.dad[State.lson[p]] = r; State.dad[State.rson[p]] = r; 130 | 131 | if (State.rson[State.dad[p]] == p) State.rson[State.dad[p]] = r; else State.lson[State.dad[p]] = r; 132 | 133 | State.dad[p] = NIL; 134 | } 135 | 136 | private static void DeleteNode(LzState State, int p) 137 | { 138 | int q; 139 | 140 | if (State.dad[p] == NIL) return; 141 | 142 | if (State.rson[p] == NIL) q = State.lson[p]; 143 | else if (State.lson[p] == NIL) q = State.rson[p]; 144 | else 145 | { 146 | q = State.lson[p]; 147 | if (State.rson[q] != NIL) 148 | { 149 | do { q = State.rson[q]; } while (State.rson[q] != NIL); 150 | State.rson[State.dad[q]] = State.lson[q]; State.dad[State.lson[q]] = State.dad[q]; 151 | State.lson[q] = State.lson[p]; State.dad[State.lson[p]] = q; 152 | } 153 | State.rson[q] = State.rson[p]; State.dad[State.rson[p]] = q; 154 | } 155 | State.dad[q] = State.dad[p]; 156 | 157 | if (State.rson[State.dad[p]] == p) State.rson[State.dad[p]] = q; else State.lson[State.dad[p]] = q; 158 | State.dad[p] = NIL; 159 | } 160 | 161 | public static int Encode(int version, byte[] @in, int inl, byte[] @out, ref uint outl) 162 | { 163 | /* pointers! */ 164 | int insp = 0, inst = 0, ousp = 0, oust = 0, inspb = 0, insplb = 0; 165 | int i, c, len, r, s, last_match_length, dup_match_length = 0, code_buf_ptr, dup_last_match_length = 0; 166 | byte[] code_buf = new byte[1 + 8 * 5]; 167 | byte mask; 168 | int error = SUCCESS; 169 | LzState State = LzStateCreate(); 170 | if (State == null) goto _cleanup; 171 | 172 | inst = inl; oust = (int)(outl); 173 | 174 | if (version == 0) 175 | { 176 | while (true) 177 | { 178 | int left = inst - insp; 179 | int left8 = left; 180 | //printf("left8:%d\n", left8); 181 | if (left8 > 8) left8 = 8; 182 | //{ if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = (byte)(((1 << left8) - 1) << (8 - left8)); } 183 | { if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = (byte)(((1 << left8) - 1) << (0)); } 184 | 185 | for (i = 0; i < 8; i++) 186 | { 187 | if (insp >= inst) break; c = @in[insp++]; 188 | { if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = (byte)c; } 189 | } 190 | 191 | if (insp >= inst) break; 192 | } 193 | 194 | if (insp != inst) 195 | { 196 | LzStateDelete(State); 197 | Console.WriteLine("(insp != inst) ({0} != {1})", insp, inst); 198 | return ERROR_BAD_INPUT; 199 | } 200 | 201 | outl = (uint)(ousp - 0); 202 | 203 | LzStateDelete(State); 204 | return error; 205 | } 206 | 207 | FillTextBuffer(State); 208 | PrepareVersion(State, version); 209 | InitTree(State); 210 | 211 | code_buf[0] = 0x00; 212 | code_buf_ptr = mask = 1; 213 | s = 0; r = N - State.F; 214 | 215 | //printf("%d\n", r); 216 | 217 | for (len = 0; len < State.F; len++) { if (insp >= inst) break; c = @in[insp++]; State.text_buf[r + len] = (byte)c; } 218 | if ((State.textsize = (ulong)len) == 0) return SUCCESS; 219 | 220 | for (i = 1; i <= State.F; i++) InsertNode(State, r - i); 221 | InsertNode(State, r); 222 | 223 | do 224 | { 225 | if (version >= 3) 226 | { 227 | if (insplb - inspb <= 0) 228 | { 229 | insplb = inspb + 1; 230 | while ((insplb < inst) && (@in[insplb] == @in[inspb])) insplb++; 231 | } 232 | 233 | dup_match_length = insplb - inspb; 234 | } 235 | 236 | if (State.match_length > len) State.match_length = len; 237 | 238 | if (version >= 3 && dup_match_length > MAX_DUP) dup_match_length = MAX_DUP; 239 | 240 | if (version >= 3 && dup_match_length > (State.T + 1) && dup_match_length >= State.match_length) 241 | { 242 | if (dup_match_length >= (inst - insp)) dup_match_length--; 243 | } 244 | else 245 | { 246 | if (State.match_length >= (inst - insp)) State.match_length--; 247 | } 248 | /* 249 | if (version >= 3 && dup_match_length > (State.T + 1) && dup_match_length >= State.match_length) { 250 | if (dup_match_length >= (inst - insp)) dup_match_length -= 8; 251 | } else { 252 | if (State.match_length >= (inst - insp)) State.match_length -= 8; 253 | } 254 | */ 255 | 256 | if (version >= 3 && dup_match_length > (State.T + 1) && dup_match_length >= State.match_length) 257 | { 258 | State.match_length = dup_match_length; 259 | State.match_position = r; 260 | 261 | if (State.match_length <= 0x12) 262 | { 263 | code_buf[code_buf_ptr++] = State.text_buf[r]; 264 | code_buf[code_buf_ptr++] = (byte)(0x0f | (((State.match_length - (State.T + 1)) & 0xf) << 4)); 265 | } 266 | else 267 | { 268 | code_buf[code_buf_ptr++] = (byte)(State.match_length - 0x13); 269 | code_buf[code_buf_ptr++] = 0x0f; 270 | code_buf[code_buf_ptr++] = State.text_buf[r]; 271 | } 272 | } 273 | else if (State.match_length > State.T) 274 | { 275 | code_buf[code_buf_ptr++] = (byte)State.match_position; 276 | code_buf[code_buf_ptr++] = (byte)(((State.match_position >> 4) & 0xf0) | ((State.match_length - (State.T + 1)) & 0x0f)); 277 | } 278 | else 279 | { 280 | code_buf[0] |= mask; 281 | State.match_length = 1; 282 | code_buf[code_buf_ptr++] = State.text_buf[r]; 283 | } 284 | 285 | if ((mask <<= 1) == 0) 286 | { 287 | for (i = 0; i < code_buf_ptr; i++) { if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = code_buf[i]; } 288 | State.codesize += (ulong)code_buf_ptr; 289 | code_buf[0] = 0x00; code_buf_ptr = mask = 1; 290 | } 291 | 292 | last_match_length = State.match_length; 293 | for (i = 0; i < last_match_length; i++) 294 | { 295 | if (insp >= inst) break; c = @in[insp++]; DeleteNode(State, s); State.text_buf[s] = (byte)c; 296 | if (s < State.F - 1) State.text_buf[s + N] = (byte)c; 297 | s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); 298 | inspb++; 299 | InsertNode(State, r); 300 | } 301 | 302 | State.textsize += (ulong)i; 303 | 304 | while (i++ < last_match_length) 305 | { 306 | DeleteNode(State, s); s = (s + 1) & (N - 1); r = (r + 1) & (N - 1); 307 | inspb++; 308 | if ((--len) != 0) InsertNode(State, r); 309 | } 310 | } while (len > 0); 311 | 312 | if (code_buf_ptr > 1) 313 | { 314 | for (i = 0; i < code_buf_ptr; i++) { if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = code_buf[i]; } 315 | State.codesize += (ulong)code_buf_ptr; 316 | } 317 | 318 | _cleanup: 319 | 320 | if (State == null) return ERROR_MALLOC; 321 | if (insp != inst) 322 | { 323 | Console.WriteLine("(insp != inst) ({0} != {1})", insp, inst); 324 | return ERROR_BAD_INPUT; 325 | } 326 | 327 | outl = (uint)(ousp - 0); 328 | LzStateDelete(State); 329 | 330 | return SUCCESS; 331 | } 332 | 333 | public static int Decode(int version, byte[] @in, uint inl, byte[] @out, ref uint outl) 334 | { 335 | if (version == 0 && inl == outl) 336 | { 337 | for (uint loopidx = 0; loopidx < inl; ++loopidx) 338 | { 339 | @out[loopidx] = @in[loopidx]; 340 | } 341 | return SUCCESS; 342 | } 343 | 344 | /* pointers! */ 345 | int insp = 0, inst = 0, ousp = 0, oust = 0; 346 | uint flags = 0, i, j, k, r, c; 347 | int error = SUCCESS; 348 | LzState State = LzStateCreate(); 349 | if (State == null) goto _cleanup; 350 | 351 | inst = (int)inl; oust = (int)(outl); 352 | 353 | FillTextBuffer(State); 354 | if ((error = PrepareVersion(State, version)) != SUCCESS) return error; 355 | r = (uint)(N - State.F); 356 | 357 | for (; ; ) 358 | { 359 | if (((flags >>= 1) & 0x100) == 0) { if (insp >= inst) break; c = @in[insp++]; if (profilef != null) profilef.WriteLine("-------- {0:X2} -------- [{1:X8}:{2:X8}]", c, insp - 0, ousp - 0); flags = c | 0xff00; } 360 | if ((flags & 1) != 0) { if (insp >= inst) break; c = @in[insp++]; if (profilef != null) profilef.WriteLine("BYTE[{0:X2}]", c); { { if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = (byte)c; } State.text_buf[r++] = (byte)c; r &= (uint)(N - 1); }; continue; } 361 | if (insp >= inst) break; i = @in[insp++]; if (insp >= inst) break; j = @in[insp++]; i |= (j & 0xf0) << 4; j = (uint)((j & 0x0f) + State.T); 362 | if (version == 1 || j < (State.F)) { if (profilef != null) profilef.Write("WINDOW[{0:X3},*{1:X2}] : (", j + 1, i); for (k = 0; k <= j; k++) { c = State.text_buf[(i + k) & (N - 1)]; { { if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = (byte)c; } State.text_buf[r++] = (byte)c; r &= (uint)(N - 1); }; if (profilef != null && k != 0) profilef.Write(", "); if (profilef != null) profilef.Write("{0:X2}", c); } if (profilef != null) profilef.WriteLine(")"); continue; } 363 | if (i < 0x100) { if (insp >= inst) break; j = @in[insp++]; i += (uint)(State.F + 1); } else { j = i & 0xff; i = (uint)((i >> 8) + State.T); } 364 | if (profilef != null) profilef.Write("REPEAT[{0:X3},{1:X2}] : (", i + 1, j); for (k = 0; k <= i; k++) { { { if (ousp >= oust) { error = SUCCESS; goto _cleanup; } @out[ousp++] = (byte)j; } State.text_buf[r++] = (byte)j; r &= (uint)(N - 1); }; if (profilef != null && k != 0) profilef.Write(", "); if (profilef != null) profilef.Write("{0:X2}", j); } 365 | if (profilef != null) profilef.WriteLine(")"); 366 | } 367 | 368 | _cleanup: 369 | 370 | if (State == null) return ERROR_MALLOC; 371 | if (insp != inst) return ERROR_BAD_INPUT; 372 | 373 | outl = (uint)ousp; 374 | LzStateDelete(State); 375 | 376 | return error; 377 | } 378 | 379 | private static uint ReadUInt(System.IO.Stream s, bool littleEndian) 380 | { 381 | int b1 = s.ReadByte(); 382 | int b2 = s.ReadByte(); 383 | int b3 = s.ReadByte(); 384 | int b4 = s.ReadByte(); 385 | if (littleEndian) 386 | { 387 | return (uint)(b4 << 24 | b3 << 16 | b2 << 8 | b1); 388 | } 389 | else 390 | { 391 | return (uint)(b1 << 24 | b2 << 16 | b3 << 8 | b4); 392 | } 393 | } 394 | 395 | public static int DecodeStream(System.IO.Stream fin, System.IO.Stream fout, int raw, int version, bool littleEndian) 396 | { 397 | int error = SUCCESS; 398 | uint inl, outl; 399 | byte[] ind, outd; 400 | 401 | if (raw != 0) 402 | { 403 | inl = (uint)fin.Length; 404 | outl = inl * 10; 405 | } 406 | else 407 | { 408 | version = fin.ReadByte(); 409 | inl = ReadUInt(fin, littleEndian); 410 | outl = ReadUInt(fin, littleEndian); 411 | if (PrepareVersion(null, version) != SUCCESS) { error = ERROR_FILE_IN; goto _cleanup; } 412 | } 413 | 414 | ind = new byte[inl]; 415 | outd = new byte[outl]; 416 | 417 | fin.Read(ind, 0, (int)inl); 418 | 419 | error = Decode(version, ind, inl, outd, ref outl); 420 | 421 | if (fout != null) 422 | { 423 | fout.Write(outd, 0, (int)outl); 424 | } 425 | 426 | _cleanup: 427 | 428 | outd = null; 429 | ind = null; 430 | 431 | return error; 432 | } 433 | 434 | public static int DecodeFile(string @in, string @out, int raw, int version, bool littleEndian) 435 | { 436 | int error = SUCCESS; 437 | System.IO.FileStream fin, fout = null; 438 | 439 | Console.Write("Decoding[{0:X2}] {1} -> {2}...", version, @in ?? "", @out ?? ""); 440 | 441 | fin = new System.IO.FileStream(@in, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); 442 | 443 | if (@out != null) 444 | { 445 | fout = new System.IO.FileStream(@out, System.IO.FileMode.Create); 446 | } 447 | 448 | error = DecodeStream(fin, fout, raw, version, littleEndian); 449 | 450 | if (fout != null) fout.Close(); 451 | if (fin != null) fin.Close(); 452 | 453 | Console.WriteLine(GetErrorString(error)); 454 | 455 | return error; 456 | } 457 | 458 | private static void WriteUInt(System.IO.Stream s, uint v, bool littleEndian) 459 | { 460 | byte b1 = (byte)(v & 0xFF); 461 | byte b2 = (byte)((v >> 8) & 0xFF); 462 | byte b3 = (byte)((v >> 16) & 0xFF); 463 | byte b4 = (byte)((v >> 24) & 0xFF); 464 | if (littleEndian) 465 | { 466 | s.WriteByte(b1); 467 | s.WriteByte(b2); 468 | s.WriteByte(b3); 469 | s.WriteByte(b4); 470 | } 471 | else 472 | { 473 | s.WriteByte(b4); 474 | s.WriteByte(b3); 475 | s.WriteByte(b2); 476 | s.WriteByte(b1); 477 | } 478 | } 479 | 480 | public static int EncodeStream(System.IO.Stream fin, System.IO.Stream fout, int raw, int version, bool littleEndian) 481 | { 482 | int error = SUCCESS; 483 | uint inl, outl; 484 | byte[] ind, outd; 485 | 486 | int eversion = 0; 487 | 488 | if (version < 0) 489 | { 490 | version = -version; 491 | eversion = 0; 492 | } 493 | else 494 | { 495 | eversion = version; 496 | } 497 | 498 | //Console.WriteLine("{0}, {1}", version, eversion); 499 | 500 | inl = (uint)fin.Length; outl = ((inl * 9) / 8) + 10; 501 | 502 | ind = new byte[inl]; 503 | outd = new byte[outl]; 504 | 505 | fin.Read(ind, 0, (int)inl); 506 | 507 | error = Encode(eversion, ind, (int)inl, outd, ref outl); 508 | 509 | if (fout != null) 510 | { 511 | if (raw == 0) 512 | { 513 | fout.WriteByte((byte)version); 514 | WriteUInt(fout, outl, littleEndian); 515 | WriteUInt(fout, inl, littleEndian); 516 | } 517 | 518 | fout.Write(outd, 0, (int)outl); 519 | } 520 | 521 | outd = null; 522 | ind = null; 523 | 524 | Console.WriteLine(GetErrorString(error)); 525 | 526 | return error; 527 | } 528 | 529 | public static int EncodeFile(string @in, string @out, int raw, int version, bool littleEndian) 530 | { 531 | int error = SUCCESS; 532 | System.IO.FileStream fin = null, fout = null; 533 | Console.Write("Encoding[{0:X2}] {1} -> {2}...", version < 0 ? -version : version, @in ?? "", @out ?? ""); 534 | 535 | fin = new System.IO.FileStream(@in, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); 536 | 537 | if (@out != null) 538 | { 539 | fout = new System.IO.FileStream(@out, System.IO.FileMode.Create); 540 | } 541 | 542 | error = EncodeStream(fin, fout, raw, version, littleEndian); 543 | 544 | if (fout != null) fout.Close(); 545 | if (fin != null) fin.Close(); 546 | 547 | return error; 548 | } 549 | 550 | public static int DumpTextBuffer(string @out) 551 | { 552 | int error = SUCCESS; 553 | System.IO.FileStream fout = null; 554 | 555 | Console.Write("Dumping text buffer..."); 556 | 557 | LzState State = LzStateCreate(); 558 | if (State == null) goto _cleanup; 559 | 560 | FillTextBuffer(State); 561 | 562 | fout = new System.IO.FileStream(@out, System.IO.FileMode.Create); 563 | 564 | fout.Write(State.text_buf, 0, N); 565 | 566 | _cleanup: 567 | 568 | if (State != null) LzStateDelete(State); 569 | if (fout != null) fout.Close(); 570 | 571 | Console.WriteLine(GetErrorString(error)); 572 | 573 | return error; 574 | } 575 | 576 | public static int CheckCompression(string @in, int version) 577 | { 578 | System.IO.FileStream fin = null; byte[] ind = null, outd = null, outd2 = null; 579 | int error = SUCCESS; uint inl, outl, outl2; 580 | 581 | Console.Write("Checking compression [{0:X2}] ({1}) ...", version, @in); 582 | 583 | fin = new System.IO.FileStream(@in, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); 584 | 585 | outl2 = inl = (uint)fin.Length; outl = ((inl * 9) / 8) + 10; 586 | 587 | ind = new byte[inl]; 588 | outd = new byte[outl]; 589 | outd2 = new byte[outl2]; 590 | 591 | fin.Read(ind, 0, (int)inl); 592 | 593 | if ((error = Encode(version, ind, (int)inl, outd, ref outl)) != SUCCESS) { goto _cleanup; } 594 | 595 | if ((error = Decode(version, outd, outl, outd2, ref outl2)) != SUCCESS) { goto _cleanup; } 596 | 597 | if (inl != outl2) { error = ERROR_FILES_MISMATCH; goto _cleanup; } 598 | if (!ind.SequenceEqual(outd2)) { error = ERROR_FILES_MISMATCH; goto _cleanup; } 599 | 600 | _cleanup: 601 | 602 | outd2 = null; 603 | outd = null; 604 | ind = null; 605 | 606 | if (fin != null) fin.Close(); 607 | 608 | Console.WriteLine(GetErrorString(error)); 609 | 610 | return error; 611 | } 612 | 613 | public static void ProfileStart(string @out) 614 | { 615 | profilef = new System.IO.StreamWriter(@out, false); 616 | } 617 | 618 | public static void ProfileEnd() 619 | { 620 | if (profilef == null) return; 621 | profilef.Close(); 622 | profilef = null; 623 | } 624 | } 625 | } 626 | --------------------------------------------------------------------------------