Binary Format of GameMaker 7.0 Save Files (gmk) No Includes (no icon) By IsmAvatar You may need a hex editor to examine GM files and/or make modifications. You can use the one I used, XVI32. It can be found at http://www.chmaas.handshake.de/delphi/freeware/xvi32/xvi32.htm License Agreement These files are creations of IsmAvatar, you may copy them and share them etc. AS IS. You may not modify them, and you must include this License Agreement when distributing them. If you wish to see a modification, please contact IsmAvatar cmagicj@nni.com regarding the changes. You may use these files to learn from and experiment with, and, of course, to create your own GM IDE and/or as a guide for reading/modifying GM files. If you use these files for a product that is later distributed, I would appreciate credit in that product. If you violate this license agreement, I can and will seek Authoritative Intervention. Disclaimer These files are a source of education. I am not responsible for any damage caused by the usage of these files and any products created thereof. Note About Legality: I have thoroughly reviewed the Licence Agreement of GameMaker, and have a general knowledge of copyrights and such. These activities are legal, as long as I do not tamper with the compiler and as long as I maintain credit to Mark Overmars for the original GameMaker, which I will make sure to revere and worship greatly in this product. A note from Mark Overmars himself regarding this activity: "Sorry for not replying earlier. I was first was on holidays and then trying to catch up. I don;t think this is an important issue. Actually .gm6 files are not really protected. And even though I prefer people not to hack them, I cannot see a reason to be worried about this. Mark" (all typos preserved) Note About Hex: Unless otherwise stated (normally with a note that says "(dec)" after it), all byte locations and quantities are in hex. All dates and times are in decimal gregorian. Use calculator to get the binary or decimal representation.* To get the ascii representation, open up notepad, put font in Courier New, Press and hold the Alt Key, and then, with the numpad, type the decimal (4 digits, leading zeros), and release Alt. The character will appear. Note About Calculator Translation: Longints tend to appear backwords in MS Calc. For example, bytes 8-B make up the game id. The max, 00E1F505, when typed into the calculator will return 14808325(dec), which is incorrect. When typed completely backwords, 505F1E00, you get 1348410880(dec), still incorrect. Take each byte, which is 2 hexs, and put all the bytes in backwords. 05F5E100 will return 100million(dec). For the purposes herein, we will refer to hex in the way it appears as read from a hex editor, left to right. You must take it upon yourself to flip that if you want to type it into a calculator. Note About Images and Game Information GM appears to store images in Icon format. If you leave the default, GM tends to insert a longint of FFFFFFFF or -1(dec) meaning No Image. The Game Information is in Rich Text Format. Documentations for the Icon Format and for the Rich Text Format should not be difficult to find, in case your language does not support these. Note About Longint GM uses primarily longints for all data. This is a combination of 4 bytes. Translate it into a decimal equivelent (see calculator note above) for your magical number. Even basic things like True/False and Radio Buttons etc. each tend to be stored in their own 4-byte set, rather than merging 8 of them to make a single byte or merging 32 of them to make that same 4-byte. When I refer to any single byte for a location, it is not unlikely that I am actually referring to the entire 4-byte set - that byte with the 3 bytes after it - as a longint. Note About Insertion and Curly Brackets { } Sometimes, GM will use something call Insertion, in which it will make one longint the length of the insertion, and then it will insert. It is unlikely (25% chance) for an Insertion to divide evenly by longints. I record insertions with curly brackets { }. Prior to the curly brackets will be a piece of information regarding how many times the information inside the curly brackets will appear/reappear. The prior information may be either a true or false statement, stating whether the information contained appears at all or not, or it may be a number higher than 1 in which case the information will be repeated or given a length of the specification. Example of a true/false Object exists { Object ID } In the above statement, you'll have a longint of 1/0. If 1, meaning the Object Exists, the Object ID will follow that longint. If 0, meaning the Object does not exist, the Object ID will not appear, and it will proceed on to the next step. Example of string Name Length { Name } which would turn out as something like 07 Sprites ... or just 00 ... Example of repetition Number of Views { View ID } In this case, supposing there's 8 views, each with increments of ID The result would be: 08 00 01 02 03 04 05 06 07 (replacing each of those numbers with the longint equivelent) At other times, I may just type "Insert Stuff", in which case it will be understood that if no stuff is inserted or the stuff has a 0-length, it can be skipped over and bytes proceed as normal (you will notice a "b" or "c" after resuming - this simply means that it is the second occurrance of this byte location, supposing the first never occurred). Byte numbers are not placed inside curly brackets for this txt, because they would require bare necessities, such as previously-existing objects or sprites, and this txt is not permitted to rely on insertions. ---------------------------- Now to explain the bytes. 0 tells GM that it's a gm save file. 91D51200 or 1234321(dec) 4 is the version of GM. BC020000 is all we focus on here. BC02 = 700(dec) for GM7.0 note, BD02 or 701(dec) pops up in various places in GM7.0 note, 6C02 or 620(dec) pops up in various places in GM7.0 this may be because Mark was planning on calling it 6.2 5802 = 600(dec) for GM6.1 and GM6.0 (cross-compatable). 1202 = 530(dec) for GM5.3, F401 = 500(dec) for GM5.2 and GM5.0 (forward compat) 0802 or FE01 = 510(dec) for GM5.1 AE01 = 430(dec) for GM4.3 9B01 = 411(dec) popped up. GM4.3 and 4.1[1] were probably compat. For resources, first GM version tells when the resource type was added. Second GM version tells when the last update of the resource type was. 8 is Game ID. 0-100,000,000(dec) or 0-00E1F505 C-1B seem to go hand-in-hand with the Game ID, for when they change. Its purpose is unclear. You may just plug in 0's with no apparent effects. 1C is the GM version needed for the following info (BD02) 20 is "Start in full-screen mode" 24 is "Interpolate colors between pixels" 28 is "Don't draw a border in windowed mode" 2C is "Display the cursor" 30-33 is "Scaling" in longint. "Fixed scale (in %):" = 01000000-E7030000 or 1-999(dec) "Full scale" = 00000000 or 0(dec) "Keep aspect ratio" = FFFFFFFF or -1(dec) 34 is "Allow the player to resize the game window" 38 is "Let the game window always stay on top" 3C-3F is "Color outside the room region:". While GM uses a longint for colors, you can use only the first 3 for RGB and leave the last one as 00 40 is "Set the resolution of the screen" 44 is "Color Depth": 00-02 (see options, going down) 48 is "Resolution": 00-06 (see options, going down) 4C is "Frequency": 00-05 (see options, going down) 50 is "Don't show the buttons in the window caption" 54 is "Use synchronization to avoid tearing" 58 is "Let switch between screen modes" 5C is "Let show the game information" 60 is "Let end the game" 64 is "Let save the game and load a game" 68 is "Let take a screenshot of the game" 6C is "Treat the close button as key" 70 is "Game Process Priority": 00-02 (see options, going down) 74 is "Freeze the game when the form looses focus" 78 is Loading Progress Bar (LPB) 00-02 (see options, going down) 7Ca Insert Progress Back Image data (def FFFFFFFF) only if LPB is 02 80 (if image is included) is unclear, but always 0A or 10(dec) 84 (if image is included) is size of image { image data } 7Cb Insert Progress Front Image data (def FFFFFFFF) only if LPB is 02 80 (if image is included) is unclear, but always 0A or 10(dec) 84 (if image is included) is size of image { image data } 7Cc is "Show your own image while loading" 80a Insert Loading Image data (def FFFFFFFF) 84 (if image is included) is unclear, but always 0A or 10(dec) 88 (if image is included) is size of image { image data } 80b is "Make image partially transparent" 84 is "Make translucent with alpha value:" 0-FF or 0-255(dec) 88 is "Scale progress bar image" 8C tells how large the Icon is { Icon Image Data } 90 is "Display error messages" 94 is "Write error messages to file game_errors.log" 98 is "Abort on all error messages" 9C is "Treat uninitialized variables as value 0" (evil!) A0 tells how long "Author:" is { "Author:" } A4 tells how long "Version:" is { "Version:" } A8-B0 (double) is the "Last Changed:" time - days since 1899-12-30 B4 tells how long "Information:" is { "Information:" } B8 is how many Constants there are { How long the name is { the name } How long the value is { the value } } C0 to 10F contain Resource Insertion, as I now document. C0 is the GM version needed for the following info (9001) C4 is how many sound ID's there are { Whether sound of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (5802) "Kind" (0="Normal", 1="Background", 2="3D", 3="Multimedia") How long filetype is { Filetype (like ".wav") } How long filename is { Filename (no directory) } Music exists { Data size { Music Data } } "Effects" (Chorus=1, Echo=2, Flanger=4, Gargle=8, Reverb=10) Notice, Effects is actually merged into 1 longint To get the value, simply add active Effect numbers together (double - 8 bytes) "Volume:" (double - 8 bytes) "Pan:" (0=center) Preload } } C8 is the GM version needed for the following info (9001) CC is how many sprite ID's there are { Whether sprite of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (1E02) "Width:" "Height:" "Left [Bounding Box]" (these numbers can be negative) "Right [Bounding Box]" "Bottom [Bounding Box]" "Top [Bounding Box]" (^^) "Transparent" "Smooth Edges" "Preload Texture" "Bounding Box" (0=Automatic, 1=Full image, 2=Manual) "Precise collision checking" "X Origin" "Y Origin" How many subimages there are { 0A Image Size { Image Data } } } } D0 is the GM version needed for the following info (9001) D4 is how many background ID's there are { Whether background of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (1F02) "Width:" "Height:" "Transparent" "Smooth Edges" "Preload Texture" "Use as tile set" "tile width:" "tile height:" "horizontal offset:" "vertical offset:" "horizontal sep:" "vertical sep:" Image exists { 0A Image Size { Image Data } } } } D8 is the GM version needed for the following info (A401) DC is how many path ID's there are { Whether path of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (1202) "connection kind" (0="Straight lines", 1="Smooth curve") "Closed" "Precision:" Room Index to show as Background (-1dec for none) "Snap X:" "Snap Y:" How many Points there are { (double - 8 bytes) X (double - 8 bytes) Y (double - 8 bytes) Speed } } } E0 is the GM version needed for the following info (9001) E4 is how many script ID's there are { Whether script of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (9001) How long Script is { Script } } } E8 is the GM version needed for the following info (1C02) EC is how many font ID's there are { Whether font of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (1C02) How long "Font:" is { "Font:" } "Size:" "Bold" "Italic" "Character Range" Begin "Character Range" End } } F0 is the GM version needed for the following info (F401) F4 is how many timeline ID's there are { Whether timeline of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (F401) How many Moments there are { Moment position Here is where Action information is inserted. This information is exactly the same as it appears in objects. As such, to understand how these bytes work, rather than documenting it twice, I shall refer you to see Actions below, between the **stars** } } } F8 is the GM version needed for the following info (9001) FC is how many object ID's there are { Whether object of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (AE01) "Sprite [Index]" (-1dec for none) "Solid" "Visible" "Depth:" "Persistent" "Parent [object index]:" (9CFFFFFF or -100dec for none) "Mask [sprite index]:" (-1dec for none) 0A (All numbers in this note are in decimal) Now we have an interesting thing. We have 11 -1's thrown in for each of the event types. They appear after each event's data (or simply appear where they are if the event type has no data), but the events may have -1's in them too, so you can't just count down 11 -1's and hope to be at the end of the object. No event begins with -1, so if you have a -1 where an event should be, you know that the event has no data. If there is data, it is documented here. If there are multiple kinds (event kind = 'numb' in the GM manual) of the same event, each kind will be added in without a -1 appended on the end until the last. The first kind to appear will actually be the highest kind number, and then it will count down. Other than that, Events appear in standard order. The stars ** you will see below relate to Time Lines. See Time Lines for more info. { Event Numb (see GM Manual) **9001 How many actions this event has { B801 Library ID Action ID Action Kind (Normal, Begin Group, Else, etc. See Lib Builder) Action may be Relative Action is a Question Action Applies to something Action Type (0=Nothing, 1=Function, 2=Code) How long "Function Name:" is { "Function Name:" } How long "Code ..." is { "Code ..." } "Argument Count:" How many arguments there are (actually this is always 8) { Argument Kind (See Lib Builder) } "Applies to" Object Index (dec: -1 for Self, -2 for Other) "Relative" How many arguments there are (always 8) { Argument Length { Argument Value } } "NOT" }** } (Don't forget the -1(dec) after each Primary event) } } 100 is the GM version needed for the following info (A401) 104 is how many room ID's there are { Whether room of this ID exists or not { How long "Name:" is { "Name:" } The GM version needed for the following info (1D02) How long "Caption for the room:" is { "Caption for the room:" } "Width:" "Height:" "Snap Y:" "Snap X:" "Turn the grid into an isometric grid" "Speed:" "Persistent" "[Background] Color:" (default C0C0C000) "Draw background color" How long "Creation code" is { "Creation code" } How many Backgrounds there are (always 8) { "Visible when room starts" "Foreground Image" (y/n) Background Image index (-1dec for none) "X:" "Y:" "Tile Hor." "Tile Vert." "Hor. Speed:" "Vert. Speed:" "Stretch" } "Enable the use of Views" How many Vies there are (always 8) { "Visible when room starts" "[View] X:" (min 0) "[View] Y:" (min 0) "[View] W:" (min 1) "[View] H:" (min 1) "[Port] X:" (min 0) "[Port] Y:" (min 0) "[Port] W:" (min 1) "[Port] H:" (min 1) "Hbor:" (min 0) "Vbor:" (min 0) "Hsp:" (min -1) "Vsp:" (min -1) "Object following" (-1dec for none) } How many Instances are in the room { X Y Object Index ID (starts with 100001dec) How long "Creation code" is { "Creation code" } Locked } How many Tiles are in the room { X Y Background Index TileX TileY Width Height Layer (Depth) ID (starts with 10000001dec) Locked } Remember the Room Editor size (true/false) Room Editor Width Room Editor Height "Toggle the showing of the grid" "Show Objects" "Show Tiles" "Show Backgrounds" "Show Foregrounds" "Show Views" "Delete underlying [objects]" "Delete underlying [tiles]" Tab: 0 objects, 1 settings, 2 tiles, 3 backgrounds, 4 views X Position of the horizontal scrollbar of the Room Editor Y Position of the vertical scrollbar of the Room Editor } } 108 is ID of last instance placed, so you know ID for next instance 10C is ID of last tile placed, so you know ID for next tile is the GM version needed for the following info (6C02) is how many Includes there are { The GM version needed for the following info (6C02) How long "File Name:" is { "File Name:" } How long "Original File:" is { "Original File:" } Whether an Original File is chosen or not Size of Original File "Store in the editable gm6 file" { Size of the file data { file data (zlib compression) } } Export (don't, temp dir, working dir, following folder) Size of folder to export to { folder to export to } "Overwrite the file if it exists" "Free memory after export" "Remove at game end" } is the GM version needed for the following info (BC02) is how many Extension Packages there are { How long Extension Package Name is { Extension Package Name } } 110 is the GM version needed for the following info (5802) 114 Background Color of Game Information 118 is the opposite of "Show help in a separate window" 11C tells how long "Form Caption:" is { "Form Caption:" } 120 is "Position Left:" 124 is "Position Top:" 128 is "Position Width:" 12C is "Position Height:" 130 is "Show the window border and caption" 134 is "Allow the player to resize the window" 138 is "Stay always on top" 13C is "Stop the game while showing help" 140 tells how long the "Game Information" content is { Content (rich text format) } 144 is the GM version needed for the following info (F401) 148 is how many longints will follow it (this information seems insignificant, but the format must be maintained. You're welcome to just plug in a 0 and leave out the contents with no apparent effects) { How many bytes will follow this { bytes } (I've almost always seen this number as 0, but once I ran across something where it contained a string of code that said "global.xxxx.. (56 x's)=0global.maxh=0", removed with no apparent effects on the functionality of the GM6 nor the gameplay) } 14C is the GM version needed for the following info (BC02) 150 is how many existing rooms there are (You're welcome to just plug in a 0 and leave out the contents with no apparent effects) { room indexes in tree order (this is just the way it appears, and the significance is unknown. As long as the format is maintained, it's fine. I've removed all the rooms and set the leading longint (above) to 0, I've replaced longints and reordered them, given indexes much higher than the last room index, etc., all with no apparent effects) } 154-End: Creating the Resource Tree There are C or 12(dec) Tree Resources - 9 primary, 3 secondary Format: Status, Grouping, Index, Length, { Name }, Contents You repeat that for all things on the resource tree, as they appear, going down, all branches expanded. Status: 1) Primary Resource 2) Group 3) Secondary Resource Grouping (in the order they appear in the file): 2) Sprites 3) Sounds 6) Backgrounds 8) Paths 7) Scripts 9) Fonts C) Time Lines 1) Objects 4) Rooms A) Game Information* B) Global Game Settings* D) Extension Packages* *Considered Secondary Resources that don't belong in a primary resource group. Index: All Secondary Resources are given an Index, or a reference number, which reflects the index of the resource far above, where you set their properties and options. Primary Resources, Groups, and Game Information and Global Game Settings all use 0 for their Index. Occasionally you may find that they will actually use a number other than zero. It is unclear why they would do this, but just keep it in mind. If the index does not reflect their resource number above, you may either get invisible resources, or resources. Also, make sure that no two secondary resources under a single Grouping have the same Index. This may result in Duplicate Complex, in which both are dependant on each other and anything you do to one is done to the other. Name Length { Name }: All have a Name Length. After that, it will count out that many bytes for the name itself. Contents: All Primary Resources and Groups have Contents count, which is how many groups/secondary resources it has within itself, non-recursive. Secondary Resources always have a content count of 0, since they may never contain anything. Non-recursive means that if a folder has several resources, only that folder is counted. Once it reaches the folder, it will tell how many resources it contains. Let's say you have 5 sprites, but all 5 of them are inside a Group, and that group falls under Sprites. The Sprites Primary Resource has 1 content, which is the Group. The Group has 5 contents - the 5 sprites. In an empty file, it would look something like this: 1 2 0 7 Sprites 0 1 3 0 6 Sounds 0 etc. In the pacman example that comes with GM6.1, it would look like this: 1 2 0 7 Sprites 6 2 2 0 7 pacmans 5 3 2 0 8 pac_left 0 3 2 1 9 pac_right 0 3 2 2 6 pac_up 0 3 2 3 8 pac_down 0 3 2 4 9 pac_stand 0 2 2 0 8 monsters 2 etc. This will cause the tree to look like this: +Sprites -pacmans []pac_left []pac_right []pac_up []pac_down []pac_stand -monsters (etc...) Since Primary Resources are included in the save file, you can change their names (and their lengths, as long as you update the longint before their name to reflect the new length) and it will actually display in the resource tree to the left.