public static ScriptInfo GetScriptInfo(GMFileContent content, uint id) { if (id >= content.Scripts->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var se = (ScriptEntry *)GMFile.PtrFromOffset(content, (&content.Scripts->Offsets)[id]); var ret = new ScriptInfo(); ret.Name = StringFromOffset(content, se->Name); ret.CodeId = se->CodeId; return(ret); }
static RoomView ReadRoomView(GMFileContent content, IntPtr p) { var entry = (RoomViewEntry *)p; var v = new RoomView(); v.IsEnabled = entry->IsEnabled.IsTrue(); v.Port = entry->Port; v.View = entry->View; v.Border = entry->Border; v.Speed = entry->Speed; v.ObjectId = entry->ObjectId == 0xFFFFFFFF ? null : (uint?)entry->ObjectId; return(v); }
public static OptionInfo GetOptionInfo(GMFileContent content) { var ret = new OptionInfo(); var oe = content.Options; var h = GMFile.ChunkOf(content, oe->IconOffset); ret.Constants = new Dictionary <string, string>((int)oe->ConstMap.Count); for (uint i = 0; i < oe->ConstMap.Count; i++) { ret.Constants.Add(StringFromOffset(content, (&oe->ConstMap.Offsets)[i * 2]), StringFromOffset(content, (&oe->ConstMap.Offsets)[i * 2 + 1])); } return(ret); }
public static FontInfo GetFontInfo(GMFileContent content, uint id) { if (id >= content.Fonts->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var fe = (FontEntry *)GMFile.PtrFromOffset(content, (&content.Fonts->Offsets)[id]); var ret = new FontInfo(); var tpag = TPagFromOffset(content, fe->TPagOffset); ret.CodeName = StringFromOffset(content, fe->CodeName); ret.SystemName = StringFromOffset(content, fe->SystemName); ret.EmSize = fe->EmSize; ret.IsBold = fe->Bold.IsTrue(); ret.IsItalic = fe->Italic.IsTrue(); ret.Charset = fe->Charset; ret.AntiAliasing = fe->AntiAliasing; ret.Scale = fe->Scale; for (uint i = 0; i < content.TexturePages->Count; i++) { if (fe->TPagOffset == (&content.TexturePages->Offsets)[i]) { ret.TexPagId = i; break; } } ret.Characters = ReadList(content, &fe->Chars, (_, p) => { var entry = (FontCharEntry *)p; var c = new FontCharacter(); c.Character = entry->Character; c.TPagFrame = entry->TexPagFrame; c.Shift = entry->Shift; c.Offset = entry->Offset; return(c); }); return(ret); }
static RoomObject ReadRoomObj(GMFileContent content, IntPtr p) { var entry = (RoomObjEntry *)p; var o = new RoomObject(); o.DefIndex = entry->DefIndex; o.Position = entry->Position; o.Scale = entry->Scale; o.Colour = entry->Colour; o.Rotation = entry->Rotation; o.InstanceID = entry->InstanceID; o.CreateCodeID = entry->CreateCodeID; return(o); }
public static AudioInfo GetAudioInfo(GMFileContent content, uint id) { if (id >= content.Audio->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var au = (AudioEntry *)GMFile.PtrFromOffset(content, (&content.Audio->Offsets)[id]); var ret = new AudioInfo(); ret.Wave = new byte[au->Length]; Marshal.Copy((IntPtr)(&au->Data), ret.Wave, 0, ret.Wave.Length); return(ret); }
public static ReferenceDef[] GetRefDefsWithOthers(GMFileContent content, SectionRefDefs *section) { return(GetRefDefsInternal(content, section, 3, 0, 20 /* sizeof RefDefEntryWithOthers */, p => { var rde = (RefDefEntryWithOthers *)p; // they really are doing a Redigit here (well, this AND the DwordBools (instead of 1-byte bools or bit flags)) var ret = new ReferenceDef(); ret.Name = StringFromOffset(content, rde->NameOffset); ret.Occurrences = rde->Occurrences; ret.FirstOffset = rde->FirstAddress; ret.HasExtra = true; ret.InstanceType = (Decomp.InstanceType)rde->InstanceType; ret.unknown2 = rde->_pad1; ret.VariableType = Decomp.VariableType.Normal; return ret; })); }
static RoomBackground ReadRoomBg(GMFileContent content, IntPtr p) { var entry = (RoomBgEntry *)p; var b = new RoomBackground(); b.IsEnabled = entry->IsEnabled.IsTrue(); b.IsForeground = entry->IsForeground.IsTrue(); b.Position = entry->Position; b.TileX = entry->TileX.IsTrue(); b.TileY = entry->TileY.IsTrue(); b.Speed = entry->Speed; b.StretchSprite = entry->Stretch.IsTrue(); b.BgIndex = entry->DefIndex == 0xFFFFFFFF ? null : (uint?)entry->DefIndex; return(b); }
public static TexturePageInfo GetTexPageInfo(GMFileContent content, uint id) { if (id >= content.TexturePages->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var tpe = (TexPageEntry *)GMFile.PtrFromOffset(content, (&content.TexturePages->Offsets)[id]); var ret = new TexturePageInfo(); ret.Source = tpe->Source; ret.Destination = tpe->Dest; ret.Size = tpe->Size; ret.SpritesheetId = tpe->SpritesheetId; return(ret); }
static RoomTile ReadRoomTile(GMFileContent content, IntPtr p) { var entry = (RoomTileEntry *)p; var t = new RoomTile(); t.DefIndex = entry->DefIndex; t.Position = entry->Position; t.SourcePosition = entry->SourcePos; t.Size = entry->Size; t.Scale = entry->Scale; t.Colour = entry->Colour; t.Depth = entry->TileDepth; t.InstanceID = entry->InstanceID; return(t); }
public static GeneralInfo GetGeneralInfo(GMFileContent content) { var ret = new GeneralInfo(); var ge = content.General; ret.IsDebug = ge->Debug; ret.FileName = StringFromOffset(content, ge->FilenameOffset); ret.Configuration = StringFromOffset(content, ge->ConfigOffset); ret.Name = StringFromOffset(content, ge->NameOffset); ret.DisplayName = StringFromOffset(content, ge->DisplayNameOffset); ret.GameID = ge->GameID; ret.WindowSize = ge->WindowSize; ret.BytecodeVersion = ge->BytecodeVersion; ret.Version = new Version(ge->Major, ge->Minor, ge->Release, ge->Build); if (ret.Version >= new Version(2, 0)) { Console.Error.WriteLine("Warning: GM:S 2.0 support is incomplete!"); } ret.InfoFlags = ge->Info; ret.ActiveTargets = ge->ActiveTargets; ret.SteamAppID = ge->AppID; ret.unknown = new uint[4]; for (uint i = 0; i < 4; i++) { ret.unknown[i] = ge->_unknown[i]; } ret.LicenseMD5Hash = new byte[0x10]; Marshal.Copy((IntPtr)ge->MD5, ret.LicenseMD5Hash, 0, 0x10); ret.LicenceCRC32 = ge->CRC32; ret.Timestamp = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(ge->Timestamp); ret.WeirdNumbers = new uint[ge->NumberCount]; for (uint i = 0; i < ge->NumberCount; i++) { ret.WeirdNumbers[i] = (&ge->Numbers)[i]; } return(ret); }
static T[] ReadList <T>(GMFileContent content, CountOffsetsPair *list, Func <GMFileContent, IntPtr, T> readThing) { if (readThing == null) { throw new ArgumentNullException(nameof(readThing)); } var len = list->Count; var ret = new T[len]; var addresses = &list->Offsets; for (uint i = 0; i < len; i++) { ret[i] = readThing(content, (IntPtr)GMFile.PtrFromOffset(content, addresses[i])); } return(ret); }
// C# doesn't like pointers of generic types... static ReferenceDef[] GetRefDefsInternal(GMFileContent content, SectionRefDefs *section, long elemOff, uint amount, uint rdeSize, Func <IntPtr, ReferenceDef> iter, bool correct = true) { if (section->Header.Size <= 4) { return(new ReferenceDef[0]); } amount = correct && amount == 0 ? section->Header.Size / rdeSize : amount; var r = new ReferenceDef[amount]; uint i = 0; for (var rde = (byte *)§ion->Entries + elemOff * sizeof(uint); i < amount; rde += rdeSize, i++) { r[i] = iter((IntPtr)rde); } return(r); }
public static string DecompileCode(GMFileContent content, RefData rdata, CodeInfo code, bool absolute = false) { 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, absolute: absolute); 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.HEX_PRE).Append(code.Size.ToString(SR.HEX_FM6)).AppendLine(SR.COLON); sb.Append(SR.INDENT4).AppendLine(FinalRet.ToString()); return(sb.ToString()); }
public static CodeInfo DisassembleCode(GMFileContent content, uint id) { if (id >= content.Code->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var cee = (CodeEntryE *)GMFile.PtrFromOffset(content, (&content.Code->Offsets)[id]); var len = cee->Length; var bc = &cee->Bytecode; if (content.General->BytecodeVersion > 0xE) { var cef = (CodeEntryF *)cee; bc = (uint *)((byte *)&cef->BytecodeOffset + cef->BytecodeOffset); // ikr? } var ret = new List <IntPtr>(); // doesn't like T* as type arg len = Utils.PadTo(len, 4); AnyInstruction *instr; for (uint i = 0; i * 4 < len; /* see loop end */) { instr = (AnyInstruction *)(bc + i); ret.Add((IntPtr)instr); i += DisasmExt.Size(instr, content.General->BytecodeVersion); } return(new CodeInfo { Name = SectionReader.StringFromOffset(content, cee->Name), Instructions = Utils.MPtrListToPtrArr(ret), Size = cee->Length }); }
public static TextureInfo GetTextureInfo(GMFileContent content, uint id) { if (id >= content.Textures->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var te = (TextureEntry *)GMFile.PtrFromOffset(content, (&content.Textures->Offsets)[id]); var ret = new TextureInfo(); var png = (PngHeader *)GMFile.PtrFromOffset(content, te->Offset); ret.Width = Utils.SwapEnd32(png->IHDR.Width); ret.Height = Utils.SwapEnd32(png->IHDR.Height); ret.PngData = new byte[PngLength(png)]; Marshal.Copy((IntPtr)png, ret.PngData, 0, ret.PngData.Length); return(ret); }
public static SoundInfo GetSoundInfo(GMFileContent content, uint id) { if (id >= content.Sounds->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var se = (SoundEntry *)GMFile.PtrFromOffset(content, (&content.Sounds->Offsets)[id]); var ret = new SoundInfo(); ret.Name = StringFromOffset(content, se->NameOffset); ret.Type = StringFromOffset(content, se->TypeOffset); ret.File = StringFromOffset(content, se->FileOffset); ret.VolumeMod = se->Volume; ret.PitchMod = se->Pitch; ret.AudioId = se->AudioId; ret.IsEmbedded = (se->Flags & SoundEntryFlags.Embedded) != 0; ret.IsCompressed = (se->Flags & SoundEntryFlags.Compressed) != 0; return(ret); }
public static ShaderInfo GetShaderInfo(GMFileContent c, uint id) { if (id >= c.Shaders->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var sh = (ShaderEntry *)GMFile.PtrFromOffset(c, (&c.Shaders->Offsets)[id]); var si = new ShaderInfo(); si.Name = StringFromOffset(c, sh->Name); si.Type = sh->Type.Decode(); si.Code.GLSL_ES = GetVxFxStrings(c, sh->GLSL_ES); si.Code.GLSL = GetVxFxStrings(c, sh->GLSL); si.Code.HLSL9 = GetVxFxStrings(c, sh->HLSL9); //si.Code.HLSL11 = GetVxFxBlobs (c, sh->HLSL11 , length=???); // TODO var ats = new string[sh->AttributeCount]; for (uint i = 0; i < ats.Length; ++i) { ats[i] = StringFromOffset(c, (&sh->Attributes)[i]); } si.Attributes = ats; var sh2 = (ShaderEntry2 *)&((&sh->Attributes)[sh->AttributeCount]); si.Code.PSSL = GetVxFxBlobs(c, sh2->PSSL); si.Code.Cg = GetVxFxBlobs(c, sh2->Cg); si.Code.Cg_PS3 = GetVxFxBlobs(c, sh2->Cg_PS3); return(si); }
public static TimelineInfo GetTimelineInfo(GMFileContent c, uint id) { if (id >= c.Timelines->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var tl = (TimelineEntry *)GMFile.PtrFromOffset(c, (&c.Timelines->Offsets)[id]); TimelineInfo ti; ti.Name = StringFromOffset(c, tl->Name); var ks = new TimelineKeyframe[tl->KeyframeCount]; for (uint i = 0; i < ks.Length; ++i) { var tk = &((&tl->Keyframes)[i]); var tes = (CountOffsetsPair *)GMFile.PtrFromOffset(c, tk->OffsetToEvents); ks[i].Time = tk->Time; var ees = new uint[tes->Count]; for (uint j = 0; j < ees.Length; ++j) { ees[j] = ((TimelineEventEntry *)GMFile.PtrFromOffset(c, (&tes->Offsets)[i]))->EventID; } ks[i].EventIDs = ees; } ti.Keyframes = ks; return(ti); }
public static TextureInfo GetTextureInfo(GMFileContent content, uint id) { if (id >= content.Textures->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var teOffs = (&content.Textures->Offsets)[id]; var te = (TextureEntry *)GMFile.PtrFromOffset(content, teOffs); uint off = te->Offset; if (off == 0) { var te2 = (TextureEntry2 *)GMFile.PtrFromOffset(content, teOffs); off = te2->Offset; } if (off == 0) { Console.Error.WriteLine($"Warning: texture {id} has no PNG data."); return(default(TextureInfo)); } var ret = new TextureInfo(); var png = (PngHeader *)GMFile.PtrFromOffset(content, off); ret.Width = Utils.SwapEnd32(png->IHDR.Width); ret.Height = Utils.SwapEnd32(png->IHDR.Height); ret.PngData = new byte[PngLength(png)]; Marshal.Copy((IntPtr)png, ret.PngData, 0, ret.PngData.Length); return(ret); }
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 BackgroundInfo GetBgInfo(GMFileContent content, uint id) { if (id >= content.Backgrounds->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var ret = new BackgroundInfo(); var be = (BgEntry *)GMFile.PtrFromOffset(content, (&content.Backgrounds->Offsets)[id]); ret.Name = StringFromOffset(content, be->Name); for (uint i = 0; i < content.TexturePages->Count; i++) { if (be->TextureOffset == (&content.TexturePages->Offsets)[i]) { ret.TexPageIndex = i; break; } } return(ret); }
public static RoomInfo GetRoomInfo(GMFileContent content, uint id) { if (id >= content.Rooms->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var ret = new RoomInfo(); var re = (RoomEntry *)GMFile.PtrFromOffset(content, (&content.Rooms->Offsets)[id]); ret.Name = StringFromOffset(content, re->Name); ret.Caption = StringFromOffset(content, re->Caption); ret.Size = re->Size; ret.Speed = re->Speed; ret.IsPersistent = re->Persistent.IsTrue(); ret.Colour = re->Colour; ret.DrawBackgroundColour = re->DrawBackgroundColour.IsTrue(); ret.EnableViews = (re->Flags & RoomEntryFlags.EnableViews) != 0; ret.ShowColour = (re->Flags & RoomEntryFlags.ShowColour) != 0; ret.ClearDisplayBuffer = (re->Flags & RoomEntryFlags.ClearDisplayBuffer) != 0; ret.World = re->World; ret.Bounding = re->Bounding; ret.Gravity = re->Gravity; ret.MetresPerPixel = re->MetresPerPixel; ret.Backgrounds = ReadList(content, (CountOffsetsPair *)GMFile.PtrFromOffset(content, re->BgOffset), ReadRoomBg); ret.Views = ReadList(content, (CountOffsetsPair *)GMFile.PtrFromOffset(content, re->ViewOffset), ReadRoomView); ret.Objects = ReadList(content, (CountOffsetsPair *)GMFile.PtrFromOffset(content, re->ObjOffset), ReadRoomObj); ret.Tiles = ReadList(content, (CountOffsetsPair *)GMFile.PtrFromOffset(content, re->TileOffset), ReadRoomTile); return(ret); }
public static SpriteInfo GetSpriteInfo(GMFileContent content, uint id, Dictionary <uint, uint> toil = null) { if (id >= content.Sprites->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var se = (SpriteEntry *)GMFile.PtrFromOffset(content, (&content.Sprites->Offsets)[id]); var ret = new SpriteInfo(); ret.Name = StringFromOffset(content, se->Name); ret.Size = se->Size; ret.Bounding = se->Bounding; ret.BBoxMode = se->BBoxMode; ret.Origin = se->Origin; ret.Version = 1; ret.SeparateColMasks = se->SeparateColMasks.IsTrue(); var tex = &se->Textures; if (tex->Count == ~(uint)0) { var se2 = (SpriteEntry2 *)GMFile.PtrFromOffset(content, (&content.Sprites->Offsets)[id]); tex = &se2->Textures; ret.Version = 2; ret.UnknownFloat = se2->funk; } if (tex->Count == ~(uint)0) { Console.Error.WriteLine($"Warning: texture count of sprite {id} ({((ulong)se-(ulong)content.RawData.BPtr).ToString(SR.HEX_FM8)}) is -1."); return(ret); } ret.TextureIndices = new uint[tex->Count]; for (uint i = 0; i < tex->Count; i++) { if (toil == null) { for (uint j = 0; j < content.TexturePages->Count; j++) { if ((&tex->Offsets)[i] == (&content.TexturePages->Offsets)[j]) { ret.TextureIndices[i] = j; break; } } } else { ret.TextureIndices[i] = toil[(&tex->Offsets)[i]]; } } SpriteCollisionMask *masks = (SpriteCollisionMask *)((ulong)&tex->Offsets + sizeof(uint) * tex->Count); // TODO: store this in a different way. this is wasteful as f**k. uint amt = ret.SeparateColMasks ? masks->MaskCount : 1; //Console.WriteLine("amt="+amt.ToString(SR.HEX_FM8) + " at " + ((ulong)&masks->MaskCount - (ulong)content.RawData.BPtr).ToString(SR.HEX_FM8)); if (amt < 0x100) // guesstimate { ret.CollisionMasks = new bool[amt][, ]; byte *maskData = &masks->MaskData; uint w = (uint)(ret.Size.X & 0x7FFFFFFF); uint h = (uint)(ret.Size.Y & 0x7FFFFFFF); uint wPad = (((w & 7) == 0) ? w : (w - (w & 7) + 8)) / 8; for (uint i = 0; i < amt; i++) { bool[,] stuff = new bool[wPad * 8, h]; for (uint y = 0; y < h; y++) { for (uint x = 0; x < wPad * 8; x++) { uint rown = y * wPad; uint byten = x >> 3; byte bitn = (byte)(x & 7); byte *curptr = maskData + rown + byten; byte curbyte = *curptr; byte curbit = (byte)(curbyte & (byte)(1 << bitn)); stuff[x, y] = curbit != 0; } } ret.CollisionMasks[i] = stuff; maskData += wPad * h; } } else { Console.Error.WriteLine($"Warning: collision mask of sprite {id} ({((ulong)se - (ulong)content.RawData.BPtr).ToString(SR.HEX_FM8)}) is bogus ({amt.ToString(SR.HEX_FM8)}), ignoring..."); } return(ret); }
static byte[] ReadByteArray(GMFileContent c, uint off, uint len) { byte[] r = new byte[len]; Marshal.Copy((IntPtr)GMFile.PtrFromOffset(c, off), r, 0, unchecked ((int)len)); return(r); }
static GraphVertex[] CreateVertices(GMFileContent content, CodeInfo code) { var blocks = SplitBlocks(code, content.General->BytecodeVersion); 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 Statement[] ParseStatements(GMFileContent content, RefData rdata, CodeInfo code, AnyInstruction *[] instr = null, Stack <Expression> stack = null, List <Expression> dupTars = null, bool absolute = false) { var bcv = content.General->BytecodeVersion; //! 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]; var baseOff = absolute ? (long)content.RawData.BPtr : (long)firstI; //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); }; 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); }; 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 = (BranchInstruction *)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(bcv) == InstructionKind.SingleType ? st.Type : (ins->Kind(bcv) == InstructionKind.DoubleType ? dt.Types.Type1 : 0); var t2 = ins->Kind(bcv) == InstructionKind.DoubleType ? dt.Types.Type2 : (ins->Kind(bcv) == InstructionKind.SingleType ? st.Type : 0); #endregion GeneralOpCode opc; switch (opc = ins->OpCode.General(bcv)) { #region dup, pop case GeneralOpCode.Dup: var normal = true; if (i < instr.Length - 1 && instr[i + 1]->OpCode.General(bcv) == GeneralOpCode.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()); } //var stackArr = stack.ToArray(); //for (i = 0; i <= ins->SingleType.DupExtra; i++) //{ // if (i >= stackArr.Length) // { // // .__. // break; // } var elem = stack.Peek(); //stackArr[stackArr.Length - i - 1]; if (elem.WalkExprTree(e => e is CallExpression).Any(Utils.Identity)) { stack.Push(new UnaryOperatorExpression { Input = elem, Operator = UnaryOperator.Duplicate, OriginalType = elem.ReturnType, ReturnType = st.Type }); } else { stack.Push(elem); } //} break; case GeneralOpCode.Pop: if (stack.Count > 0 && stack.Peek() is CallExpression) { AddStmt(new CallStatement { Call = (CallExpression)stack.Pop() }); } else { AddStmt(new PopStatement()); } break; #endregion #region br etc //TODO: use actual '(with obj ...)' syntax //! it might mess with the CFG structure case GeneralOpCode.PushEnv: case GeneralOpCode.PopEnv: case GeneralOpCode.Brt: case GeneralOpCode.Brf: case GeneralOpCode.Br: { var a = pbr->Offset.UValue * 4; if ((a & 0xFF000000) != 0) { a &= 0x00FFFFFF; a -= 0x01000000; } var tar = (AnyInstruction *)((long)ins + unchecked ((int)a)); var off = (long)ins - baseOff + unchecked ((int)a); switch (opc) { case GeneralOpCode.PushEnv: AddStmt(new PushEnvStatement { Target = tar, TargetOffset = off, Parent = stack.Pop() }); break; case GeneralOpCode.PopEnv: AddStmt(new PopEnvStatement { Target = tar, TargetOffset = off }); break; case GeneralOpCode.Brt: case GeneralOpCode.Brf: case GeneralOpCode.Br: AddStmt(new BranchStatement { Type = pbr->Type(bcv), Conditional = pbr->Type(bcv) == BranchType.Unconditional ? null : Pop(), Target = tar, TargetOffset = off }); break; } } break; #endregion #region break, ret, exit case GeneralOpCode.Break: stack.Push(new AssertExpression { ControlValue = pbk->Signal, ReturnType = pbk->Type, Expr = Pop() }); break; case GeneralOpCode.Ret: AddStmt(new ReturnStatement { ReturnType = pst->Type, RetValue = Pop() }); break; case GeneralOpCode.Exit: AddStmt(new ExitStatement()); break; #endregion #region set case GeneralOpCode.Set: if (se.IsMagic) { AddStmt(new MagicSetStatement { OriginalType = se.Types.Type1, ReturnType = se.Types.Type2 }); break; } 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, true).Name : null, Target = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]], Value = Pop(), ArrayIndices = ind ?? TryGetIndices(se.DestVar.Type) }); break; #endregion default: switch (ins->ExprType(bcv)) { #region variable case ExpressionType.Variable: var vt = ((Reference *)&pps->ValueRest)->Type; stack.Push(/*vt == VariableType.StackTop &&*/ (InstanceType)ps.Value == InstanceType.StackTopOrGlobal ? new MemberExpression { Owner = Pop(), ReturnType = ps.Type, Type = vt, OwnerType = (InstanceType)ps.Value, OwnerName = se.Instance > InstanceType.StackTopOrGlobal ? SectionReader.GetObjectInfo(content, (uint)se.Instance, true).Name : null, Variable = rdata.Variables[rdata.VarAccessors[(IntPtr)ins]], ArrayIndices = TryGetIndices(vt) } : 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, (uint)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(bcv) }); break; #endregion #region unaryop case ExpressionType.UnaryOp: stack.Push(new UnaryOperatorExpression { OriginalType = t1, ReturnType = t2, Input = Pop(), Operator = ins->UnaryOp(bcv) }); break; #endregion } break; } } FlushStack(); return(stmts.ToArray()); }
public static ObjectInfo GetObjectInfo(GMFileContent content, uint id, bool nameonly = false) { if (id >= content.Objects->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var ret = new ObjectInfo(); var oe = (ObjectEntry *)GMFile.PtrFromOffset(content, (&content.Objects->Offsets)[id]); ret.Name = StringFromOffset(content, oe->Name); ret.SpriteIndex = oe->SpriteIndex; ret.IsVisible = oe->Visible.IsTrue(); ret.IsSolid = oe->Solid.IsTrue(); ret.Depth = oe->Depth; ret.IsPersistent = oe->Persistent.IsTrue(); ret.ParentId = oe->ParentId < 0 ? null : (uint?)oe->ParentId; ret.TexMaskId = oe->MaskId < 0 ? null : (uint?)oe->MaskId; ret.Physics = oe->HasPhysics.IsTrue() ? (ObjectPhysics?)oe->Physics : null; ret.IsSensor = oe->IsSensor.IsTrue(); ret.CollisionShape = oe->CollisionShape; var hasMore = oe->Rest.ShapePoints.Count > 0x00FFFFFF; // good enough for now var shapeCop = hasMore ? &oe->Rest.ShapePoints_IfMoreFloats : &oe->Rest.ShapePoints; if (nameonly) { return(ret); } if (hasMore) { ret.OtherFloats = new float[4]; Marshal.Copy((IntPtr)(oe->Rest.MoreFloats), ret.OtherFloats, 0, 4); } else { ret.OtherFloats = EmptyFloatArr; } if ((shapeCop->Count & 0xFFFFF000) != 0) { Console.Error.WriteLine($"Warning: shape point coords of object {id} are bogus, ignoring..."); ret.ShapePoints = null; } else { ret.ShapePoints = new int[shapeCop->Count][][]; for (uint i = 0; i < (shapeCop->Count); i++) { uint shapePointOff = (&shapeCop->Offsets)[i]; uint *shapePointPtr = (uint *)GMFile.PtrFromOffset(content, shapePointOff); //Console.WriteLine(((IntPtr)xoff).ToString(SR.HEX_FM8) + SR.SPACE_S + ((IntPtr)yoff).ToString(SR.HEX_FM8)); if ((shapePointOff & 0xFF000000) != 0 || shapePointPtr == null) { Console.Error.WriteLine($"Warning: shape point coord {i} of object {id} is bogus, ignoring..."); continue; } uint shapePointPointCount = *shapePointPtr; uint *shapePointPointOffsets = shapePointPtr + 1; ret.ShapePoints[i] = new int[shapePointPointCount][]; for (uint j = 0; j < shapePointPointCount; j++) { uint shapePointPointOff = shapePointPointOffsets[j]; int *shapePointPointPtr = (int *)GMFile.PtrFromOffset(content, shapePointPointOff); // TODO: fixed size, but mostly the same across // entries. Probably structure of some sort. // Index 1/2 look like count/offset, but count is // always 1 and offset is always +4. uint pointpointlen = 17; ret.ShapePoints[i][j] = new int[pointpointlen]; for (uint k = 0; k < ret.ShapePoints[i][j].Length; k++) { ret.ShapePoints[i][j][k] = shapePointPointPtr[k]; } } } } return(ret); }
public static string DisplayInstructions(GMFileContent content, RefData rdata, CodeInfo code, AnyInstruction *[] instructions = null, bool absolute = false) { var bcv = content.General->BytecodeVersion; 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 - (absolute ? (long)content.RawData.BPtr : (long)firstI); sb.Append(HEX_PRE).Append(relInstr.ToString(HEX_FM6)) .Append(COLON_S).Append(iptr->OpCode.ToPrettyString(bcv)).Append(' '); switch (iptr->Kind(content.General->BytecodeVersion)) { case InstructionKind.SingleType: var st = iptr->SingleType; sb.Append(st.Type.ToPrettyString()); if (bcv > 0xE && st.OpCode.VersionF == FOpCode.Dup) { sb.Append(' ').Append(st.DupExtra); } break; case InstructionKind.DoubleType: var dt = iptr->DoubleType; if (bcv > 0xE && iptr->OpCode.VersionF == FOpCode.Cmp) { sb.Append(dt.ComparisonType.ToPrettyString()).Append(' '); } sb.Append(dt.Types); break; case InstructionKind.Goto: var g = iptr->Goto; var a = g.Offset.UValue * 4; if ((a & 0xFF000000) != 0) { a &= 0x00FFFFFF; a -= 0x01000000; } sb.Append(HEX_PRE).Append(Utils.ToHexSignString(relInstr + unchecked ((int)a), HEX_FM6)); break; #region set case InstructionKind.Set: var s = iptr->Set; sb.Append(s.Types).Append(' '); if (s.IsMagic) { sb.Append(MAGIC); break; } 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 ObjectInfo GetObjectInfo(GMFileContent content, uint id) { if (id >= content.Objects->Count) { throw new ArgumentOutOfRangeException(nameof(id)); } var ret = new ObjectInfo(); var oe = (ObjectEntry *)GMFile.PtrFromOffset(content, (&content.Objects->Offsets)[id]); ret.Name = StringFromOffset(content, oe->Name); ret.SpriteIndex = oe->SpriteIndex; ret.IsVisible = oe->Visible.IsTrue(); ret.IsSolid = oe->Solid.IsTrue(); ret.Depth = oe->Depth; ret.IsPersistent = oe->Persistent.IsTrue(); ret.ParentId = oe->ParentId < 0 ? null : (uint?)oe->ParentId; ret.TexMaskId = oe->MaskId < 0 ? null : (uint?)oe->MaskId; ret.Physics = oe->HasPhysics.IsTrue() ? (ObjectPhysics?)oe->Physics : null; ret.IsSensor = oe->IsSensor.IsTrue(); ret.CollisionShape = oe->CollisionShape; var hasMore = oe->Rest.ShapePoints.Count > 0x00FFFFFF; // good enough for now var shapeCop = hasMore ? &oe->Rest.ShapePoints_IfMoreFloats : &oe->Rest.ShapePoints; if (hasMore) { ret.OtherFloats = new float[4]; Marshal.Copy((IntPtr)(oe->Rest.MoreFloats), ret.OtherFloats, 0, 4); } else { ret.OtherFloats = EmptyFloatArr; } if ((shapeCop->Count & 0xFFFFF000) != 0) { Console.WriteLine($"Warning: shape point coords of object {id} are bogus, ignoring..."); ret.ShapePoints = null; } else { ret.ShapePoints = new Point[shapeCop->Count >> 1]; for (uint i = 0; i < (shapeCop->Count >> 1); i++) { uint xoff = (&shapeCop->Offsets)[i << 1], yoff = (&shapeCop->Offsets)[(i << 1) + 1]; int *xptr = (int *)GMFile.PtrFromOffset(content, xoff), yptr = (int *)GMFile.PtrFromOffset(content, yoff); //Console.WriteLine(((IntPtr)xoff).ToString(SR.HEX_FM8) + SR.SPACE_S + ((IntPtr)yoff).ToString(SR.HEX_FM8)); if (((xoff | yoff) & 0xFFF00000) != 0 || xptr == null || yptr == null) { Console.WriteLine($"Warning: shape point coord {i} of object {id} is bogus, ignoring..."); ret.ShapePoints[i] = new Point(-0xDEAD, -0xC0DE); continue; } ret.ShapePoints[i] = new Point( *(int *)GMFile.PtrFromOffset(content, xoff), *(int *)GMFile.PtrFromOffset(content, yoff) ); } } return(ret); }