Exemplo n.º 1
0
        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);
        }
Exemplo n.º 2
0
        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);
        }
Exemplo n.º 3
0
        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);
        }
Exemplo n.º 4
0
        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);
        }
Exemplo n.º 5
0
        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);
        }
Exemplo n.º 6
0
        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);
        }
Exemplo n.º 7
0
        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;
            }));
        }
Exemplo n.º 8
0
        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);
        }
Exemplo n.º 9
0
        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);
        }
Exemplo n.º 10
0
        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);
        }
Exemplo n.º 11
0
        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);
        }
Exemplo n.º 12
0
        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);
        }
Exemplo n.º 13
0
        // 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 *)&section->Entries + elemOff * sizeof(uint); i < amount; rde += rdeSize, i++)
            {
                r[i] = iter((IntPtr)rde);
            }

            return(r);
        }
Exemplo n.º 14
0
        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());
        }
Exemplo n.º 15
0
        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
            });
        }
Exemplo n.º 16
0
        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);
        }
Exemplo n.º 17
0
        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);
        }
Exemplo n.º 18
0
        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);
        }
Exemplo n.º 19
0
        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);
        }
Exemplo n.º 20
0
        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);
        }
Exemplo n.º 21
0
        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);
        }
Exemplo n.º 22
0
        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);
        }
Exemplo n.º 23
0
        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);
        }
Exemplo n.º 24
0
        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);
        }
Exemplo n.º 25
0
 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);
 }
Exemplo n.º 26
0
        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);
        }
Exemplo n.º 27
0
        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());
        }
Exemplo n.º 28
0
        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);
        }
Exemplo n.º 29
0
        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());
        }
Exemplo n.º 30
0
        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);
        }