public static CodeInfo DisassembleCode(GMFileContent content, uint id) { if (content.General->BytecodeVersion > 0xE) throw new InvalidDataException("Cannot disassemble bytecode with version >0xE."); if (id >= content.Code->Count) throw new ArgumentOutOfRangeException(nameof(id)); var re = (CodeEntry*)GMFile.PtrFromOffset(content, (&content.Code->Offsets)[id]); var len = re->Length; var bc = &re->Bytecode; var ret = new List<IntPtr>(); // doesn't like T* as type arg var l = Utils.PadTo(len, 4); AnyInstruction* instr; for (uint i = 0; i * 4 < l; /* see loop end */) { instr = (AnyInstruction*)(bc + i); ret.Add((IntPtr)instr); i += DisasmExt.Size(instr); } return new CodeInfo { Name = SectionReader.StringFromOffset(content, re->Name), Instructions = Utils.MPtrListToPtrArr(ret) }; }
internal GMFile(GMFileContent f) { Content = f; General = SectionReader.GetGeneralInfo(f); Options = SectionReader.GetOptionInfo(f); Sound = Utils.UintRange(0, f.Sounds->Count).Select(i => SectionReader.GetSoundInfo(f, i)).ToArray(); Sprites = Utils.UintRange(0, f.Sprites->Count).Select(i => SectionReader.GetSpriteInfo(f, i)).ToArray(); Backgrounds = Utils.UintRange(0, f.Backgrounds->Count).Select(i => SectionReader.GetBgInfo(f, i)).ToArray(); Paths = Utils.UintRange(0, f.Paths->Count).Select(i => SectionReader.GetPathInfo(f, i)).ToArray(); Scripts = Utils.UintRange(0, f.Scripts->Count).Select(i => SectionReader.GetScriptInfo(f, i)).ToArray(); Fonts = Utils.UintRange(0, f.Fonts->Count).Select(i => SectionReader.GetFontInfo(f, i)).ToArray(); Objects = Utils.UintRange(0, f.Objects->Count).Select(i => SectionReader.GetObjectInfo(f, i)).ToArray(); Rooms = Utils.UintRange(0, f.Rooms->Count).Select(i => SectionReader.GetRoomInfo(f, i)).ToArray(); TexturePages = Utils.UintRange(0, f.TexturePages->Count).Select(i => SectionReader.GetTexPageInfo(f, i)).ToArray(); Code = Utils.UintRange(0, f.Code->Count).Select(i => Disassembler.DisassembleCode(f, i)).ToArray(); Strings = Utils.UintRange(0, f.Strings->Count).Select(i => SectionReader.GetStringInfo(f, i)).ToArray(); Textures = Utils.UintRange(0, f.Textures->Count).Select(i => SectionReader.GetTextureInfo(f, i)).ToArray(); Audio = Utils.UintRange(0, f.Audio->Count).Select(i => SectionReader.GetAudioInfo(f, i)).ToArray(); AudioSoundMap = new Dictionary <uint, uint>(); for (uint i = 0; i < Sound.Length; i++) { var s = Sound[i]; if ((s.IsEmbedded || s.IsCompressed) && s.AudioId != -1) { AudioSoundMap[(uint)s.AudioId] = i; } } var vars = General.IsOldBCVersion ? SectionReader.GetRefDefs(f, f.Variables) : SectionReader.GetRefDefsWithOthers(f, f.Variables); var fns = General.IsOldBCVersion ? SectionReader.GetRefDefs(f, f.Functions) : SectionReader.GetRefDefsWithLength(f, f.Functions); RefData = new RefData { Variables = vars, Functions = fns, VarAccessors = Disassembler.GetReferenceTable(f, vars), FuncAccessors = Disassembler.GetReferenceTable(f, fns) }; }
public static SectionHeaders ChunkOf(GMFileContent file, long offset) { var sorted = file.HeaderOffsets.OrderBy(i => i).ToArray(); if (sorted.Length == 1) { return(*(SectionHeaders *)PtrFromOffset(file, sorted[0])); } for (int i = 0; i < sorted.Length - 1 && sorted[i + 1] != 0; i++) { if (offset == sorted[i] || offset > sorted[i] && (offset < sorted[i + 1] || sorted[i + 1] == 0)) { return(*(SectionHeaders *)PtrFromOffset(file, sorted[i])); } } return(SectionHeaders.Form); }
// used to prove the chunk order doesn't matter unsafe static void SwapChunks(GMFileContent f) { int EmptyChunkSize = sizeof(SectionUnknown); var exntO = (long)f.Extensions - (long)f.RawData.BPtr; var tmlnO = (long)f.Timelines - (long)f.RawData.BPtr; byte[] extnT = new byte[EmptyChunkSize]; ILHacks.Cpblk((IntPtr)f.Extensions, extnT, 0, EmptyChunkSize); ILHacks.Cpblk((IntPtr)f.Timelines, (IntPtr)f.Extensions, EmptyChunkSize); ILHacks.Cpblk(extnT, (IntPtr)f.Timelines, 0, EmptyChunkSize); byte[] EVERYTHING = new byte[f.RawData.Size]; ILHacks.Cpblk(f.RawData.IPtr, EVERYTHING, 0, f.RawData.Size); File.WriteAllBytes("data.fake.win", EVERYTHING); }
public static Dictionary<IntPtr, int> GetReferenceTable(GMFileContent content, ReferenceDef[] defs) { var ret = new Dictionary<IntPtr, int>(defs.Length); for (int i = 0; i < defs.Length; i++) { var offTotal = (long)defs[i].FirstOffset; var addr = (AnyInstruction*)GMFile.PtrFromOffset(content, offTotal); for (int j = 0; j < defs[i].Occurrences /*&& curOffset != 0*/; j++) { ret.Add((IntPtr)addr, i); if (j < defs[i].Occurrences - 1) // at least one more iteration afterwards { var off = ((uint*)addr)[1] & 0x00FFFFFFL; addr = (AnyInstruction*)GMFile.PtrFromOffset(content, offTotal += off); //! '+=', not '+' } } } return ret; }
public static Statement[] ParseStatements(GMFileContent content, RefData rdata, CodeInfo code, AnyInstruction*[] instr = null, Stack<Expression> stack = null, List<Expression> dupTars = null) { //! here be dragons stack = stack ?? new Stack<Expression>(); dupTars = dupTars ?? new List <Expression>(); instr = instr ?? code.Instructions; if (instr.Length == 0) return EmptyStmtArray; var stmts = new List<Statement>(); var firstI = code.Instructions[0]; //TODO: use locals Func<Expression> Pop = () => stack.Count == 0 ? PopExpr : stack.Pop (); //Func<Expression> Peek = () => stack.Count == 0 ? PopExpr : stack.Peek(); Func<int, IEnumerable<Expression>> PopMany = i => { var ret = new List<Expression>(); for (int j = 0; j < i; j++) ret.Add(Pop()); return ret; }; #region Action FlushStack = () => { }; Action FlushStack = () => { var readd = new Stack<Expression>(); //? not sure if this is a good idea (random 'push'es in the wild) (see TODO) stmts.AddRange(stack.PopAll().Where(e => { if (dupTars.Contains(e)) { readd.Push(e); return false; } return !(e is PopExpression); // 'push pop' is obviously stupid to emit }).Reverse().Select(e => e is UnaryOperatorExpression && ((UnaryOperatorExpression)e).Operator == UnaryOperator.Duplicate ? (Statement)new DupStatement() : new PushStatement { Expr = e })); stack.PushRange(readd); }; #endregion Action<Statement> AddStmt = s => { FlushStack(); stmts.Add(s); }; Func<VariableType, Expression[]> TryGetIndices = vt => { Expression index = null; var dimentions = 0; if (vt == VariableType.Array) { index = Pop(); var arrInd = Pop(); if ((arrInd is LiteralExpression) && ((LiteralExpression)arrInd).Value is short) { var s = (short)((LiteralExpression)arrInd).Value; switch (s) { case -1: dimentions = 2; break; case -5: dimentions = 1; break; } } if (dimentions == 0) { stack.Push(arrInd); stack.Push(index); index = null; } } if (index == null) return null; // analyse index for specified dimention switch (dimentions) { case 2: if (index is BinaryOperatorExpression && ((BinaryOperatorExpression)index).Operator == BinaryOperator.Addition) { var boe = (BinaryOperatorExpression)index; var a = boe.Arg1; var b = boe.Arg2; if (a is BinaryOperatorExpression && ((BinaryOperatorExpression)a).Operator == BinaryOperator.Multiplication) { var a_ = (BinaryOperatorExpression)a; var c = a_.Arg2; if (c is LiteralExpression && ((LiteralExpression)c).ReturnType == DataType.Int32 && (int /* should be */)((LiteralExpression)c).Value == 32000) return new[] { a_.Arg1, b }; } } break; } return new[] { index }; }; for (int i = 0; i < instr.Length; i++) { var ins = instr[i]; #region stuff var pst = (SingleTypeInstruction*)ins; var pdt = (DoubleTypeInstruction*)ins; var pcl = (CallInstruction *)ins; var pps = (PushInstruction *)ins; var pse = (SetInstruction *)ins; var pbr = (GotoInstruction *)ins; var pbk = (BreakInstruction *)ins; var st = ins->SingleType; var dt = ins->DoubleType; var cl = ins->Call ; var ps = ins->Push ; var se = ins->Set ; var t1 = ins->Kind() == InstructionKind.SingleType ? st.Type : (ins->Kind() == InstructionKind.DoubleType ? dt.Types.Type1 : 0); var t2 = ins->Kind() == InstructionKind.DoubleType ? dt.Types.Type2 : (ins->Kind() == InstructionKind.SingleType ? st.Type : 0); #endregion switch (ins->Code()) { #region dup, pop case OpCode.Dup: var normal = true; if (i < instr.Length - 1 && instr[i + 1]->OpCode == OpCode.Push) { var n = &instr[i + 1]->Push; var t = ((Reference*)&n->ValueRest)->Type; if (t == VariableType.Array && stack.Count > 1) { normal = false; stack.Push(stack.Skip(1).First()); // second item stack.Push(stack.Skip(1).First()); // first item (original stack top) } } if (!normal) break; if (!dupTars.Contains(stack.Peek())) dupTars.Add(stack.Peek()); if (stack.Peek().WalkExprTree(e => e is CallExpression).Any(_ => _)) { stack.Push(new UnaryOperatorExpression { Input = stack.Peek(), Operator = UnaryOperator.Duplicate, OriginalType = stack.Peek().ReturnType, ReturnType = st.Type }); //AddStmt(new DupStatement()); } else stack.Push(stack.Peek()); break; case OpCode.Pop: if (stack.Count > 0 && stack.Peek() is CallExpression) AddStmt(new CallStatement { Call = stack.Pop() as CallExpression }); else AddStmt(new PopStatement()); break; #endregion #region env //TODO: use actual '(with obj ...)' syntax //! it might mess with the CFG structure case OpCode.PushEnv: AddStmt(new PushEnvStatement { Target = (AnyInstruction*)((byte*)ins + pbr->Offset * 4L), TargetOffset = (byte*)ins + pbr->Offset * 4L - (byte*)firstI, Parent = stack.Pop() }); break; case OpCode.PopEnv : AddStmt(new PopEnvStatement { Target = (AnyInstruction*)((byte*)ins + pbr->Offset * 4L), TargetOffset = (byte*)ins + pbr->Offset * 4L - (byte*)firstI }); break; #endregion #region branch case OpCode.Brt: case OpCode.Brf: case OpCode.Br: AddStmt(new BranchStatement { Type = pbr->Type(), Conditional = pbr->Type() == BranchType.Unconditional ? null : Pop(), Target = (AnyInstruction*)((byte*)ins + pbr->Offset * 4L), TargetOffset = (byte*)ins + pbr->Offset * 4L - (byte*)firstI }); break; #endregion #region break, ret, exit case OpCode.Break: stack.Push(new AssertExpression { ControlValue = pbk->Signal, ReturnType = pbk->Type, Expr = Pop() }); break; case OpCode.Ret: AddStmt(new ReturnStatement { ReturnType = pst->Type, RetValue = Pop() }); break; case OpCode.Exit: AddStmt(new ExitStatement()); break; #endregion #region set case OpCode.Set: var ind = TryGetIndices(se.DestVar.Type); // call before Value's pop AddStmt(new SetStatement { OriginalType = se.Types.Type1, ReturnType = se.Types.Type2, Type = se.DestVar.Type, OwnerType = se.Instance, OwnerName = se.Instance > InstanceType.StackTopOrGlobal ? SectionReader.GetObjectInfo(content, (uint)se.Instance).Name : null, Target = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]], Value = Pop(), ArrayIndices = ind ?? TryGetIndices(se.DestVar.Type) }); break; #endregion default: switch (ins->ExprType()) { #region variable case ExpressionType.Variable: var vt = ((Reference*)&pps->ValueRest)->Type; if (vt == VariableType.StackTop && (InstanceType)ps.Value == InstanceType.StackTopOrGlobal) { stack.Push(new MemberExpression { Owner = Pop(), ReturnType = ps.Type, Type = vt, OwnerType = (InstanceType)ps.Value, OwnerName = se.Instance > InstanceType.StackTopOrGlobal ? SectionReader.GetObjectInfo(content, (uint)se.Instance).Name : null, Variable = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]], ArrayIndices = TryGetIndices(vt) }); } else stack.Push(new VariableExpression { ReturnType = ps.Type, Type = vt, OwnerType = (InstanceType)ps.Value, Variable = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]], ArrayIndices = TryGetIndices(vt) }); break; #endregion #region literal case ExpressionType.Literal: object v = null; var rest = &pps->ValueRest; #region get value switch (ps.Type) { case DataType.Int16: v = ps.Value; break; case DataType.Boolean: v = ((DwordBool*)rest)->IsTrue(); break; case DataType.Double: v = *(double*)rest; break; case DataType.Single: v = *(float*)rest; break; case DataType.Int32: v = *(int*)rest; break; case DataType.Int64: v = *(long*)rest; break; case DataType.String: v = SectionReader.GetStringInfo(content, ps.ValueRest); break; } #endregion stack.Push(new LiteralExpression { ReturnType = ps.Type, Value = v }); break; #endregion #region call case ExpressionType.Call: stack.Push(new CallExpression { ReturnType = cl.ReturnType, Type = cl.Function.Type, Function = rdata.Functions[rdata.FuncAccessors[(IntPtr)ins]], Arguments = PopMany(cl.Arguments).Reverse().ToArray() }); break; #endregion #region binaryop case ExpressionType.BinaryOp: var a1 = Pop(); var a2 = Pop(); stack.Push(new BinaryOperatorExpression { OriginalType = t1, ReturnType = t2, Arg1 = a2, Arg2 = a1, Operator = ins->BinaryOp() }); break; #endregion #region unaryop case ExpressionType.UnaryOp: stack.Push(new UnaryOperatorExpression { OriginalType = t1, ReturnType = t2, Input = Pop(), Operator = ins->UnaryOp() }); break; #endregion } break; } } FlushStack(); return stmts.ToArray(); }
public static GraphVertex[] BuildCFGraph(GMFileContent content, CodeInfo code) { if (code.Instructions.Length == 0) return EmptyGVArray; return CreateVertices(content, code); }
static GraphVertex[] CreateVertices(GMFileContent content, CodeInfo code) { var blocks = SplitBlocks(code); var instr = code.Instructions; var firstI = (long)instr[0]; // only one block -> just return it as a single vertex if (blocks.Length == 1) return new[] { new GraphVertex { Branches = EmptyGBArray, Instructions = blocks[0].Instructions } }; var vertices = new GraphVertex[blocks.Length]; // create list of vertices for (int i = 0; i < blocks.Length; i++) { var blk = blocks[i]; var hasNext = i < blocks.Length - 1 && blk.Type != BranchType.Unconditional /* no need to check if uncond */; vertices[i] = new GraphVertex { Instructions = blk.Instructions, Branches = new GraphBranch[hasNext ? 2 : 1] }; vertices[i].Branches[0] = new GraphBranch { BranchTo = blk.BranchTo, Type = blk.Type }; if (hasNext) vertices[i].Branches[1] = new GraphBranch { BranchTo = blocks[i + 1].Instructions[0], Type = blk.Type.Invert() }; } // connect vertex branches to target vertices for (int i = 0; i < vertices.Length; i++) { var v = vertices[i]; for (int j = 0; j < v.Branches.Length; j++) v.Branches[j].ToVertex = vertices.FirstOrDefault(ve => ve.Instructions[0] == v.Branches[j].BranchTo) ?? (i == vertices.Length - 1 ? null : vertices[i + 1]); } return vertices; }
public static void *PtrFromOffset(GMFileContent file, long offset) => file.RawData.BPtr + offset;
internal GMFile(GMFileContent f) { Content = f; var orderList = new List <SectionHeaders>(f.HeaderOffsets.Length); foreach (long headerOffset in f.HeaderOffsets) { SectionHeaders tag = ((SectionHeader *)((byte *)f.Form + headerOffset))->Identity; if (tag != SectionHeaders.Form) { orderList.Add(tag); } } ChunkOrder = orderList.ToArray(); if (f.General != null) { General = SectionReader.GetGeneralInfo(f); } if (f.Options != null) { Options = SectionReader.GetOptionInfo(f); } if (f.Globals != null) { Globals = SectionReader.GetGlobalInfo(f); } Sound = MkLazyArr(f.Sounds, i => SectionReader.GetSoundInfo(f, i)); if (f.TexturePages != null) { var toil = SectionReader.BuildTPAGOffsetIndexLUT(f); Sprites = MkLazyArr(f.Sprites, i => SectionReader.GetSpriteInfo(f, i, toil)); } Backgrounds = MkLazyArr(f.Backgrounds, i => SectionReader.GetBgInfo(f, i)); Paths = MkLazyArr(f.Paths, i => SectionReader.GetPathInfo(f, i)); Scripts = MkLazyArr(f.Scripts, i => SectionReader.GetScriptInfo(f, i)); Fonts = MkLazyArr(f.Fonts, i => SectionReader.GetFontInfo(f, i)); Objects = MkLazyArr(f.Objects, i => SectionReader.GetObjectInfo(f, i)); Rooms = MkLazyArr(f.Rooms, i => SectionReader.GetRoomInfo(f, i)); TexturePages = MkLazyArr(f.TexturePages, i => SectionReader.GetTexPageInfo(f, i)); Code = MkLazyArr(f.Code, i => Disassembler.DisassembleCode(f, i)); Strings = MkLazyArr(f.Strings, i => SectionReader.GetStringInfo(f, i)); Textures = MkLazyArr(f.Textures, i => SectionReader.GetTextureInfo(f, i)); Audio = MkLazyArr(f.Audio, i => SectionReader.GetAudioInfo(f, i)); AudioGroups = MkLazyArr(f.AudioGroup, i => SectionReader.GetAudioGroupInfo(f, i)); Extensions = MkLazyArr(f.Extensions, i => SectionReader.GetExtensionInfo(f, i)); Shaders = MkLazyArr(f.Shaders, i => SectionReader.GetShaderInfo(f, i)); Timelines = MkLazyArr(f.Timelines, i => SectionReader.GetTimelineInfo(f, i)); AudioSoundMap = new Dictionary <uint, uint>(); if (f.Sounds != null) { for (uint i = 0; i < Sound.Length; i++) { var s = Sound[i]; if ((s.IsEmbedded || s.IsCompressed) && s.AudioID != -1 && s.GroupID == 0) { AudioSoundMap[(uint)s.AudioID] = i; } } } if (f.General == null) { return; } try { // TODO: do this in a better way var vars = General.IsOldBCVersion ? SectionReader.GetRefDefs(f, f.Variables) : SectionReader.GetRefDefsWithOthers(f, f.Variables); var fns = General.IsOldBCVersion ? SectionReader.GetRefDefs(f, f.Functions) : SectionReader.GetRefDefsWithLength(f, f.Functions); var varacc = Disassembler.GetReferenceTable(f, vars); var fnacc = Disassembler.GetReferenceTable(f, fns); RefData = new RefData { Variables = vars, Functions = fns, VarAccessors = varacc, FuncAccessors = fnacc }; if (f.Functions->Entries.NameOffset * 12 < f.Functions->Header.Size) { FunctionLocals = SectionReader.GetFunctionLocals(f, f.Functions); } if (f.Variables != null && !General.IsOldBCVersion) { VariableExtra = new uint[] { ((uint *)&f.Variables->Entries)[0], ((uint *)&f.Variables->Entries)[1], ((uint *)&f.Variables->Entries)[2] } } ; } catch (Exception e) { Console.Error.WriteLine("Warning: Can't figure out RefDef pairs. Exception:"); Console.Error.WriteLine(e); } }
public static unsafe SectionHeaders ChunkOf(GMFileContent file, long offset) { var sorted = file.HeaderOffsets.OrderBy(i => i).ToArray(); if (sorted.Length == 1) return *(SectionHeaders*)PtrFromOffset(file, sorted[0]); for (int i = 0; i < sorted.Length - 1 && sorted[i + 1] != 0; i++) if (offset == sorted[i] || offset > sorted[i] && (offset < sorted[i + 1] || sorted[i + 1] == 0)) return *(SectionHeaders*)PtrFromOffset(file, sorted[i]); return SectionHeaders.Form; }
public static GMFile GetFile(byte[] data) { var ret = new GMFileContent(); var hdr_bp = new UniquePtr(data); byte *hdr_b = hdr_bp.BPtr; var basePtr = (SectionHeader *)hdr_b; ret.Form = basePtr; if (ret.Form->Identity != SectionHeaders.Form) { throw new InvalidDataException(ERR_NO_FORM); } SectionHeader * hdr = basePtr + 1, hdrEnd = (SectionHeader *)((IntPtr)basePtr + (int)ret.Form->Size); int headersMet = 0; while (hdr < hdrEnd) { switch (hdr->Identity) { case SectionHeaders.General: ret.General = (SectionGeneral *)hdr; break; case SectionHeaders.Options: ret.Options = (SectionOptions *)hdr; break; case SectionHeaders.Extensions: ret.Extensions = (SectionUnknown *)hdr; break; case SectionHeaders.Sounds: ret.Sounds = (SectionCountOffsets *)hdr; break; case SectionHeaders.Sprites: ret.Sprites = (SectionCountOffsets *)hdr; break; case SectionHeaders.Backgrounds: ret.Backgrounds = (SectionCountOffsets *)hdr; break; case SectionHeaders.Paths: ret.Paths = (SectionCountOffsets *)hdr; break; case SectionHeaders.Scripts: ret.Scripts = (SectionCountOffsets *)hdr; break; case SectionHeaders.Shaders: ret.Shaders = (SectionUnknown *)hdr; break; case SectionHeaders.Fonts: ret.Fonts = (SectionCountOffsets *)hdr; break; case SectionHeaders.Timelines: ret.Timelines = (SectionUnknown *)hdr; break; case SectionHeaders.Objects: ret.Objects = (SectionCountOffsets *)hdr; break; case SectionHeaders.Rooms: ret.Rooms = (SectionCountOffsets *)hdr; break; case SectionHeaders.DataFiles: ret.DataFiles = (SectionUnknown *)hdr; break; case SectionHeaders.TexturePage: ret.TexturePages = (SectionCountOffsets *)hdr; break; case SectionHeaders.Code: ret.Code = (SectionCountOffsets *)hdr; break; case SectionHeaders.Variables: ret.Variables = (SectionRefDefs *)hdr; break; case SectionHeaders.Functions: ret.Functions = (SectionRefDefs *)hdr; break; case SectionHeaders.Strings: ret.Strings = (SectionCountOffsets *)hdr; break; case SectionHeaders.Textures: ret.Textures = (SectionCountOffsets *)hdr; break; case SectionHeaders.Audio: ret.Audio = (SectionCountOffsets *)hdr; break; case SectionHeaders.AudioGroup: ret.AudioGroup = (SectionUnknown *)hdr; break; } ret.HeaderOffsets[headersMet++] = (byte *)hdr - (byte *)basePtr; hdr = unchecked ((SectionHeader *)((IntPtr)hdr + (int)hdr->Size) + 1); } ret.RawData = hdr_bp; return(new GMFile(ret)); }
public static GMFile GetFile(byte[] data) { var ret = new GMFileContent(); var hdr_bp = new UniquePtr(data); byte *hdr_b = hdr_bp.BPtr; var basePtr = (SectionHeader *)hdr_b; ret.Form = basePtr; if (ret.Form->Identity != SectionHeaders.Form) { throw new InvalidDataException(ERR_NO_FORM); } SectionHeader * hdr = basePtr + 1, hdrEnd = (SectionHeader *)((IntPtr)basePtr + (int)ret.Form->Size); int headersMet = 0; while (hdr < hdrEnd) { switch (hdr->Identity) { case SectionHeaders.General: ret.General = (SectionGeneral *)hdr; break; case SectionHeaders.Options: ret.Options = (SectionOptions *)hdr; break; case SectionHeaders.Extensions: ret.Extensions = (SectionUnknown *)hdr; if (!ret.Extensions->IsEmpty()) { Console.WriteLine("Warning: EXTN chunk is not empty, its content will not be exported!"); } break; case SectionHeaders.Sounds: ret.Sounds = (SectionCountOffsets *)hdr; break; case SectionHeaders.Sprites: ret.Sprites = (SectionCountOffsets *)hdr; break; case SectionHeaders.Backgrounds: ret.Backgrounds = (SectionCountOffsets *)hdr; break; case SectionHeaders.Paths: ret.Paths = (SectionCountOffsets *)hdr; break; case SectionHeaders.Scripts: ret.Scripts = (SectionCountOffsets *)hdr; break; case SectionHeaders.Shaders: ret.Shaders = (SectionUnknown *)hdr; if (!ret.Shaders->IsEmpty()) { Console.WriteLine("Warning: SHDR chunk is not empty, its content will not be exported!"); } break; case SectionHeaders.Fonts: ret.Fonts = (SectionCountOffsets *)hdr; break; case SectionHeaders.Timelines: ret.Timelines = (SectionUnknown *)hdr; if (!ret.Timelines->IsEmpty()) { Console.WriteLine("Warning: TMLN chunk is not empty, its content will not be exported!"); } break; case SectionHeaders.Objects: ret.Objects = (SectionCountOffsets *)hdr; break; case SectionHeaders.Rooms: ret.Rooms = (SectionCountOffsets *)hdr; break; case SectionHeaders.DataFiles: ret.DataFiles = (SectionUnknown *)hdr; if (!ret.DataFiles->IsEmpty()) { Console.WriteLine("Warning: DAFL chunk is not empty, its content will not be exported!"); } break; case SectionHeaders.TexturePage: ret.TexturePages = (SectionCountOffsets *)hdr; break; case SectionHeaders.Code: ret.Code = (SectionCountOffsets *)hdr; break; case SectionHeaders.Variables: ret.Variables = (SectionRefDefs *)hdr; break; case SectionHeaders.Functions: ret.Functions = (SectionRefDefs *)hdr; break; case SectionHeaders.Strings: ret.Strings = (SectionCountOffsets *)hdr; break; case SectionHeaders.Textures: ret.Textures = (SectionCountOffsets *)hdr; break; case SectionHeaders.Audio: ret.Audio = (SectionCountOffsets *)hdr; break; case SectionHeaders.AudioGroup: ret.AudioGroup = (SectionUnknown *)hdr; if (!ret.AudioGroup->IsEmpty()) { Console.WriteLine("Warning: AGRP chunk is not empty, its content will not be exported!"); } break; case SectionHeaders.GNAL_Unk: ret.GNAL_Unk = (SectionUnknown *)hdr; if (!ret.GNAL_Unk->IsEmpty()) { Console.WriteLine("Warning: GNAL chunk is not empty, its content will not be exported!"); } break; default: var unk = (SectionUnknown *)hdr; if (!unk->IsEmpty()) { Console.WriteLine($"Warning: unknown chunk {hdr->Identity.ToChunkName()}, chunk is not empty, its content will not be exported!"); } ret.UnknownChunks.Add(hdr->Identity, (IntPtr)unk); break; } for (int i = 0; i < ret.HeaderOffsets.Length; i++) { if (((SectionHeader *)((byte *)basePtr + ret.HeaderOffsets[i]))->Identity == hdr->Identity) { Console.WriteLine($"WARNING: chunk {hdr->MagicString()} encountered (at least) twice! Only the last occurrence will be exported! (If you see this message, consider reversing manually.)"); } } if (ret.HeaderOffsets.Length >= headersMet) { var ho = ret.HeaderOffsets; Array.Resize(ref ho, (headersMet == ret.HeaderOffsets.Length) ? 1 : (headersMet + 2)); ret.HeaderOffsets = ho; } ret.HeaderOffsets[headersMet++] = (byte *)hdr - (byte *)basePtr; hdr = unchecked ((SectionHeader *)((IntPtr)hdr + (int)hdr->Size) + 1); } ret.RawData = hdr_bp; return(new GMFile(ret)); }
public unsafe static GMFileContent GetFile(byte[] data) { var ret = new GMFileContent(); var hdr_bp = new UniquePtr(data); byte* hdr_b = hdr_bp.BPtr; var basePtr = (SectionHeader*)hdr_b; ret.Form = basePtr; if (ret.Form->Identity != SectionHeaders.Form) throw new InvalidDataException(ERR_NO_FORM); SectionHeader* hdr = basePtr + 1, hdrEnd = (SectionHeader*)((IntPtr)basePtr + (int)ret.Form->Size); int headersMet = 0; while (hdr < hdrEnd) { switch (hdr->Identity) { case SectionHeaders.General: ret.General = (SectionGeneral*)hdr; break; case SectionHeaders.Options: ret.Options = (SectionOptions*)hdr; break; case SectionHeaders.Extensions: ret.Extensions = (SectionUnknown*)hdr; break; case SectionHeaders.Sounds: ret.Sounds = (SectionCountOffsets*)hdr; break; case SectionHeaders.Sprites: ret.Sprites = (SectionCountOffsets*)hdr; break; case SectionHeaders.Backgrounds: ret.Backgrounds = (SectionCountOffsets*)hdr; break; case SectionHeaders.Paths: ret.Paths = (SectionCountOffsets*)hdr; break; case SectionHeaders.Scripts: ret.Scripts = (SectionCountOffsets*)hdr; break; case SectionHeaders.Shaders: ret.Shaders = (SectionUnknown*)hdr; break; case SectionHeaders.Fonts: ret.Fonts = (SectionCountOffsets*)hdr; break; case SectionHeaders.Timelines: ret.Timelines = (SectionUnknown*)hdr; break; case SectionHeaders.Objects: ret.Objects = (SectionCountOffsets*)hdr; break; case SectionHeaders.Rooms: ret.Rooms = (SectionCountOffsets*)hdr; break; case SectionHeaders.DataFiles: ret.DataFiles = (SectionUnknown*)hdr; break; case SectionHeaders.TexturePage: ret.TexturePages = (SectionCountOffsets*)hdr; break; case SectionHeaders.Code: ret.Code = (SectionCountOffsets*)hdr; break; case SectionHeaders.Variables: ret.Variables = (SectionRefDefs*)hdr; break; case SectionHeaders.Functions: ret.Functions = (SectionRefDefs*)hdr; break; case SectionHeaders.Strings: ret.Strings = (SectionCountOffsets*)hdr; break; case SectionHeaders.Textures: ret.Textures = (SectionCountOffsets*)hdr; break; case SectionHeaders.Audio: ret.Audio = (SectionCountOffsets*)hdr; break; case SectionHeaders.AudioGroup: ret.AudioGroup = (SectionUnknown*)hdr; break; } ret.HeaderOffsets[headersMet++] = (byte*)hdr - (byte*)basePtr; hdr = unchecked((SectionHeader*)((IntPtr)hdr + (int)hdr->Size) + 1); } ret.RawData = hdr_bp; return ret; }
public static string DecompileCode(GMFileContent content, RefData rdata, CodeInfo code) { if (code.Instructions.Length == 0) return String.Empty; var sb = new StringBuilder(); var stack = new Stack<Expression>(); var dupts = new List <Expression>(); var firstI = (long)code.Instructions[0]; var graph = BuildCFGraph(content, code); //TODO: CFG kind recognition stuff (if, if/else, for, while, etc) var i = 0; foreach (var g in graph) { var stmts = ParseStatements(content, rdata, code, g.Instructions, stack); sb .Append(SR.HEX_PRE) .Append(((long)g.Instructions[0] - firstI).ToString(SR.HEX_FM6)) .AppendLine(SR.COLON); foreach (var s in stmts) sb.Append(SR.INDENT4).AppendLine(s.ToString()); i++; } sb.Append(SR.INDENT4).AppendLine(FinalRet.ToString()); return sb.ToString(); }
public static string DisplayInstructions(GMFileContent content, RefData rdata, CodeInfo code, AnyInstruction*[] instructions = null) { var instrs = instructions ?? code.Instructions; if (instrs.Length == 0) return String.Empty; var sb = new StringBuilder(); var firstI = code.Instructions[0]; for (int i = 0; i < instrs.Length; i++) { var iptr = instrs[i]; var relInstr = (long)iptr - (long)firstI; sb .Append(HEX_PRE).Append(relInstr.ToString(HEX_FM6)) .Append(' ').Append(iptr->Code().ToPrettyString()).Append(' '); switch (iptr->Kind()) { case InstructionKind.SingleType: var st = iptr->SingleType; sb.Append(st.Type.ToPrettyString()); break; case InstructionKind.DoubleType: var dt = iptr->DoubleType; sb.Append(dt.Types); break; case InstructionKind.Goto: var g = iptr->Goto; sb.Append(HEX_PRE).Append((relInstr + g.Offset * 4L).ToString(HEX_FM6)); break; #region set case InstructionKind.Set: var s = iptr->Set; sb.Append(s.Types).Append(' '); if (s.Instance <= InstanceType.StackTopOrGlobal) sb.Append(s.Instance.ToPrettyString()); else { var o = SectionReader.GetObjectInfo(content, (uint)s.Instance); sb.Append('[').Append(o.Name).Append(']'); } sb.Append(':'); sb.Append(rdata.Variables[rdata.VarAccessors[(IntPtr)iptr]].Name); sb.Append(s.DestVar.Type.ToPrettyString()); break; #endregion #region push case InstructionKind.Push: var pp = (PushInstruction*)iptr; var p = iptr->Push; sb.Append(p.Type.ToPrettyString()).Append(' '); var r = p.ValueRest; switch (p.Type) { case DataType.Int16: sb.Append(p.Value.ToString(CultureInfo.InvariantCulture)); break; case DataType.Variable: var rv = *(Reference*)&r; var inst = (InstanceType)p.Value; if (inst <= InstanceType.StackTopOrGlobal) sb.Append(inst.ToPrettyString()); else { var o = SectionReader.GetObjectInfo(content, (uint)inst); sb.Append('[').Append(o.Name).Append(']'); } sb.Append(':'); sb.Append(rdata.Variables[rdata.VarAccessors[(IntPtr)iptr]].Name); sb.Append(rv.Type.ToPrettyString()); break; case DataType.Boolean: sb.Append(((DwordBool*)&r)->ToPrettyString()); break; case DataType.Double: sb.Append(((double*)&r)->ToString(CultureInfo.InvariantCulture)); break; case DataType.Single: sb.Append(((float*)&r)->ToString(CultureInfo.InvariantCulture)); break; case DataType.Int32: sb.Append(unchecked((int)r).ToString(CultureInfo.InvariantCulture)); break; case DataType.Int64: sb.Append(((long*)&pp->ValueRest)->ToString(CultureInfo.InvariantCulture)); break; case DataType.String: sb.Append(SectionReader.GetStringInfo(content, p.ValueRest).Escape()); break; } break; #endregion #region call case InstructionKind.Call: var c = iptr->Call; sb.Append(c.ReturnType.ToPrettyString()).Append(':') .Append(c.Arguments).Append(' '); sb.Append(rdata.Functions[rdata.FuncAccessors[(IntPtr)iptr]].Name); sb.Append(c.Function.Type.ToPrettyString()); break; #endregion case InstructionKind.Break: var b = iptr->Break; sb.Append(b.Type.ToPrettyString()).Append(' ').Append(b.Signal); break; } sb.AppendLine(); } return sb.ToString(); }
public static unsafe void* PtrFromOffset(GMFileContent file, long offset) => file.RawData.BPtr + offset;