OrionSR Posted July 21, 2017 Share Posted July 21, 2017 (edited) San Andreas Save File Companion This topic is open to discussion of the details of all versions of San Andreas save files. It is intended to provide a wider context for the save structure than the wiki can provide, and a home for questions and investigations related to SA saves and documentation in the tradition of: GTA III Save File Documentation and Vice City Save File Format The official documentation of the save file structure for the PC version is available on the SA Save Wiki at GTAModding.com. This document serves as a baseline for all variations. The save wiki has been well tested for many years and is very accurate, but it could use a few updates, consistent formatting, and a modern naming convention for field variables. SA Save Template for the 010 Editor: GTASA_010_BinaryTemplate_Xbox (on Google Drive) 010 templates can parse binary files into defined structures and variables. They are an invaluable tool for precisely documenting all known data and mapping out new structures. I'd prefer to use an open source tool but this is what we're working with now. Fortunately, the template files (*.bt) are in plain text and can serve as documentation for variations in saves of other systems. SAS.bt - Original 010 template created by Seemann. PC version only. Includes checksum script. SAS.multi.bt - Supports PC, PS2, PS2-J, and mobile versions of San Andreas. GTASA_010_BinaryTemplate_2017 - Extended support for non-standard mobile saves, enumeration, and custom functions. GTASA_010_BinaryTemplate_2019 - Minor adjustments to tasks, PopCycle, and names of structures. GTASA_010_BinaryTemplate_Xbox - Latest Version - Added Support for OG Xbox Save Files (more info) Always a work in progress. Links to Related Topics: Embedding Scripts in Save Files SA Save File Glitch Detection and Repair San Andreas Enex Documentation [SA] girlfriend globals / bitmasks Blip/Marker Struct - memory and save info Cheat Code Creation - PS2 save editing using AR-Max, Codebreaker, etc. Edited May 31, 2021 by OrionSR Updated GTASA template RyanDri3957V, Nick007J, dkluin and 4 others 7 Link to comment Share on other sites More sharing options...
Nick007J Posted July 21, 2017 Share Posted July 21, 2017 (edited) Mobile version additionally saves an index (2 bytes) of script allocated to the object in CTheScripts::ScriptsForBrains array. Rest of fields, as named by DK22Pac (unless I renamed them for my convenience): +24 byte bBonusValue +26 word wCostValue +28 dword nRemovalTime +32 dword EntityFlags +36 byte const 0 +40 dword ObjectFlags +44 byte bObjectType +45 byte bColDamageEffect +46 byte bStoredColDamageEffect +47 byte PhysicalFlags [partial, custom]: 1 DISABLE_COLLISION_FORCE 2 FREEZE_POSITION 4 IS_BULLETPROOF 8 IS_FIREPROOF 10 IS_COLLISIONPROOF 20 IS_MELEEPROOF 40 IS_EXPLOSIONPROOF [+48 word wScriptTriggerIndex] Edited July 21, 2017 by Nick007J RyanDri3957V and OrionSR 2 Link to comment Share on other sites More sharing options...
OrionSR Posted July 22, 2017 Author Share Posted July 22, 2017 (edited) Thanks Nick. That will help a lot. Do you have the full reference? At first glance I'm having a difficult time aligning the hex and decimal offsets. DK22Pac... So this a reference for objects in PCv1 memory? I'm anxious to start mapping things out against save records and to experiment with the flags but first I need to figure out why the timer reset fix is breaking Vice City saves. I've got reports that shopkeepers are refusing to spawn after the fix, and a glitch in a published tool has priority status. Edited July 22, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted July 22, 2017 Share Posted July 22, 2017 DK22Pac... So this a reference for objects in PCv1 memory? I used his field names for CObject structure in memory, as they are called in plugin-sdk. https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CObject.h As I see it now, I only changed hungarian notation prefixes for consistency with how I use it. I haven't got to CObject myself yet so can't comment too much on their exact meaning, but I don't see any reason not to trust his names. I'm anxious to start mapping things out against save records and to experiment with the flags but first I need to figure out why the timer reset fix is breaking Vice City saves. I've got reports that shopkeepers are refusing to spawn after the fix, and a glitch in a published tool has priority status. I will comment in relevant topic soon, I already know why it happens as this is a crucial knowledge for speedrunning Vice City. Link to comment Share on other sites More sharing options...
OrionSR Posted July 23, 2017 Author Share Posted July 23, 2017 (edited) 0x38 - bObjectType?I think I found a strategy to flag objects so they won't be saved. At save object record offset 0x38 is what I suspect is the Type field. The value is always 2 in my current set of examples; I suspect these are type 2 mission objects. When this value was edited to 6 to indicate "OBJECT_MISSION2" the object is missing in the next save. In the PC version object type 0 will be removed, but on Android they are changed back to type 2 objects. I managed a little progress on several of the flags. I'm not exactly sure if I've got the EntityFlags and ObjectFlags named properly. All offsets are for PC, and for these offsets anyway, appear to apply to Android as well. Unknown flags have been observed. 0x2C Opcode EntityFlags 01 0382: collision detection 04 unknown 20 0392: moveable 80 0750: visible 0100 unknown 1000 unknown 4000 0418: draw last 8000 unknown 010000 unknown 020000 0905: openable 200000 unknown08000000 unknown10000000 unknown 0x34 Opcode ObjectFlags 10 unknown 20 unknown 40 unknown 0200 035D: targetable 0400 0723: broken 2000 08E9: liftable (stealable) 4000 unknown 8000 unknown 010000 unknown 040000 0916: attractive_to_magnet 080000 unknown 0x3B PhysicalFlags 01 Disable_Collision_Force (0550: keep object in memory) 02 Freeze_Position 04 Is_Bulletproof (09CA:) 08 Is_Fireproof (09CA:) 10 Is_Collisionproof (09CA:) 20 Is_Meleeproof (09CA:) 40 Is_Explosionproof (09CA:) Based on the reference posted by Nick, I suspect but haven't tested: 0x39 bColDamageEffect0x3A bStoredColDamageEffect Opcodes that produced no changes in the save record: 0566: link_object $2658 to_interior 100654: make_object $2658 fireproof 1 (render scorched)071F: set_object $2658 health_to 4007F7: set_object $2658 destructible 1 // or 00875: set_object $2658 immune_to_nonplayer 1 // or 008D2: object $2658 scale_model 2.50906: set_object $2658 mass_to 1000.0 // float0908: set_object $2658 turn_mass_to 500 // float (whoops)04D9: set_object_records_collisions $2658 to 1 Specific Objects Blocking Missions Burning Desire, Model ID 3029, cr1_door, $4052, $2665 PC, remove if handle does not match global Architectural Espionage, Model ID 1491, Gen_doorINT01, remove all Breaking the Bank at Caligula's... 3037, warehouse_door2b, remove all 3089, ab_casdorLok, $4090. $2703 PC, remove if handle doesn't match global 2634, ab_vaultDoor, remove all Edited August 1, 2017 by OrionSR dkluin 1 Link to comment Share on other sites More sharing options...
OrionSR Posted July 30, 2017 Author Share Posted July 30, 2017 (edited) Progress on the object structure has stalled, but I made enough progress on my templates to get a script working for the 010 editor that can remove the glitchy objects that block missions. I'm working to expand my scripts to remove most of the trash objects from mobile saves without removing the standard objects from the map. To do that, I needed a list of saved objects in an In the Beginning save and information on what's added or removed later by missions. Saved Object List for In the Beginning Index Opcode MobVar PC_Var X Y Z ModelID ModelName Description0 029B: $4045 $2658 1903.383 967.62 11.438 3084 TRDCSGRGDOOR_LVS Four Dragons garage door used in casinos and heists1 029B: $4046 $2659 2167.82 -1518.193 20.237 3083 MD_POSTER billboard poster removed during Reuniting the Families2 029B: $4051 $2664 1055.629 2087.67 12.469 2938 SHUTTER_VEGAS loading bay door moved in You've Had Your Chips3 0107: $4052 $2665 2352.851 -1171.027 26.9669 3029 CR1_DOOR gang house door replaced in Burning Desire4 029B: $4053 $2666 -2179.353 661.232 50.214 3036 CT_GATEXR no parking gate removed in Mountain Cloud Boys5 029B: $4054 $2667 215.941 1874.571 13.903 2927 A51_BLASTDOORR blast door in Black Project6 029B: $4055 $2668 211.842 1874.571 13.903 2929 A51_BLASTDOORL blast door in Black Project7 029B: $4056 $2669 297.766 1842.618 6.764 2951 A51_LABDOOR lab door in Black Project8 0107: $4057 $2670 268.664 1884.06 15.925 3095 A51_JETDOOR jetpack escape hatch in Black Project9 0107: $4058 $2671 -2108.0 155.0 35.639 1684 PORTAKABIN cabin in Doherty broken in Deconstruction10 0107: $4059 $2672 -2089.0 172.0 35.639 1684 PORTAKABIN cabin in Doherty broken in Deconstruction11 0107: $4060 $2673 -2069.0 229.0 36.611 1684 PORTAKABIN cabin in Doherty removed in Deconstruction12 0107: $4061 $2674 -2077.0 271.0 35.337 1684 PORTAKABIN cabin in Doherty broken in Deconstruction13 0107: $4062 $2675 -2096.0 261.0 36.171 1684 PORTAKABIN cabin in Doherty broken in Deconstruction14 0107: $4063 $2676 -2129.0 306.0 35.314 1684 PORTAKABIN cabin in Doherty broken in Deconstruction15 0107: $4089 $2702 -2468.141 1547.938 22.7 2944 FREIGHT_SFW_DOOR cabin door in The Da Nang Thang16 029B: $4090 $2703 2168.644 1619.43 1000.3 3089 AB_CASDORLOK door to loading area in Breaking the Bank at Caligula's17 0107: $4093 $2706 1833.36 -1995.45 12.5 3061 AD_FLATDOOR extra doors in El Corona projects18 0107: $4094 $2707 1819.81 -1994.66 12.5 3061 AD_FLATDOOR extra doors in El Corona projects19 0107: $4095 $2708 1827.68 -1980.0 12.5 3061 AD_FLATDOOR extra doors in El Corona projects20 0107: $4096 $2709 1851.84 -1990.67 12.5 3061 AD_FLATDOOR extra doors in El Corona projects21 0107: $4097 $2710 1867.29 -1984.96 12.5 3061 AD_FLATDOOR extra doors in El Corona projects22 0107: $4098 $2711 1866.52 -1998.53 12.5 3061 AD_FLATDOOR extra doors in El Corona projects23 0107: $4099 $2712 1899.75 -1984.95 12.5 3061 AD_FLATDOOR extra doors in El Corona projects24 0107: $4100 $2713 1914.39 -1992.82 12.5 3061 AD_FLATDOOR extra doors in El Corona projects25 0107: $4101 $2714 1899.01 -1998.5 12.5 3061 AD_FLATDOOR extra doors in El Corona projects26 0107: $4102 $2715 1900.89 -2020.11 12.5 3061 AD_FLATDOOR extra doors in El Corona projects27 0107: $4103 $2716 1914.4 -2020.91 12.5 3061 AD_FLATDOOR extra doors in El Corona projects28 0107: $4104 $2717 1906.54 -2035.52 12.5 3061 AD_FLATDOOR extra doors in El Corona projects29 0107: $4105 $2718 1851.86 -2020.14 12.5 3061 AD_FLATDOOR extra doors in El Corona projects30 0107: $4106 $2719 1865.42 -2020.89 12.5 3061 AD_FLATDOOR extra doors in El Corona projects31 0107: $4107 $2720 1857.55 -2035.52 12.5 3061 AD_FLATDOOR extra doors in El Corona projects32 029B: $4108 $2721 2522.008 -1272.93 35.609 3059 IMY_SHASH_WALL smash wall removed in End of the Line33 029B: $4109 $2722 -2080.441 256.015 66.869 1383 TWRCRANE_M_04 Doherty crane34 029B: $4110 $2723 -2080.441 256.007 99.408 1384 TWRCRANE_M_01 Doherty crane35 029B: $4111 $2724 -2080.441 296.46 102.861 1385 TWRCRANE_M_02 Doherty crane36 029B: $4112 $2725 -2080.441 256.015 66.869 1404 LODCRANE_M_04 Doherty crane37 029B: $4113 $2726 -2080.441 256.007 99.408 1403 LODCRANE_M_01 Doherty crane38 029B: $4114 $2727 -1547.978 123.9883 26.9332 1380 MAGNOCRANE_01 export crane39 029B: $4115 $2728 -1547.978 123.988 26.933 1379 MAGNOCRANE_02 export crane40 029B: $4116 $2729 -1546.573 125.662 35.443 1382 MAGNOCRANE_03 export crane41 029B: $4117 $2730 -1547.978 123.988 26.933 1398 LODNOCRANE_01 export crane42 029B: $4118 $2731 -1547.978 123.988 26.933 1399 LODNOCRANE_02 export crane43 029B: $4119 $2732 -1546.573 125.662 35.443 1400 LODNOCRANE_03 export crane44 029B: $4127 $2740 709.45 915.93 -19.66 16330 QUARRY_CRANEBASE quarry crane45 029B: $4128 $2741 709.45 915.93 -15.591 16328 QUARRY_CRANE quarry crane46 029B: $4129 $2742 709.45 915.93 -14.570 16329 QUARRY_CRANEARM quarry crane47 029B: $4130 $2743 2399.202 1879.139 37.55 1383 TWRCRANE_M_04 Venturas crane48 029B: $4131 $2744 2399.202 1879.139 70.071 1384 TWRCRANE_M_01 Venturas crane49 029B: $4132 $2745 2399.202 1899.139 73.860 1385 TWRCRANE_M_02 Venturas crane50 029B: $4133 $2746 2399.202 1879.139 37.550 1404 LODCRANE_M_04 Venturas crane51 029B: $4134 $2747 2399.202 1879.139 70.071 1403 LODCRANE_M_01 Venturas crane52 029B: $1266 $1242 2401.75 -1714.477 13.125 1498 GEN_DOOREXT03 Denise's door53 029B: $1267 $1243 -2574.495 1153.023 54.669 1505 GEN_DOOREXT07 Katie's door54 029B: $1268 $1244 -1800.706 1201.041 24.12 1496 GEN_DOORSHOP02 Michelle's door55 029B: $1269 $1245 -383.46 -1439.64 25.33 1501 GEN_DOOREXT04 Helena's door56 029B: $1270 $1246 -1390.79 2639.33 54.973 1522 GEN_DOORSHOP3 Barbara's door57 029B: $1271 $1247 2038.036 2721.37 10.53 1498 GEN_DOOREXT03 Millie's door58 029B: $1272 $1248 -371.4 -1429.42 26.47 3093 c*ntGIRLDOOR Helena's barn door59 029B: $1219 $1195 -1543.742 -432.703 6.039 988 WS_APGATE SF airport gate60 029B: $1220 $1196 -1547.625 -428.82 6.039 988 WS_APGATE SF airport gate61 029B: $1221 $1197 -1222.953 53.826 14.134 988 WS_APGATE SF runway gate62 029B: $1222 $1198 -1218.206 68.883 14.134 988 WS_APGATE SF runway gate63 029B: $1223 $1199 1964.342 -2189.776 13.533 988 WS_APGATE LS airport gate64 029B: $1224 $1200 1958.851 -2189.777 13.553 988 WS_APGATE LS airport gate65 029B: $1225 $1201 1704.777 1605.165 10.058 988 WS_APGATE LV airport gate66 029B: $1226 $1202 1706.364 1610.422 10.058 988 WS_APGATE LV airport gate67 0107: $6561 $5177 245.968 1862.843 19.49 3117 A51_VENTCOVERB area 51 vent cover68 029B: $6674 $5290 -1465.797 501.289 1.145 3113 CARRIER_DOOR_SFSE carrier back door69 029B: $6675 $5291 -1414.453 516.453 16.688 3114 CARRIER_LIFT2_SFSE carrier side lift70 029B: $6676 $5292 -1456.719 501.297 16.953 3115 CARRIER_LIFT1_SFSE carrier rear lift Saved Objects Changed by Missions Total Opcode MobVar PC_Var X Y Z ModelID ModelName Description70 029B: $4046 $2659 2167.82 -1518.193 20.237 3083 MD_POSTER billboard poster removed during Reuniting the Familes74 029B: $4047 $2660 2322.845 8.304 25.483 2947 CR_DOOR_01 cafe back door added by Small Town Bank74 029B: $4048 $2661 2316.233 0.712 25.742 2947 CR_DOOR_01 bank back door added by Small Town Bank74 029B: $4049 $2662 2304.257 -17.744 25.742 2946 CR_DOOR_03 bank front door added by Small Town Bank74 029B: $4050 $2663 2304.257 -14.583 25.742 2946 CR_DOOR_03 bank front door added by Small Town Bank73 0107: $4060 $2673 -2069.0 229.0 36.611 1684 PORTAKABIN cabin in Doherty removed in Deconstruction72 029B: $4053 $2666 -2179.353 661.232 50.214 3036 CT_GATEXR no parking gate removed in Mountain Cloud Boys73 IMPEXPM [email protected] [email protected] -1573.881 135.3845 2.535 3077 NF_BLACKBOARD blackboard used by import/export thread72 029B: $4108 $2721 2522.008 -1272.93 35.609 3059 IMY_SHASH_WALL smash wall removed in End of the Line Notes: Known to delete Export Blackboard if repeated after it's creation - very rare. 0108: destroy_object $201($200,46i) // Back to School0108: destroy_object $201($200,46i) // Bike School It looks like the doors added by Small Town Bank were intended to be added during the initial game setup along with most of the other objects but they weren't removed from the mission cleanup list. These handles might reference objects that no longer exist early in the game. Object groups like the bridge barriers are not controlled by this process. Replacement objects for YKBB are controlled by another process. Missions that use saved objects often destroy and replace the object during the mission. The replacement object will probably have a new handle which will change the index associated with each object within the save. Edited July 31, 2017 by OrionSR Link to comment Share on other sites More sharing options...
OrionSR Posted August 1, 2017 Author Share Posted August 1, 2017 (edited) Notes on modifying San Andreas 010 templates to parse mobile saves: I'm gathering the info I need for a multi-version SA template. Block 0 - Misc: All messed up. I'll come back to this. Block 1 - Script: Extra 4 bytes (int?) at +8. short index; /* Script Thread */ int pNext <format=hex>, pPrev <format=hex>; int mobileStuff0; //mobile Mobile threads have 40 local variables. int locals[40]; //mobile There is extra information at the end of this block. I'm not sure what it does but I think I've managed to map it's structure well enough to handle most 1.06+ saves with the following changes to the end of block1's structure. int mobileStuff1; //mobile tThread threads[nRunningThreads]; WORD unknownMobileCount; WORD unknownMobileA; WORD unknownMobileB; struct tUnknownMobileStruct { int unknownMobile; WORD unknownMobile1; WORD unknownMobile2; WORD unknownMobile3; } unknownMobileStruct[unknownMobileCount];} block1; Block 2 - Player and Objects Modified tPlayer struct struct tPlayer{ DWORD handle; int modelID; int pedType; DWORD _size1<format=hex>; /*0x18C*/ RwV3D pos; int unknown_mobileP; //mobile float health, armor; tWeapon weapons[13]; byte ref; byte weaponSlot; byte unknown; byte __gap1[13]; int unknown_enex; byte unknown_melee1; byte unknown_melee2; byte __gap2[10]; DWORD _size2<format=hex>; /*0x84*/ int chaos; byte wantedLevel; DWORD texturesCRC[28]<format=hex>; byte __gap3[3]; float fat, muscle; DWORD __gap4; int mobileJunk[6]; //mobile}; Modified Object Struct struct tObject{ DWORD handle; int modelID; DWORD _size; /*52*/ RwV3D pos; // 3 floats byte pos_right[3]; byte pos_top[3]; int unknownModel0 <format=hex>; int unknownModel1 <format=hex>; byte unknown[6]; int entityFlags <format=hex>; int unknownModel2 <format=hex>; int objectFlags <format=hex>; byte bObjectType; byte bColDamageEffect; byte bStoredColDamageEffect; byte physicalFlags <format=hex>; WORD unknownMobile1; //mobile only (swap these for iOS) WORD unknownMobile2; //mobile only (last in Android)}; Block 9 - Markers They added more markers in the 1.06 patch. // Block 9: Markersstruct tBlock09_Markers{ tblockHeader header; tMarker markers[250]; // mobile} block9; Block 23 - Gangwars An extra byte at end. byte unknown4mobile; // mobile} block23; Edited August 1, 2017 by OrionSR Link to comment Share on other sites More sharing options...
OrionSR Posted August 7, 2017 Author Share Posted August 7, 2017 (edited) I'm trying to identify and document the fields of a "player car" structure unique to SA Mobile quick saves and auto-saves. I'm hoping that if I can fill in enough details that the structure's elements might start to look familiar to people more experienced with this type of data. The player car structure is located at +580 within the player struct of the Player and Objects block. The structure length is 176 bytes for vehicles without passenger seats. Vehicles that can support passengers have an additional 24 byte structure, which throws off the offsets at the end. +0 dword vehicleHandle, unconfirmed +4 dword modelID +8 dword size of following data without optional structures +12 72 bytes struct CMatrix +84 byte radioStation (enum?) +85 4 bytes colors +89 (align) +90 word alarmState +92 byte nPassengers seats available +96 float wheelState +100 float gasPedalState +104 float breakPedalState +108 8 bytes vehicleFlags +116 float health +120 int doorLock +124 4 bytes physicalFlags +126 byte immunity flags +128 float mass +132 float turnmass +136 float buoyancy +140 24 bytes optional damageManager struct +140 2 bytes struct trains +140 driverHandle +144 8 dwords passengerHandles The offsets above have been updated with available information. The screenshots below were created with early guesses. BMX and Camper: Expanded Rotations Properties not saved in the player car structure are: mod parts, nitro, hydraulics, paintjobs, extra part variations, bombs, and custom plates. I don't expect to find handling flags if hydraulics don't save. Edited August 9, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted August 7, 2017 Share Posted August 7, 2017 Additions to the structure I managed to find. +12 CMatrix (geometrical description of vehicle position in the world, three vectors and position (all with dummy 4 bytes after three floats), and 2 more zeros after it (unused in saved matrix), structure is 72 bytes) +90 (2 bytes) alarm state +96 (float) wheel state +100 (float) gas pedal state +104 (float) break pedal state +108 (8 bytes) vehicle flags +120 (int) [most likely 'projectile fire time' or 'door lock', I would say the latter, can't give exact answer because of minor vehicle structure shift in mobile version] +124 (4 bytes) physical flags +136 (float) buoyancy? [optional fields] +140 (for automobiles only) (CDamageManager) damaged state, 24 bytes +140 (for trains only) unknown 2 bytes value; if train is a mission train, followed by value of CTrain::bDisableRandomTrains (1 byte) [end of optional fields] +140 (+offset) (int) driver handle +144 (+offset) (int[8]) passengers handles OrionSR 1 Link to comment Share on other sites More sharing options...
OrionSR Posted August 8, 2017 Author Share Posted August 8, 2017 (edited) Trains? I doubt I would have thought of that. Thanks. But that means my template is still broken. I'm not sure if these are the same question but... Have you seen any sign of an IS_TRAIN flag? Are the vehicle flags (and physical flags) well documented in other structures? Alarm State: a word? or state 0 and state 1? Um... okay. It'll take a while to fill in the fields. I'll hold anymore questions until then. No, wait. Damage manager? Okay, that makes sense but automobiles only? I'll recheck on a motorcycle but I thought I checked this on an NRG. My thought was the bicycles don't have this structure, which makes sense since bicycles don't take damage unless they flip perfectly and explode. I'm not sure how to flag this structure if nPassengers doesn't hold true. I think I'll take another look at your vehicle flags and see if I can align anything with the modelFlags in handling.cfg. Edit 1) Dummy data in CMatrix: There is definitely data in these floats in a couple of these 100% SnP saves - BMX and NRG-500. It looks like the player was getting 100% from the Chiliad Challenge and the game saved while the bike was in motion. However, the vehicle doesn't seem to be moving during the fade-in from loading. Edit 2) +120 confirmed as door lock. I locked the door with my editor and tweaked the ped handle so CJ would spawn on top of the camper. It should have been an easy test but the ped handle I chose put a driver in the car and she drove off when CJ jumped off the roof. So... driverHandle at +140(+offset) has been confirmed as well. CJ is driving if driverHandle == .player.handle, or -1. +136 float values for: Coach. 84.44444 Greenwood, 18.28572 Brown Streak, 48.88889 Camper, 17.88235 Tampa, 19.42857 MtBike & BMX, 0.776699 +89 byte, align? (unused bytes that pad memory so the next word. dword or structure can start on a doubly-even offset) Edited August 8, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted August 8, 2017 Share Posted August 8, 2017 Dummy data in CMatrix: There is definitely data in these floats in a couple of these 100% SnP saves - BMX and NRG-500. It looks like the player was getting 100% from the Chiliad Challenge and the game saved while the bike was in motion. However, the vehicle doesn't seem to be moving during the fade-in from loading. Oh right, you are correct, first 4 bytes (between first two vectors, so at +24) represent flags. I don't know their meaning, however. Trains? I doubt I would have thought of that. Thanks. But that means my template is still broken. I'm not sure if these are the same question but... Have you seen any sign of an IS_TRAIN flag? Are the vehicle flags (and physical flags) well documented in other structures? Game uses "type" column from vehicles.ide to identify vehicle as a train. Besides general model info data, which holds this information, each vehicle contains a vehicle type in its memory structure. Vehicle flags are described here: https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CVehicle.h (starting with 'unsigned char bIsLawEnforcer : 1; // Is this guy chasing the player at the moment') Physical flags are described here: https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CPhysical.h (starting with 'unsigned int b01 : 1;') I have minor additions to it: b01 => bIsHeavy bInvulnerable => bIsOnlyDamagedByPlayer I also have notes about b17 that it is calculated during collision testing and that it probably affects how collision should be processed this frame. Alarm State: a word? or state 0 and state 1? A word. -1 for "car has alarm system", values >=0 mean 'how many milliseconds remaining for alarm to be active' (when alarm is triggered this value is set to 15000). No, wait. Damage manager? Okay, that makes sense but automobiles only? I'll recheck on a motorcycle but I thought I checked this on an NRG. My thought was the bicycles don't have this structure, which makes sense since bicycles don't take damage unless they flip perfectly and explode. I'm not sure how to flag this structure if nPassengers doesn't hold true. I think I'll take another look at your vehicle flags and see if I can align anything with the modelFlags in handling.cfg. To be more certain, automobiles and everything derived from them (helis, monster trucks, planes, quadbikes and trailers). Bikes should not have this structure. This structure is described here: https://github.com/DK22Pac/plugin-sdk/blob/master/plugin_sa/game_sa/CDamageManager.h +89 byte, align? (unused bytes that pad memory so the next word. dword or structure can start on a doubly-even offset) Alignment, yes. +136 float values for: Coach. 84.44444 Greenwood, 18.28572 Brown Streak, 48.88889 Camper, 17.88235 Tampa, 19.42857 MtBike & BMX, 0.776699 Buoyancy is calculated as B = m * 0.8 / ps where m is mass of vehicle and ps is 'percent submerged' in handling data. Values you provide seem to correspond to this formula. OrionSR 1 Link to comment Share on other sites More sharing options...
OrionSR Posted August 8, 2017 Author Share Posted August 8, 2017 (edited) Thanks again. I won't be able to investigate this until after work but, when I saved a Brown Streak I had it running in full speed reverse but that dummy data was still blank. So, maybe something specific to a MtBike. Brake pedal state was active though. Updated:______________________________________________________________________ As expected, an NRG broke my script. I'll get to work If I understand correctly, there's nothing in the save preceding the option fields to indicate vehicle type other than model IDs. So if I want to find the type properly then I need to parse vehicle.ide for use by my template. I may consider that later but for now I think I'll consider the automobile structure as default and run options for motorcycles and bicycles), and train models. I guess I'll learn more as I dig through the file. trains: 590 freibox569 freiflat537 freight538 streak570 streakc449 trambikes: 581 bf400523 copbike462 faggio521 fcr900463 freeway522 nrg500461 pcj600448 pizzaboy468 sanchez586 wayfarerbmx: 481 bmx509 bike510 mtbikeboat: 472 coastg473 dinghy493 jetmax595 launch484 marquis430 predator453 reefer452 speeder446 squalo454 tropic Damn. It'd be so much easier if it just saved these flags instead of that other useless junk. Edited August 9, 2017 by OrionSR Link to comment Share on other sites More sharing options...
OrionSR Posted August 9, 2017 Author Share Posted August 9, 2017 (edited) Can anyone explain this ped/object/weapon structure that was tacked onto the end of the script block? I've got it mapped out, but I don't understand what I'm reading well enough to label much of anything. What's not shown in the screenshots is that the models tend to be added in reverse. Mobile2[0] is 75 (max records) if there is only one record in the structure, and 74 if there are two. MobileA = Mobile2[0] - 1. if (isMobile == 1) { WORD MobileCount; // nRecords WORD MobileA; // minIndex WORD MobileB; // struct tMobileStruct { struct tModel { int modelID; // modelID WORD Mobile1; // type? WORD Mobile2; // index? [string length] WORD Mobile3; // maxIndex? if (modelID >= 290 && 299 >= modelID) { char modelName[Mobile2]; int MobileInt <format=hex>; //handle? } } Model[MobileCount] <optimize=false>; } MobileStruct; } So... objects and weapons and ped (oh my). I've got it mapped and don't know what it does. I've read reports of extra Denise's hanging around after save exploits but I haven't looked into it yet. Any suggestions? Edited August 9, 2017 by OrionSR Link to comment Share on other sites More sharing options...
OrionSR Posted August 9, 2017 Author Share Posted August 9, 2017 (edited) Description of struct CMatrix (72 bytes): I'm having a difficult time sorting out how I want to rework and describe the CMatrix structure. My thought is to organize most of the data into 4 structs of 4 floats, currently described as X, Y, Z and dummy. Description for structs would be: +0 struct LevelToGround +16 struct LookingDirection +32 struct dymamic flight data? +48 struct position +64 dword unknownA +68 dword unknownB +72 (end) I'm not at all confident in any of the descriptions besides position and (end). Edited August 9, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted August 9, 2017 Share Posted August 9, 2017 (edited) This mobile structure is generated by CScriptResourceManager::Save(). I documented this structure (for PC version) as follows: struct CScriptResourceManager{ CScriptResourceEntry m_asResources[75];};struct CScriptResourceEntry{ int m_nResourceID; CRunningScript *m_pScript; short m_wType; char gap_10[2];};This structure holds information about which resources were requested. Following opcodes register resource in manager: Type 1 (animation - values starting with 25575) REQUEST_ANIMATION [+] REMOVE_ANIMATION [-] Type 2 (model - model ids; special chars have models 290-299) LOAD_SPECIAL_CHARACTER [+] REQUEST_MODEL [+] MARK_MODEL_AS_NO_LONGER_NEEDED [-] UNLOAD_SPECIAL_CHARACTER [-] Type 3 (decision maker - likely to be high values) COPY_SHARED_CHAR_DECISION_MAKER [+] REMOVE_DECISION_MAKER [-] (command IDs and opcode names correspond as here: http://pastebin.com/raw/kCwS6rdG) If I understand correctly, this is how (probably) it should be stored in a save: WORD count;struct ResourceManager { WORD index; //index in manager array WORD scriptIndex; //index of a script int resourceID; WORD type; if (type == 2 && resourceID >= 290 && resourceID <= 299){ int length; char modelName[length] }} [count]; Edited August 9, 2017 by Nick007J OrionSR 1 Link to comment Share on other sites More sharing options...
OrionSR Posted August 9, 2017 Author Share Posted August 9, 2017 Oh yeah. That's what I was watching when you were showing how the dealers spawn. Hm... To align with what I see, maybe... WORD count;struct ResourceManager { int resourceID; WORD type; WORD index; //index in manager array WORD scriptIndex; //index of a script if (type == 2 && resourceID >= 290 && resourceID <= 299){ char modelName[index] int dunno; }} [count]; Using index for length saves me the trouble of creating a local variable length = index. It doesn't make sense grammatically but that's how the data is used here. Type 2 (model - model ids; special chars have models 290-299) LOAD_SPECIAL_CHARACTER [+] REQUEST_MODEL [+] MARK_MODEL_AS_NO_LONGER_NEEDED [-] UNLOAD_SPECIAL_CHARACTER [-] How best to flag resources so they are removed when the save is loaded, or at least not saved again? I'm thinking 0 if that does everything with a [-] designation. Do you have any suggestions for intentionally saving resources? Some saves may have as many as 5 or 6. I was amazed when I came across a save with the resources maxed out at 75. What would that do to a save? Link to comment Share on other sites More sharing options...
Nick007J Posted August 9, 2017 Share Posted August 9, 2017 How best to flag resources so they are removed when the save is loaded, or at least not saved again? I'm thinking 0 if that does everything with a [-] designation. Do you have any suggestions for intentionally saving resources? Some saves may have as many as 5 or 6. I was amazed when I came across a save with the resources maxed out at 75. What would that do to a save? Originally, resource manager is just informational, as far as I understand. It just registers that something was requested or marked as no longer needed. The only time this array is actually being checked is at the end of cutscenes, when presence of a model in the manager may block marking stuff as no longer needed. Presence of many resources may suggest that either mission might be badly programmed, or that one of missions was incorrectly finished (something related to duping, for example), or that a mission is currently running. The manager in mobile version is more useful, most notably allowing saving during missions, so that models are loaded upon reload. Is your first question about how to remove these resources from save? Probably setting type to 0 and resourceID to -1 should do the trick. Link to comment Share on other sites More sharing options...
OrionSR Posted August 10, 2017 Author Share Posted August 10, 2017 (edited) Yes, I'm looking towards strategies to edit the saves; documentation is an important early step. The primary focus is always on glitch detection and repair, but I always have an eye out for strategies to customize saved games. Can the resource manager be responsible for extra Denise's hanging around after dating exploits? Reports suggest that her body will persist if killed. I'm starting to doubt that these issues are related. Removal strategy: Type 0 to force the game to remove the resource, that makes sense. Disabled modelID resourceID, to prevent changes to active resources when the save first loads? Disabling the resourceID will break my template if I run it on this save as the special actors will still have strings. To answer the question I was going to ask: resourceID is a super-set of model ID that also includes animation IDs and decision IDs. Pending an extra Denise save to investigate, the next major save structure is liable to be difficult. Those slot 10 mission saves are larger by 65000 bytes; enough to hold the largest mission. I'm not sure what to expect. I haven't even adjusted my PC template for this purpose. Players can save missions on PC using a basic saveanywhere script that doesn't check for OM0. The saves just won't load unless you put the mission thread to sleep for a while, and that's harder to do with a broken template. (I could use an strategy to kill threads and missions using an editor. I always use cleo for this purpose.) Here's a link to that save with 75 extra player resources. It's a 100% save named Wrong Side of the Tracks, so I would suspect some exploits were being used. Or a mission select mod. I've been able to save one player vehicle but not 2 or 3 like in some of these saves. Edited August 10, 2017 by OrionSR Link to comment Share on other sites More sharing options...
OrionSR Posted August 11, 2017 Author Share Posted August 11, 2017 (edited) My first efforts to adapt my template for the larger slot 10 mission saves have failed miserably. I'll document what I've learned so far as a lead-in to another investigation. If a PC player saves during a mission using a saveanywhere script that doesn't check for OM0 (not that that matters much anymore) the save will crash when loaded, but the glitch can be fixed by putting the thread to sleep and killing it with cleo. These saves load just fine in my template without modification, but it doesn't look like the "mission" gets saved, it's just a "thread" with the properties of the mission. SASE also reads these threads without issue. I tried again to kill an active mission thread on PC without any luck. Setting the wake time to something large keeps it from crashing right away, but tweaking other variables isn;t helping. isActive = 0 loads but cleo scripts don't work so I can't save. isActive = -1 can be saved but the mission thread persists in the save. Fiddling with the short index at the start of the record always produced a crash. I can't think of anything else that might cause the thread to end by randomly fiddling with the settings. Instead I think I'll look for something in the standard scripts that can kill a sleeping thread for me if I rename it properly. Any suggestions? Can anyone explain the "range" of the pointers at the start of the thread structures. I'm trying to estimate the range of possible pointer values for pPrev (+6) and currentIP (+22) of the main thread, which should always be located at a static offset in mobile saves. My hypothesis is that I can detect the difference between iOS and Android pointers. Android uses global and local memory addresses quite differently than PC, leading to huge values for most pointers. pPrev (dword preceding "main"): A2886CF0 - Android 006B59B4 - iOS 00A90590 - PCv1 I'm not sure what to expect from the Remastered version. I have no known examples to work with. And I only have known iOS saves from one source, which isn't helping. Anyone have a save to donate? Anything will do - start a new game, move away from the bike so the safehouse icons can appear, and pause to produce a slot 10 quicksave. Android saves are useful too. I assume most of what I see are Android saves but I can't tell for sure yet. Missions in Mobile Saves From what I can tell the whole mission is saved in-line with the other threads as I see plenty of strings that associate with this mission, and there's a chance that other threads can exist after a mission thread. The structure varies from a thread right away - the thread name is offset by -5 and not even the index looks right. So... stumped again for a while. I think I'll go back to my global variable translation table and look for a what to convert it for 010 so I can finally get my object cleanup scripts working. Added:_________________________________________ Can anyone explain the function of the short index that begins each thread? It seems to be the only hint that a mission is impending in a mobile save. Scanning through thread indexes I find a lot of values in the 0x45 range, give or take a dozen or so, but I haven't worked out a meaning of the values. But when I encounter a mission as the next thread, this example reads as 0x8046. So I'm thinking that 80 byte is flagging it as a mission. The index short is followed by an unknown mobile dword, always -1 as far as I've seen. Then I get 03A4: name_thread 'CRASH1' 0050: gosub and I get lost as I'm not as good at decompiling as Sanny is. How am I supposed to know where the mission ends? I just want to block around it. Edited August 11, 2017 by OrionSR Link to comment Share on other sites More sharing options...
OrionSR Posted August 11, 2017 Author Share Posted August 11, 2017 (edited) SA Mobile Mission Saves - got it working for a single example.Mission saves are created by the checkpoint system of some missions, and can also be enabled during other missions by failing and trying again. Mission saves are typically found in slot 10, the slot that is loaded when resuming a game after a fresh start or retying a failed mission. They are easily overwritten by quick-saving while pausing, but can easily be renamed to another slot. Mission saves are 65000 bytes larger than other mobile saves.It doesn't make sense to define saves by their slot or save/load strategy. Any save will work in any slot. Auto-saves are written to and read from slot 9, and quick/checkpoint/resume saves are assigned to slot 10. Manage the slots to best suit your needs.Before making changes to accommodate a mission thread, I had to move my unknown mobileStuff0 int before the pointers. To get the normal threads fixed. Then, if the index is negative then reserve 73096 bytes for mission code. I suspect there's stuff besides just code in this data, like local variables, but for now it's good enough for testing.Update: I found the locals hiding behind the couch. 69000 bytes for mission code as documented elsewhere, and 1024 dwords for local variables. They're all empty though, except for a nominal value in the timers. struct tThread{ short index; /* Script Thread */ if (isMobile==1) int mobileStuff0 <format=hex>; //mobile if (index < 0) missionThread = 1; if (index < 0) byte missionCode[69000]; if (index < 0) DWORD localvar[1024]; int pNext <format=hex>, pPrev <format=hex>; char name[8]; int baseIP <format=hex>; int currentIP <format=hex>; If there's a missionThread then an 8 byte character field for the mission name is created at the end of all structures, follow by an unknown dword, and then the start of the next BLOCK begings. Update: The character field for the mission name isn't holding true for other missions. 8 bytes are still required to round out the structure for mission saves but I'm not sure what these bytes are for. if (missionThread == 1) { char missionName[8]; int uMobile1; } }} Script;[size=4][/size] Edited August 11, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted August 11, 2017 Share Posted August 11, 2017 (edited) int mobileStuff0 is an index of a streamed script, if saved script is streamed. I am not sure if they can actually be saved (they certainly are not on PC). There is a variable before block with mission name and "uMobile1", which is a value of "IsOddJob", which is set to 1 if a command CHECKPOINT_SAVE_ODDJOB (0A77) [with an exception of "crane2" in combination with OM1] has been executed before mission cleanup; or by command SCRIPT_NAME (03A4) with parameter "copcar", "ambulan", "firetruck" or "stunt". It being set to 0 + save having a mission script causes these two fields to be added. uMobile1 is a value of a variable "missionReplaySetting". It is set to: - 0 by CHECKPOINT_SAVE_ODDJOB (0A77) [same exception applies as above], - 0 by LOAD_AND_LAUNCH_MISSION_INTERNAL (0417) - 1 by CHECKPOINT_SAVE (0A6F) [unless first parameter is 99], - 1 by SCRIPT_NAME (03A4) if current script is mission script, not a streamed script, currently on OM1 and IsOddJob == 0. 'missionName' is actually a name of mission text (LOAD_MISSION_TEXT, 054C), not the script itself (e.g. mission name of Management Issues is 'MUSIC3' and of mission text 'STRAP3'). As you correctly determined, game intentionally sets a bit in an index to mark script as a mission script (game checks by logical and with 0x8000 but checking sign does the same). Edited August 11, 2017 by Nick007J OrionSR 1 Link to comment Share on other sites More sharing options...
OrionSR Posted August 11, 2017 Author Share Posted August 11, 2017 (edited) Ah, and there's that other structure we renamed in the topic but it never propagated to the script. Search and Replace: uMobile1 with IsOddJob missionName with missionText local missionThread with isMissionThread What is the proper form of the logical and with 0x8000? I haven't performed that operation yet. if (isMissionThread == 1) { char missionText[8]; int isOddJob; } The isMissionThread evaluation occurs at a lower level of the template so I can't query the index directly. I prefer to limit my locals if I can. As it turned out, I don't need to track isSlot10 (large mobile save). isMissionThread should be true if any of the threads were mission threads. I'll review the logic in your description but the primary question is, Is if (isMissionThread == 1) a complete evaluation? Are the Mission Code + Local Array and Mission Text + OddJob structures always paired or can I get some parts without the others? Edited August 11, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted August 11, 2017 Share Posted August 11, 2017 (edited) You probably misunderstood me (or I misunderstood the code). Following mission text there is "missionReplaySetting". "IsOddJob" is a variable (4 bytes) that preceeds this block. So, if I understand it right, it should be int IsOddJob;if (isMissionThread == 1 && IsOddJob == 0){ char missionText[8]; int missionReplaySetting;} Edited August 11, 2017 by Nick007J Link to comment Share on other sites More sharing options...
OrionSR Posted August 11, 2017 Author Share Posted August 11, 2017 (edited) Ah. I think I found it. if (isMobile == 1) int mobileStuff1; //mobile tThread threads[nRunningThreads] <optimize=false>; So mobileStuff1 with isOddJob and missionReplaySettings fixed. How to test it? And then I'm into the resource manager that never got updated in my template. I've got a lot of cleanup work to do but overall structure is the priority. Thanks again for the support. I doubt I would have found the odd job stuff by experimentation. Edited August 11, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted August 11, 2017 Share Posted August 11, 2017 (edited) Hm, I would expect this variable to be right after resource manager and certainly after scripts. The variable before threads array is called SaveGameStateType. Possible values are: 0 - done by CHECKPOINT_SAVE (0A6F) 1 - done by AUTO_SAVE (0A72) 2 - done by TERMINATE_THIS_SCRIPT (004E) 3 - done by OS_ApplicationEvent (possible when 'close app' event is triggered?) 4 - done by CHECKPOINT_SAVE_ODDJOB (0A77) and function CPad::CheckPointSave() [hard to say what latter one is, but it seems to be triggered when player is in a vehicle, and there are checks for model ids 537, 538 and 570) 5 - done by SCRIPT_NAME (03A4) Edited August 11, 2017 by Nick007J Link to comment Share on other sites More sharing options...
OrionSR Posted August 11, 2017 Author Share Posted August 11, 2017 (edited) Okay, I was hoping to clean up the resource manager struct first but for now, here's a wider view on the structure. int nRunningThreads; if (isMobile == 1) int SaveGameStateType; //mobile tThread threads[nRunningThreads] <optimize=false>; if (isMobile == 1) { WORD MobileCount; // nRecords WORD MobileA; // minIndex WORD MobileB; // maxIndex <= unexplained data struct tMobileStruct (snipped resource manager) if (isMissionThread == 1) {// if (isMissionThread == 1 && isOddJob == 0) { char missionText[8]; int missionReplaySetting; } }} Script; // end of block It looks like the missionText string is often full of garbage data. MobileB looks like the most likely candidate. MobileA was usually consistent with the next available record index. It looks like I was never able to align your predictions for the resource manager structure with what I saw in the saves. Edited August 11, 2017 by OrionSR Link to comment Share on other sites More sharing options...
Nick007J Posted August 11, 2017 Share Posted August 11, 2017 (edited) Based on function CTheScripts::Save() in mobile version, this is what I would expect from a mobile save in this block. int sizeOfVars;char scriptSpace[*]; //* this block seems to be in chunks of 51200 bytes in PC version, and in chunks of 65000 bytes in mobile version, e.g. formally if sizeOfVars is 63000, the size should be 102400 on PC, and 65000 in mobiletScriptForBrains scriptsForBrains[70]; (sizeof(tScriptForBrains) == 20)int CTheScripts::OnAMissionFlag;int CTheScripts::LastMissionPassedTime;t2 CTheScripts::BuildingSwapArray[25] (t2: int type (2 or 0), int handle, int newModel, int oldModel)t3 CTheScripts::InvisibilitySettingArray[20]; (t3: int type, int handle)int CTheScripts::VehicleModelsBlockedByScript[20];t4 CTheScripts::ScriptConnectLodsObjects[10] (t4: int objectId, int lodId)t5 CTheScripts::ScriptAttachedAnimGroups[8] (t5: int model, char name[16])char CTheScripts::bUsingAMultiScriptFile;char CTheScripts::bPlayerHasMetDebbieHarry;int CTheScripts::MainScriptSize;int CTheScripts::LargestMissionScriptSize;short CTheScripts::NumberOfMissionScripts;short CTheScripts::NumberOfExclusiveMissionScripts;int CTheScripts::LargestNumberOfMissionScriptLocalVariables;int SaveGameStateType; //mobile only!int totalActiveScripts;CRunningScript scripts[totalActiveScripts];-- PC save block ends here --short resourcesCount;tResourceManagerEntry resources[resourcesCount];int IsOddJob;if (IsOddJob != 1 && isMissionThread == 1){ char missionText[8]; int missionReplaySetting;}struct tResourceManagerEntry{ WORD index; //index in manager array WORD scriptIndex; //index of a script int resourceID; WORD type; if (type == 2 && resourceID >= 290 && resourceID <= 299){ int length; char modelName[length] }}Of course, I may be wrong. Edited August 11, 2017 by Nick007J OrionSR 1 Link to comment Share on other sites More sharing options...
OrionSR Posted August 11, 2017 Author Share Posted August 11, 2017 I wonder if I can replace a missionThread with something of my own, and execute custom cleo-like missions on iOS and Remastered versions. Thanks again. I've got a busy work weekend so progress may slow for a bit. But I'll review what I've got and try to get things updated for another round. In case you missed my added comment, please review my reply to your comments on the resource manager. Link to comment Share on other sites More sharing options...
Nick007J Posted August 11, 2017 Share Posted August 11, 2017 (edited) Do you mean different scructure than what I posted now? I may be checking too old (too new) version, maybe it was changed at some point? Because what I see is that game clearly writes length of a string, followed by string itself, and in that particular order. I will try to find different android versions to check. Edit: Checked 1.01 and 1.08, in both special model block is saved as 4 bytes of length of string + the string itself. Weird. Edit 2: I modified your template into following structure (sorry for bad names, just wanted to try): if (isMobile == 1) { WORD MobileCount; // nRecords struct tMobileStruct { struct tModel { WORD MobileA; // minIndex WORD MobileB; // maxIndex int Mobile; // modelID [290 to 299 WORD Mobile1; // type? if (Mobile >= 290 && 299 >= Mobile) { int t; char mobileString[t]; //modelName } } Model[MobileCount] <optimize=false>; } MobileStruct; } int oddjob;and I think successfully opened a savefile you provided earlier. Edited August 11, 2017 by Nick007J Link to comment Share on other sites More sharing options...
OrionSR Posted August 11, 2017 Author Share Posted August 11, 2017 (edited) I am unaware of any changes to the save structure since v1.06. Anything before that is so cluttered with garbage bytes that revising the template to compensate is a hopeless proposition. It seems that we are out of sync with expectations and observations. I'm not even sure what structures were taking about now. On some occasions I find it useful to seed unique data into the save and observe where it ends up in memory. Or the other way around. Full Script Struct. // Block 1: Scriptstruct tBlock01{ tblockHeader header; int varSpaceSize; int globalVar[varSpaceSize/4]; tExternalScriptTrigger trigger[70]; int onMissionFlagOffset; int lastMissionTime; struct tStaticReplacement { DWORD type; /*always 2*/ int handle; int newModelID, oldModelID; } staticReplacement[25]; struct tInvisibleObject { int type, handle; } invisibleObject[20]; int disabledCarparkingModels[20]; struct tLodAssignment { int objHandle, lodHandle; } LodAssignment[10]; struct tScriptAssignment { int actorModelID; char scriptName[8]; DWORD unknown[2]; } ScriptAssignment[8]; byte unknown1, unknown2; int scmMainSize; int largestMissionSize; int missionsNumber; int highestLocalVariable; int nRunningThreads; if (isMobile == 1) int SaveGameStateType; //mobile tThread threads[nRunningThreads] <optimize=false>; if (isMobile == 1) { WORD MobileCount; // nRecords WORD MobileA; // minIndex WORD MobileB; // struct tMobileStruct { struct tModel { int modelID; // modelID WORD Mobile1; // type? WORD Mobile2; // index? [string length] WORD Mobile3; // maxIndex? if (modelID >= 290 && 299 >= modelID) { char modelName[Mobile2]; int MobileInt <format=hex>; //handle? } } Model[MobileCount] <optimize=false>; } MobileStruct; if (isMissionThread == 1) {// if (isMissionThread == 1 && isOddJob == 0) { char missionText[8]; int missionReplaySetting; } }} Script; Edited August 11, 2017 by OrionSR Link to comment Share on other sites More sharing options...