protected void Decode(BinaryReader inp, FileInfo dstF) { textCoveragePool.Clear(); imageCoveragePool.Clear(); using var pout = new StreamWriter(dstF.FullName, false, new UTF8Encoding(true)); pout.NewLine = NewLine; bool commandMode = false; while (inp.BaseStream.HasRemaining()) { pout.Write("[{0:x8}]", inp.BaseStream.Position); pos = dstF.FullName + ":" + inp.BaseStream.Position.ToString("x"); currentOpcodeOffset = inp.BaseStream.Position; int opcode = inp.ReadByte() & 0xFF; long lastPosition = inp.BaseStream.Position; string GetRemainingByteCodes() { long remainingByteLength = inp.BaseStream.Position - lastPosition; inp.BaseStream.Position = lastPosition; byte[] remainingBytes = new byte[remainingByteLength]; inp.BaseStream.Read(remainingBytes, 0, (int)remainingByteLength); return(string.Join(" ", remainingBytes.Select(e => e.ToString("x2")))); } if (WorkAround.Make(currentOpcodeOffset, currentFileName, inp, jumpTable, pout)) { continue; } else { pout.Write("{0:x2}", opcode); } if (commandMode) { commandMode = false; Opcode op = Opcode.Get(opcode); if (op == null) { if (unsupportedOpCodes.Add(opcode)) { Log.Write(string.Format(" [Unknown] Unsupported opcode: {0:x2} ({1}) at {2:x8}", opcode, opcode, currentOpcodeOffset)); } pout.Write(": unsupported {0} ", opcode); } else { string result = null; try { result = DecodeOp(op); var remaining = GetRemainingByteCodes(); pout.Write(remaining.Length == 0 ? remaining : (" " + remaining)); pout.Write(": "); if (result != null) { pout.Write(result); } } catch (Exception re) { Log.Write($" [Error] Exception while decoding opcode ({op}) {dstF.FullName}:{inp.BaseStream.Position}"); Log.Write(re.ToString()); } } } else if (opcode == 0x10) { pout.Write(": "); commandMode = true; } else { Opcode op = Opcode.Get(opcode); if (op == Opcode.end || op == Opcode.waitForSFX || op == Opcode.gotoif || op == Opcode._switch || op == Opcode.varop || op == Opcode.text || Opcode.rest.ContainsKey(opcode)) { string result = null; try { if (Opcode.rest.ContainsKey(opcode)) { result = DecodeOpRest(opcode); } else { result = DecodeOp(op); } var remaining = GetRemainingByteCodes(); pout.Write(remaining.Length == 0 ? remaining : (" " + remaining)); pout.Write(": "); if (result != null) { pout.Write(result); } } catch (Exception re) { Log.Write(" [Error] Exception while decoding opcode (" + $"{(op == null ? opcode.ToString() : op.ToString())}) " + pos); Log.Write(re.ToString()); } } else { pout.Write(": unsupported"); } } pout.WriteLine(); } if (textCoveragePool.Count < textTable.Length) { Log.Write(" [Warn] Text coverage not covered all texts! (" + textCoveragePool.Count + "/" + textTable.Length + ")"); } if (imageCoveragePool.Count < graphicsTable.Length) { Log.Write(" [Warn] Graphics coverage not covered all images! (" + imageCoveragePool.Count + "/" + graphicsTable.Length + ")"); var unusedImages = new List <string>(); for (var i = 0; i < graphicsTable.Length; i++) { if (!imageCoveragePool.Contains(i)) { unusedImages.Add(ReadText(input, graphicsTable[i])); } } Log.Write(" [Warn] Unused images: " + string.Join(" ", unusedImages)); } }
public static string ReadExpr(BinaryReader buf, long lastPosition = -1) { var _position = ""; if (lastPosition > 0) { _position = string.Format(" at {0:x8}", lastPosition); } int arg0 = buf.ReadByte() & 0xFF; if (arg0 == 0xC0) { int mode = arg0; int val = buf.ReadByte() & 0xFF; return(string.Format("VAR_{0:x2}_{1}", mode, val)); } else if (arg0 >= 0xC1 && arg0 <= 0xCF) { int mode = arg0; int a = buf.ReadInt16() & 0xFFFF; int b = buf.ReadInt16() & 0xFFFF; return(string.Format("VAR_{0:x2}_{1}_{2}", mode, a, b)); } else if (arg0 >= 0xA0 && arg0 <= 0xAF) { int m = arg0; int a = buf.ReadByte() & 0xFF; int b = buf.ReadByte() & 0xFF; int nil = buf.ReadByte(); if (nil != 0) { Log.Write(" [Unknown] I thought exprs ended with 0x00, but got: " + nil + _position); } if (b == 0 && nil == 0) { return((256 * (m - 0xA0) + a).ToString()); } else if (m == 0xA4) { return(KIDUtil.GetVarname(string.Format("{0:x2}", a))); } return(string.Format("CONST_{0:x2}_{1:x2}_{2:x2}", m, a, b)); } else if (arg0 >= 0xB0 && arg0 <= 0xBF) { int m = arg0; int a = buf.ReadByte() & 0xFF; int b = buf.ReadByte() & 0xFF; int nil = buf.ReadByte(); if (nil != 0) { Log.Write(" [Unknown] I thought negative exprs ended with 0x00, but got: " + nil + _position); } if (b == 0 && nil == 0) { return((256 * (m - 0xBF) + (a - 0x100)).ToString()); } else if (m == 0xB4) { return(KIDUtil.GetVarname(string.Format("{0:x2}", a))); } return(string.Format("CONST_{0:x2}_{1:x2}_{2:x2}", m, a, b)); } else if (arg0 >= 0x80 && arg0 <= 0x8F) { int a = arg0 - 0x80; //Constant int b = buf.ReadByte() & 0xFF; if (b != 0) { Log.Write(string.Format( " [Unknown] I don't really know what to do with 2-byte constants: {0:x2} {1:x2}{2}", arg0, b, _position)); } int nil = buf.ReadByte(); if (nil != 0) { Log.Write(" [Unknown] I thought exprs ended with 0x00, but got: " + nil + _position); } return("" + a); } else if (arg0 == 0x28) { int arg1 = buf.ReadByte() & 0xFF; if (arg1 != 0x0a) { Log.Write(" [Unknown] I thought 0x28 is always followed by 0x0a, but got: " + arg1 + _position); } return(ReadExpr(buf)); } else if (arg0 == 0xe0) { int rColor = buf.ReadByte() & 0xFF; int gColor = buf.ReadByte() & 0xFF; int bColor = buf.ReadByte() & 0xFF; int null1 = buf.ReadByte(); int null2 = buf.ReadByte(); int null3 = buf.ReadByte(); if (null1 != 0 || null2 != 0 || null3 != 0) { Log.Write(" [Unknown] I thought 0xe0 color code is always ended by three 0x00, at " + _position); } return(string.Format($"rgb({rColor},{gColor},{bColor})")); } else { if (!WorkAround.CanSuppressVaropError(arg0)) { Log.Write(" [Unknown] What kind of expr is this? " + arg0 + _position); } return("" + arg0); } }