using StreamFAdd; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; namespace sceWork { internal class Program { public static tableModule jpcodes; public static tableModule codes; public static Encoding SJIS = Encoding.GetEncoding(932); public static String altPath; private static bool useSJIS = false; private static bool useAltPath = false; private static bool doDeduplication = false; private static bool dumpRaw = false; private static bool dumpCount = false; private static bool flag1 = false; private static bool flag2 = false; public static bool verbose = false; private static void Main(string[] args) { Console.WriteLine("TOD1RSCE sceWork v0.9 - Cross-Platform"); string exeDir = Path.GetDirectoryName(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); exeDir = new Uri(exeDir).LocalPath; if (File.Exists(Path.Combine(exeDir, "jpcodes.txt"))) jpcodes = new tableModule(Path.Combine(exeDir, "jpcodes.txt")); if (File.Exists(Path.Combine(exeDir, "codes.txt"))) codes = new tableModule(Path.Combine(exeDir, "codes.txt"), true); if (args.Length == 0) { Console.WriteLine("Generously donated by Temple of Tales Translations team"); Console.WriteLine("http://temple-tales.ru/translations.html\n"); Console.WriteLine("Usage:"); Console.WriteLine(" extract: .exe -e *.tod1rsce"); Console.WriteLine(" extract: .exe -e *.tod1rsce4"); Console.WriteLine(" extract: .exe -e *.rsce"); Console.WriteLine(" extract: .exe -e "); Console.WriteLine(" repack : .exe -r *.tod1rsce"); Console.WriteLine(" repack : .exe -r *.tod1rsce4"); Console.WriteLine(" repack : .exe -r *.rsce"); Console.WriteLine(" repack : .exe -r "); Console.WriteLine("Add params:"); Console.WriteLine(" -i : Specify a diferent input file"); Console.WriteLine(" -v : Verbose failed string output"); Console.WriteLine(" -as : Add bytes to start file"); Console.WriteLine(" -ae : Seek file to 16 bytes"); Console.WriteLine(" -sjis : Use CP-932 (SHIFT-JIS) encoding"); Console.WriteLine(" -dedup : deduplicate strings on insertion"); Console.WriteLine(" -count : Dump byte count (debug)"); Console.WriteLine(" -raw : Dump/insert raw bytes ignoring JPCODES and CODES"); //Console.ReadKey(); Environment.Exit(0); } ParseArgs(args); string str = args[0]; //Do extraction if (str == "-e") { if (Path.GetExtension(args[1]) == ".rsce" || Path.GetExtension(args[1]) == ".tod1rsce" || Path.GetExtension(args[1]) == ".tod1rsce4") { if (jpcodes == null) { MiscUtils.Warn("No JPCODES.txt file! Dumping raw bytes..."); } Extract(args[1]); } else if (Directory.Exists(args[1])) { foreach (string fileName in ((IEnumerable)Directory.GetFiles(args[1], "*.*", SearchOption.AllDirectories)).Where((Func)(s => s.EndsWith(".rsce") || s.EndsWith(".tod1rsce") || s.EndsWith(".tod1rsce4"))).ToArray()) Extract(fileName); } } //Do insertion if (str == "-r") { if (Path.GetExtension(args[1]) == ".rsce" || Path.GetExtension(args[1]) == ".tod1rsce" || Path.GetExtension(args[1]) == ".tod1rsce4") { if (jpcodes == null) { MiscUtils.Warn("No JPCODES.txt file! Assuming dump is raw..."); } Repack(args[1]); AddBytes(args[1], args); } else if (Directory.Exists(args[1])) { string[] array = ((IEnumerable)Directory.GetFiles(args[1], "*.*", SearchOption.AllDirectories)).Where((Func)(s => s.EndsWith(".rsce") || s.EndsWith(".tod1rsce") || s.EndsWith(".tod1rsce4"))).ToArray(); for (int index = 0; index < array.Length; ++index) { Repack(array[index]); AddBytes(array[index], args); } } } Console.WriteLine("Complete!"); //Console.ReadKey(); } private static void ParseArgs(string[] args) { for (int index = 0; index < args.Length; ++index) { try { if (args[index] == "-i") { useAltPath = true; if (!File.Exists(Path.GetFullPath(args[index + 1]))) { MiscUtils.Die("Error. The file of the -i parameter does not exist."); } altPath = Path.GetFullPath(args[index + 1]); MiscUtils.Info("Using input file: \"" + altPath + "\""); index++; } if (args[index] == "-v") { verbose = true; MiscUtils.Info("verbose mode active"); } if (args[index] == "-sjis") { useSJIS = true; MiscUtils.Info("using CP-932 (SHIFT-JIS) for this operation."); } if (args[index] == "-count") { dumpCount = true; MiscUtils.Info("dumping debug byte counts to file."); } if (args[index] == "-dedup") { doDeduplication = true; MiscUtils.Info("string deduplication is active for this operation."); } if (args[index] == "-raw") { dumpRaw = true; MiscUtils.Info("Ignoring JPCODES and CODES for this operation."); } if (args[index] == "-as") { flag1 = true; MiscUtils.Info("Activate add bytes to start."); if (!int.TryParse(args[index + 1], out _)) { MiscUtils.Die("Error. The value of the -as parameter is not a number."); } index++; } if (args[index] == "-ae") { flag2 = true; MiscUtils.Info("Activate add %16 to end."); } } catch (Exception) { MiscUtils.Die("Parameter reference error, perhaps a parameter was entered, but its value was not entered."); } } } private static void AddBytes(string fileName, string[] args) { int result = 0; Console.ForegroundColor = ConsoleColor.Red; for (int index = 0; index < args.Length; ++index) { if (args[index] == "-as") { int.TryParse(args[index + 1], out result); } } Console.ForegroundColor = ConsoleColor.White; StreamFunctionAdd streamFunctionAdd1 = new StreamFunctionAdd(); StreamFunctionAdd streamFunctionAdd2 = new StreamFunctionAdd(fileName); if (flag2) { streamFunctionAdd2.PositionStream = streamFunctionAdd2.LengthStream; streamFunctionAdd2.SeekValueWrite(16U); } if (!flag1) return; StreamFunctionAdd destination = new StreamFunctionAdd(); for (int index = 0; index < result; ++index) destination.WriteByte((byte)0); streamFunctionAdd2.PositionStream = 0L; streamFunctionAdd2.CopyPartTo(destination, (int)streamFunctionAdd2.LengthStream); streamFunctionAdd2.Dispose(); destination.SaveToFile(fileName); } private static void Extract(string fileName) { sceModule sceModule = new sceModule(fileName); if (!sceModule.isHaveText()) return; string path = !(Path.GetDirectoryName(fileName) != "") ? Environment.CurrentDirectory + "\\" + Path.GetFileNameWithoutExtension(fileName) + ".txt" : Path.GetDirectoryName(fileName) + "\\" + Path.GetFileNameWithoutExtension(fileName) + ".txt"; Console.WriteLine(string.Format("Extracting {0}...", Path.GetFileName(fileName))); if (jpcodes == null || dumpRaw) { ExtractRaw(fileName, sceModule); return; } if (dumpCount) { ExtractCount(fileName, sceModule); return; } if (codes == null) MiscUtils.Warn("No CODES.txt file! Special sequences will be ignored, output can get messy!"); List stringList = new List(); for (int idx = 0; idx < sceModule.Count; ++idx) { string str = sceModule.GetStringBlock(idx); if (codes != null) str = codes.ConvertNativeToTags(str); if (jpcodes != null) str = jpcodes.ConvertAtoB(str); stringList.Add(HexToAnsi(str)); stringList.Add("[ENDBLOCK]"); } if (useSJIS) File.WriteAllLines(path, stringList.ToArray(), SJIS); else File.WriteAllLines(path, stringList.ToArray()); } private static void ExtractRaw(string fileName, sceModule sceModule) { string path; if (Path.GetDirectoryName(fileName) == "") { path = Environment.CurrentDirectory + "\\" + Path.GetFileNameWithoutExtension(fileName) + "_raw.txt"; } else { path = Path.GetDirectoryName(fileName) + "\\" + Path.GetFileNameWithoutExtension(fileName) + "_raw.txt"; } List dump = new List(); for (int idx = 0; idx < sceModule.Count; ++idx) { dump.AddRange(sceModule.GetBlock(idx)); dump.AddRange(Encoding.ASCII.GetBytes("\r\n[ENDBLOCK]\r\n")); } File.WriteAllBytes(path, dump.ToArray()); return; } private static void ExtractCount(string fileName, sceModule sceModule) { string path; if (Path.GetDirectoryName(fileName) == "") { path = Environment.CurrentDirectory + "\\" + Path.GetFileNameWithoutExtension(fileName) + "_count.txt"; } else { path = Path.GetDirectoryName(fileName) + "\\" + Path.GetFileNameWithoutExtension(fileName) + "_count.txt"; } List byteInfo = new List(); int total = 0; bool switch1 = true; for (int idx = 0; idx < sceModule.Count; ++idx) { total += sceModule.GetBlock(idx).Length; if (total > 0xFFF && switch1) { byteInfo.Add("-----------CUT POINT-----------------------"); byteInfo.Add("| length average: " + (total / idx + 1)); byteInfo.Add("-----------CUT POINT-----------------------"); switch1 = false; } byteInfo.Add(string.Format("Block: {0:D3},", idx) + string.Format(" Ptr: 0x{0:X6}", sceModule.Header.fileStrings[idx].myOffset) + string.Format(" ({0:D}),", (int)sceModule.Header.fileStrings[idx].typeOffset) + string.Format(" Text: 0x{0:X6}", sceModule.Header.fileStrings[idx].offset) + string.Format(" | Line length: {0:D3}, ", sceModule.GetBlock(idx).Length) + string.Format("total length: {0:D4}", total)); } File.WriteAllLines(path, byteInfo.ToArray()); return; } private static void Repack(string fileName) { sceModule sceModule = new sceModule(fileName); //check if rsce has text beforehand if (!sceModule.isHaveText()) return; string path; if (useAltPath) path = altPath; else { if (Path.GetDirectoryName(fileName) == "") path = Environment.CurrentDirectory + "\\" + Path.GetFileNameWithoutExtension(fileName) + ".txt"; else path = Path.GetDirectoryName(fileName) + "\\" + Path.GetFileNameWithoutExtension(fileName) + ".txt"; } if (!File.Exists(path)) MiscUtils.Die("The file \"" + Path.GetFileName(path) + "\" does not exist!"); //Echo! Console.WriteLine(string.Format("Repacking {0}...", Path.GetFileName(fileName))); if (jpcodes == null || dumpRaw) { RepackRaw(path, sceModule); return; } //Convert UTF-8 to SJIS, as it's easier to detect kanjis in SJIS byte[] temp; if (!useSJIS) temp = Encoding.Convert(Encoding.UTF8, SJIS, File.ReadAllBytes(path)); else temp = File.ReadAllBytes(path); string[] stringSeparators = new string[] { "\r\n" }; string[] strArray = SJIS.GetString(temp).Split(stringSeparators, StringSplitOptions.None); //Can't ignore empty lines with the split (some lines might be blank in the source file) //but the last line can't be blank, so handle that here if (strArray[strArray.Length - 1].Equals(string.Empty)) { Array.Resize(ref strArray, strArray.Length - 1); } List stringList = new List(); List plainStringList = new List(); List lineNumberList = new List(); int lineNumber = 0; int blockLine = 0; string str = ""; for (int index = 0; index < strArray.Length; ++index) { if (strArray[index].StartsWith("//")) continue; if (strArray[index].StartsWith("[ENDBLOCK]")) { lineNumberList.Add(lineNumber - blockLine); blockLine = 0; stringList.Add(str); plainStringList.Add(str.Replace("\r\n", "\\r\\n")); str = ""; } else { if (strArray[index + 1].StartsWith("[ENDBLOCK]")) { str = str + strArray[index]; } else { str = str + strArray[index] + "\r\n"; blockLine++; } } lineNumber++; } //check if same ammount of lines are present if (sceModule.Header.fileStrings.Count != stringList.Count) { MiscUtils.Warn("Different amount of strings in the file!"); MiscUtils.Warn("expected " + sceModule.Header.fileStrings.Count + " but got " + stringList.Count); MiscUtils.Warn("Packing anyway, but expect errors!"); Console.WriteLine(); } for (int index = 0; index < stringList.Count; ++index) stringList[index] = AnsiToHex(stringList[index]); if (jpcodes != null) { for (int index = 0; index < stringList.Count; ++index) stringList[index] = jpcodes.ConvertBtoA(stringList[index]); } if (codes != null) { for (int index = 0; index < stringList.Count; ++index) stringList[index] = codes.ConvertTagsToNative(stringList[index]); } else { MiscUtils.Warn("No CODES.txt file! If the file has replaced tags they will be inserted verbatim!"); } int realLine = 0; int skippedLine = 0; for (int index = 0; index < stringList.Count; ++index) { realLine++; while (stringList[index].StartsWith(AnsiToHex(""))) { if (verbose) MiscUtils.Info("Ignoring block " + realLine + " (around line " + lineNumberList[index] + ")..."); lineNumberList.RemoveAt(index); plainStringList.RemoveAt(index); sceModule.Header.fileStrings.RemoveAt(index); stringList.RemoveAt(index); realLine++; skippedLine++; } sceModule.SetBlock(index, HexToArrayByte(stringList[index]), plainStringList, lineNumberList); } MiscUtils.Info("Ignored " + skippedLine + " blocks"); Console.WriteLine(); sceModule.Save(doDeduplication); sceModule.Dispose(); } private static void RepackRaw(string path, sceModule sceModule) { byte[] dump = File.ReadAllBytes(path); string hexStringDump = BitConverter.ToString(dump).Replace("-", string.Empty); string[] hexDump = hexStringDump.Split(new[] { AnsiToHex("\r\n[ENDBLOCK]\r\n") }, StringSplitOptions.None); if (hexDump[hexDump.Length - 1].Equals(string.Empty)) { Array.Resize(ref hexDump, hexDump.Length - 1); } for (int index = 0; index < hexDump.Length; ++index) sceModule.SetBlock(index, HexToArrayByte(hexDump[index])); sceModule.Save(); sceModule.Dispose(); return; } private static byte[] HexToArrayByte(string str) { List byteList = new List(); for (int index = 0; index < str.Length; index += 2) { byte num = (byte)(((uint)Program.HexToByte(str[index]) << 4) + (uint)Program.HexToByte(str[index + 1])); byteList.Add(num); } return byteList.ToArray(); } private static byte HexToByte(char s) { switch (s) { case '0': return 0; case '1': return 1; case '2': return 2; case '3': return 3; case '4': return 4; case '5': return 5; case '6': return 6; case '7': return 7; case '8': return 8; case '9': return 9; case 'A': return 10; case 'B': return 11; case 'C': return 12; case 'D': return 13; case 'E': return 14; case 'F': return 15; default: return 0; } } private static string HexToAnsi(string str) { byte[] temp = Encoding.Convert(Encoding.GetEncoding(932), Encoding.UTF8, Program.HexToArrayByte(str)); return Encoding.UTF8.GetString(temp); } private static string AnsiToHex(string str) { string str1 = ""; byte[] byteArr = Encoding.GetEncoding(932).GetBytes(str); for (int index = 0; index < byteArr.Length; ++index) str1 += System.BitConverter.ToString(byteArr, index, 1).Replace("-", string.Empty); return str1; } private static byte GetAnsiByte(char chr) => Encoding.GetEncoding(932).GetBytes(chr.ToString())[0]; private static string GetAnsi(byte[] data) => Encoding.GetEncoding(932).GetString(data, 0, data.Length); } }