一键导入
hamsterball-re
Reverse engineer and recreate the Hamsterball game (2000s Windows game by Raptisoft)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Reverse engineer and recreate the Hamsterball game (2000s Windows game by Raptisoft)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Start GhidraMCP headless server with the Hamsterball.exe project — always do this before any RE work
Restore all function renames from FUNCTION_MAP.md back into a fresh Ghidra project after DB loss or re-import
Complete guide to using and extending Hermes Agent — CLI usage, setup, configuration, spawning additional agents, gateway platforms, skills, voice, tools, profiles, and a concise contributor reference. Load this skill when helping users configure Hermes, troubleshoot issues, spawn agent instances, or make code contributions.
Create hand-drawn style diagrams using Excalidraw JSON format. Generate .excalidraw files for architecture diagrams, flowcharts, sequence diagrams, concept maps, and more. Files can be opened at excalidraw.com or uploaded for shareable links.
54 production-quality design systems extracted from real websites. Load a template to generate HTML/CSS that matches the visual identity of sites like Stripe, Linear, Vercel, Notion, Airbnb, and more. Each template includes colors, typography, components, layout rules, and ready-to-use CSS values.
Cross-compile D3D8-based game with MinGW, test on Wine with OpenGL translation
| name | hamsterball-re |
| description | Reverse engineer and recreate the Hamsterball game (2000s Windows game by Raptisoft) |
| tags | ["reverse-engineering","gaming","binary-analysis","c-reimplementation"] |
Reverse engineer and recreate the Hamsterball game (2000s Windows game by Raptisoft).
Engine Name Note: The binary only contains one reference to "Athena" — the Win32 window class name "AthenaWindow" at 0x4D9374. There is no "Athena Engine" string, no about box, no internal branding. "Athena" as the engine name is derived from the window class. "Raptisoft" is the company name (Nick Raptis), not the engine name. Use "Athena" as the working name for namespace prefixes (AthenaString, AthenaList, etc.).
How to prove engine names from window class strings: In Win32 games, WNDCLASSEX.lpszClassName is set during window registration and traditionally carries the engine name. Proof from open-source Quake 3 Arena (code/win32/win_glimp.c): #define WINDOW_CLASS_NAME "Quake 3: Arena" followed by wc.lpszClassName = WINDOW_CLASS_NAME; RegisterClass(&wc);. Similarly, Half-Life/Source uses "Valve001", Unity uses "UnityWndClass", GameMaker uses "YYGODragonWindow". These class names are developer-chosen identifiers visible via Spy++ or GetClassName(). In Hamsterball, at 0x46D218 the code does mov [esp+0x30], 0x4D9374 ("AthenaWindow") as lpszClassName before calling RegisterClassExA. This is standard practice — the window class name IS the engine identifier.
| Address | Function | Notes |
|---|---|---|
| 0x4BB4C8 | entry | CRT entry point (GetVersionEx, heap init) |
| 0x4278E0 | WinMain | Calls App_InitFull→App_Run→App_Shutdown |
| 0x429530 | App_Initialize_Full | 26 init steps, debug strings at this+0x208 |
| 0x46BB40 | App_Initialize | 12-step base init (vtable calls + D3D8) |
| 0x46BD80 | App_Run | Game loop: PeekMessage→Update→Render→Present |
| 0x46BA10 | App_Shutdown | Sets this->running=1, vtable jump |
| 0x455380 | Graphics_Initialize | Calls Direct3DCreate8, device creation |
| 0x455A60 | Graphics_Defaults | Set default render states |
| 0x453B50 | Graphics_BeginFrame | Begin frame/render setup |
| 0x455A90 | Graphics_PresentOrEnd | Present frame or end scene |
| 0x45DE30 | LoadMeshWorld | Load .meshworld level file |
| 0x4015b0 | Ball_SetupCollisionRender | Init collision mesh render objects from level data |
| 0x4016f0 | Ball_ApplyForceV2 | Alternate force application (gravity plane, ice/dizzy/tube) |
| 0x402c10 | Ball_RenderWithCollision | Ball render with collision plane check + shadow |
| 0x4280e0 | App_ShowMainMenu | Create MainMenu (0xCDC bytes), store at App+0x224 |
| 0x4288b0 | App_StartTournamentRace | Start tournament: config mirror, create scene, advance race |
| 0x446b80 | RegisterDialog_ValidateSerial | Validate serial via XOR cipher key "54138", registry write |
| 0x459660 | Sound_LoadOggOrWav | Load sound: try .ogg first, then .wav fallback |
| 0x459310 | Sound_LoadOgg | Load OGG Vorbis file into D3D sound buffer |
| 0x459810 | Sound_GetNextChannel | Circular buffer sound channel allocator |
| 0x4706E0 | MeshWorld_ctor | Constructor (0x488 bytes), vtable at 0x4D9CDC |
| 0x470930 | MeshWorld_Parse | Parse text format (*MATERIAL, *MESH tokens) |
| 0x46A020 | LoadMusicFile | BASS_MusicLoad wrapper |
| 0x46A4D0 | LoadJukebox | Parse jukebox.xml |
| 0x42DE50 | MainMenu_ctor | Main menu (LET'S PLAY, HIGH SCORES, OPTIONS, CREDITS, EXIT) |
| 0x442CE0 | OptionsMenu_ctor | Options screen (Resolution, Fullscreen, Color, Volume, Key Remap, Mouse) |
| 0x42B470 | HighScoreEntry_ctor | High score entry screen (name input + score display) |
| 0x42BD40 | HighScoreEntry_Render | Render high score entry UI |
| 0x42E060 | GameSelectionScreen_ctor | Tournament difficulty selector screen |
| 0x44FD60 | SaveTourneyDialog_ctor | Save tournament dialog |
| 0x4476B0 | RegisterDialog_ctor | Register/purchase dialog |
| 0x4652E0 | CollisionLevel_ctor | Collision-only level (.meshcollision format) |
| 0x465260 | Level_LoadCollision | Load binary collision mesh (planes, objects, AABB) |
| 0x4624C0 | Level_Cleanup | Level destructor (free objects, VBs, textures) |
| 0x413C20 | ArenaLevel_WarmUp_Init | Initialize Warm-Up arena (levels\arena-WarmUp) |
| 0x416F40 | ArenaLevel_Neon_Init | Initialize Neon arena (levels\arena-neon) |
| 0x456E20 | Font_MeasureText | Measure text string width for centering |
| 0x457440 | Font_DrawGlyph | Core glyph rendering (1 call = 1 glyph quad) |
| 0x4013A0 | UI_DrawTextCenteredAbsolute | Draw centered text (x - width/2) |
| 0x409C60 | UI_DrawTextCentered | Draw centered text with shadow |
| 0x4012C0 | UI_DrawTextShadow | Draw text with shadow (offset + main) |
| 0x40A120 | LoadRaceData | Parse racedata.xml |
| 0x413280 | CameraLookAt | Camera look-at targets (CAMERALOOKAT) |
| 0x429200 | ESellerate_Init | eSellerate DRM init |
| 0x4254E0 | CreditsScreen_ctor | Credits scrolling screen (formerly mislabeled Physics_Init) |
| 0x469CF0 | GameUpdate | Main game tick - iterates objects, calls vtable+4 (Update) and vtable+0x3C (Render) |
| 0x428160 | PauseGame | Pause game (RightButtonPause) |
| 0x42FAD0 | QuitRace | Quit current race |
| 0x4298C0 | TimerDisplay | Race timer display (timerblot.png) |
| 0x40FA20 | CreateBumper | BUMPER1/2/3/4 objects |
| 0x40E250 | CreateExpertLevelObjects | SAWBLADE objects |
| 0x4117B0 | CreateUpLevelObjects | SPEEDCYLINDER |
| 0x412850 | HandleArenaCollisionEvents | N:SPINNER |
| 0x410D00 | NeonCollisionEvents | Neon board collision handler (E:PEGS, E:HEATON, E:LIMIT, etc.) |
| 0x40BF50 | CreateMouseTrap | MOUSETRAP |
| 0x40C5D0 | DispatchCollisionEvents | Main collision event dispatcher — handles ALL object types: E:NODIZZY, E:SAFESWITCH, E:LIMIT, E:BREAK, E:JUMP, E:ACTION (ONCE/SCORE), E:TRAJECTORY, N:NOCONTROL, N:WATER, N:TARPIT, N:GOAL, N:MOUSETRAP, N:SECRET, N:UNLOCKSECRET, DROPIN, PIPEBONK, POPOUT |
| 0x40E6A0 | ExpertCollisionEvents | Expert board events: E:CALLHAMMER, E:HAMMERCHASE, E:ALERTSAW1/2, E:ACTIVATESAW1/2, E:ALERTJUDGES, E:SCORE, E:JUMP, E:BELL (+delegates to DispatchCollisionEvents) |
| 0x40DCD0 | TowerCollisionEvents | Tower board events: E:CATAPULTBOTTOM, E:OPENSESAME, N:TRAPDOOR, E:BITE, E:MACETRIGGER, N:MACE (+delegates to DispatchCollisionEvents) |
| 0x434770 | Saw_AlertActivate | Saw blade alert mode (clear flag + 3D sound) |
| 0x434A50 | Saw_Activate | Saw blade full activate (set flag + 3D sound) |
| 0x434C40 | Judge_Reset | Reset judge objects |
| 0x434C80 | ScoreDisplay_SetTime | Set score display time value |
| 0x434E20 | Bell_Activate | Activate bell (+5 seconds extra time) |
| 0x438BB0 | Hammer_ChaseStart | Start hammer chase sequence |
| 0x434290 | Catapult_Launch | Catapult launch (set active + timer) |
| 0x4344D0 | Trapdoor_Open | Open trapdoor (scale 0→1.0) |
| 0x438410 | Trapdoor_Activate | Activate trapdoor (3D sound + timer) |
| 0x459860 | Sound_Play3D | Play 3D positioned sound (set BASS pos + play) |
| 0x4597B0 | Sound_PlayChannel | Play sound channel (pool dispatch) |
| 0x4595B0 | Sound_StartSample | Start BASS sample (vtable: reset, volume, 3D pos) |
| 0x46EC30 | Ball_GetInputForce | 3 |
| 0x466750 | Sound_CalculateDistanceAttenuation | 3 |
| 0x44C260 | RaceResultPopup_ctor | Race end popup (rank image + TIME'S UP!/OUT OF TIME!) |
| 0x438B30 | CreateBonkPopup | BONKPOPUP feedback |
| 0x40BAA0 | CreateSecretObjects | Create SECRET and SECRETUNLOCK objects |
| 0x43DFB0 | Secret_ctor | Secret object constructor (0x10EC bytes) |
| 0x4121D0 | CreateLevelObjects | Factory: BRIDGE, TIPPER, BONK, BBRIDGE1/2, POPCYLINDER, BLOCKDAWG1/2, CATAPULT, GLUEBIE |
| 0x4133E0 | CreatePlatformOrStands | Factory: PLATFORM, STANDS |
| 0x417FE0 | CreateMechanicalObjects | Factory: LOOPER, GEAR, BIGGEAR, ROTATOR, PENDULUM |
| 0x37040 | Popcylinder_ctor | Platform constructor (0x10FC bytes) |
| 0x462850 | Stands_ctor | Stands constructor (0x10D0 bytes) |
| 0x435800 | Looper_ctor | Looper constructor (0x1500 bytes) |
| 0x437590 | Gear_ctor | Gear/BigGear constructor (0x1514 bytes) |
| 0x435940 | Rotator_ctor | Rotator constructor (0x1508 bytes) |
| 0x437700 | Pendulum_ctor | Pendulum constructor (0x1504 bytes) |
| 0x437960 | Tipper_ctor | Tipper constructor (0x1104 bytes) |
| 0x4661A0 | TipperVisual_ctor | Tipper visual component |
| 0x465200 | TipperVisual_Attach | Attach visual to tipper |
| 0x438850 | Bonk_ctor | Bonk (hammer) constructor (0x1200 bytes) |
| 0x436D70 | BreakBridge_ctor | Breakable bridge constructor (0x1100 bytes) |
| 0x436EE0 | PopCylinder_ctor | Pop cylinder constructor (0x10E8 bytes) |
| 0x43C310 | Blockdawg_ctor | Blockdawg constructor (0x1154 bytes) |
| 0x437E10 | Catapult_ctor | Catapult constructor (0x1108 bytes) |
| 0x437CB0 | Gluebie_ctor | Gluebie (glue blob) constructor (0x110C bytes) |
| 0x41D060 | LevelBoard_Dizzy_ctor | BoardLevel3 constructor, vtable at 0x4D0890 |
| 0x40ABA0 | CheckArenaUnlock | Check arena unlock conditions |
| 0x4279F0 | LoadOrSaveConfig | Config load/save dispatcher |
| 0x42AE80 | LoadConfig | Load HS.CFG |
| 0x42B6E0 | SaveConfig | Save HS.CFG |
| 0x433AC0 | GameSelectionManager | Tournament save/load |
| 0x457130 | LoadFont | Load font.description + PNG glyphs |
| 0x429450 | FinishLoad | Final setup after loading |
| 0x46EE10 | Input_Init | DirectInput8 init (0x438 byte object), stored at App+0x180 |
| 0x46C110 | Input_Create | Creates Input object, stores at param+0x180 |
| 0x46F010 | Input_Cleanup | Input object cleanup/release |
| 0x46F0E0 | Input_dtor | Input destructor (delegates to Input_Cleanup) |
| 0x46E250 | KeyboardDevice_ctor | DI keyboard device (0x524 bytes), vtable at 0x4D9840 |
| 0x46DE60 | KeyboardDevice_dtor | Keyboard cleanup, releases DI device, unbinds keys |
| 0x461510 | Level_ctor | Level object constructor (loads MeshWorld, creates LevelState) |
| 0x461460 | LevelState_ctor | Ball/physics state object (0x10D4 bytes) |
| 0x4629C0 | Level_dtor | Level destructor |
| 0x65080 | Level_Clone | Clone level object for secondary display |
| 0x46F310 | Level_base_ctor | Base level init (before vtable set) |
| 0x19030 | Board_ctor | Base Board/Screen class (vtable at 0x4D0260) |
| 0x453210 | AthenaList_Init | Init 256-entry hash list (used everywhere) |
| 0x453280 | AthenaList_Clear | Clear list contents |
| 0x4532B0 | AthenaList_GetIndex | Get list iteration index |
| 0x4534D0 | AthenaList_Remove | Remove item from list |
| 0x453780 | AthenaList_Append | Append item to dynamic list (malloc/realloc) |
| 0x4532E0 | AthenaList_SortedInsert | Insert with insertion-sort (ascending/descending) |
| 0x402BC0 | AthenaList_SetIndex | Set list iteration index |
| 0x40A020 | AthenaList_GetAt | Get element by index with bounds check; returns 0 if OOB |
| Address | Function | Notes |
|---|---|---|
| 0x458f40 | SoundDevice_LoadWAV | Load WAV file→DirectSound buffer. Validates RIFF/WAVE header, "data" chunk, 6 error paths |
| 0x4668a0 | SoundDevice_dtor | Release all DS buffers, save volume to registry, free memory |
| 0x466570 | SoundDevice_ReadVolume | Read Sound Volume float from registry (default 1.0f) |
| Address | Function | Notes |
|---|---|---|
| 0x451df0 | RaceTimer_Tick | Advance frame, adjust speed, accumulate score, determine rank (0-15), load rank sprite |
| 0x44c880 | RaceTimer_ctor | Init timer, score thresholds (100-900), load weasel.png, update high score |
| Address | Function | Notes |
|---|---|---|
| 0x473000 | RegKey_WriteDword | Write REG_DWORD (type 3) to registry key |
| 0x475430 | GameInfo_AddSoundEntry | Add "SOUND" resource entry (0x48 bytes) |
| 0x4752f0 | GameInfo_AddSpriteEntry | Add "SPRITE" resource entry (0x48 bytes) |
| 0x475270 | GameInfo_AddCMeshEntry | Add "BCMESH" collision mesh entry (0x48 bytes) |
| 0x46d91d | GameInfo_WriteDSoundTags | Write DSOUND/CURRENTOBJECT/CURRENTOPERATION tags |
| 0x46d230 | GameInfo_BuildFullReport | Build full diagnostic report (PRODUCT, VERSION, D3D8/9, DSOUND, etc.) |
| 0x4764f0 | D3DXERR_ToString | Convert D3DX error code to human-readable string |
| Address | Function | Notes |
|---|---|---|
| 0x453ed0 | Graphics_ReadQualitySettings | Read Texture Quality, ColorMode, SafeMode from registry |
| 0x453f90 | Graphics_WriteQualitySettings | Write Texture Quality, ColorMode, SafeMode to registry |
| 0x4794d0 | SplashScreen_ctor | Brand logo + raptisoftlogo.png + showcardgothic16 font |
| 0x487070 | FontFormatString_WriteFloat | printf-style float formatting (%f, %g, %e, %a) |
| 0x4819ec | D3DXMesh_AttributeSort | Sort mesh faces by attribute ID, rebuild attribute table |
| 0x444880 | AbortConfirm_Render | "REALLY ABORT?" dialog with YES/NO, score reset warning |
| 0x445410 | RollbackConfirm_Render | "REALLY ROLL BACK?" dialog, score zeroing warning |
| 0x4431e0 | KeyRemapMenu_ctor | Keyboard remap menu (Up/Down/Left/Right/Action1/Action2) |
| 0x474900 | LoaderGadget_OK | LoaderGadget OK handler, set operation name |
| Address | Function | Notes |
|---|---|---|
| 0x401AA0 | Vec3_NormalizeAndScale | 59 xrefs — most common math utility. Normalizes and scales to length param_1. |
| 0x401D60 | Matrix_TransformVec3 | 15 xrefs. Transform 3D vector by 4x3 matrix. |
| 0x402BF0 | Vec3_Copy | 18 xrefs. Copy 3 floats with self-check. |
| 0x40A050 | Color_RandomRGBA | Generate 32-bit color from 4 random bytes. |
| 0x4580D0 | AABB_ContainsPoint | Test if (x,y,z) inside AABB. Used by collision spatial tree. |
| 0x453150 | Matrix_Scale4x4 | Set 4x4 matrix row scale values. |
| 0x453200 | Matrix_Identity | Set matrix to identity. |
| 0x458B50 | Matrix_ScaleTransform | Create 4x4 by scaling source matrix rows. |
| Address | Function | Notes |
|---|---|---|
| 0x4542C0 | Graphics_ctor | Constructor (vtable 0x4D88A0, init render context, texture cache, frustum) |
| 0x455360 | Graphics_dtor | Destructor (cleanup + optional free) |
| 0x454550 | Graphics_Cleanup | Release D3D objects, free texture path, clear cache |
| 0x454000 | Graphics_SetTexturePath | Set custom texture prefix (strdup at +0x7D8) |
| 0x454060 | D3DFMT_ToString | Convert D3DFORMAT enum to debug string |
| 0x454B50 | Graphics_SetViewport | Set viewport dimensions |
| 0x454D30 | Graphics_Reset | Reset device with new params (CreateDevice twice) |
| 0x455D60 | Graphics_DrawScreenRect | Draw 2D rectangle via TLVERTEX tristrip. 63 xrefs. |
| 0x455110 | Graphics_ApplyMaterialAndDraw | Apply material states + draw. 17 xrefs. |
| 0x454190 | Graphics_SetRenderMode | Set shading mode, vertex shader, render states |
| 0x455B80 | Graphics_SetStreamBuffers | Set vertex buffer streams |
| 0x457FA0 | RenderContext_Init | Init 0x50 byte render context (vtable 0x4D8E68) |
| 0x401160 | Graphics_SetViewportClip | Set viewport clip from 4x4 matrix |
| Address | Function | Notes |
|---|---|---|
| 0x403100 | Ball_SetTiltedGravity | Gravity plane=1, normal (-1,0,0) |
| 0x403150 | Ball_SetFlatGravity | Gravity plane=2, normal (0,0,1) |
| 0x403850 | Ball_SetTrajectory | Set trajectory direction + force scale |
| 0x403750 | Ball_ApplyTrajectory | Apply trajectory force (normalize, scale, sound, counter=100) |
| 0x403980 | Ball_FindMeshCollision | Mesh_FindClosestCollision wrapper |
| 0x401DD0 | Ball_CreateTrailParticles | Create trail particles (10 iterations, 0x28 byte objects) |
| 0x401920 | Ball_RenderShadow | Render ball shadow at XYZ |
| 0x401DD0 | Ball_CreateTrailParticles | Create trail particles: 9 iterations, spherical camera-relative distribution using RumbleScore objects, velocity=RNG/(RNG+20), appended to scene+0x3b00 |
| 0x413280 | CameraLookAt | Arena camera init: Load arena-spawnplatform + arena-stands meshes, find CAMERALOOKAT target, set distance=45f, height=800f, max_height=800f |
| 0x419FA0 | Scene_SetCamera | 5 camera modes: Default follow, Path rail (spring+sin dampening, dist<700→0, >700→offset-sin), Shake (±50 random offset), Snap (countdown frames→hard position), Orbit (sin/cos rotation at +0x29BC angle, dist +0x29C0). Ball target at +0x758, actual at +0x76C |
| 0x4284C0 | App_SaveAllConfig | Save all config to registry: MouseSensitivity, MirrorTournament, 11 race unlock flags, 9 arena unlock flags, RightButtonPause, BestTime[0x50 bytes], Medals[0x50 bytes], 2PController1-4. Registry key at App+0x54 |
| 0x4279F0 | LoadOrSaveConfig | Config destructor: delete all resources (graphics, meshes, sounds, UI elements), save config, call ShellExecuteA("http://www.raptisoft.com") if !registered, call App_Shutdown |
| 0x446730 | Tourney_SaveTournament | Save tournament: remove DATA\tournament.sav, call vtable save function |
| 0x446730 | Tourney_SaveTournament | Save tournament: remove DATA\tournament.sav, call vtable save function |
| Address | Function | Notes |
|---|---|---|
| 0x465EF0 | Collision_TraverseSpatialTree | Recursive octree traversal calling AABB test for each face |
| 0x458000 | Collision_InitDefaultAABB | Set AABB bounds to ±39M (0x4b18967f) |
| 0x4583F0 | AABB_TriangleIntersect2 | Double AABB-triangle test (calls AABB_TriangleTest6Edges twice) |
| 0x458190 | Collision_GradientEval_Stub | Empty stub — called from Ball_Update and Gear_AdvanceAlongPath |
| 0x456D80 | CollisionMesh_ctor | CollisionMesh constructor: 0xCB4 bytes, vtable 0x4D8E10. Initializes triangle list (AthenaList at +0x18), material list (AthenaList at +0x430), vertex list (AthenaList at +0x848), position at +0xCA4, scale factors at +0xC64/0xC68 |
| 0x456120 | CollisionMesh_AddTriangle | Add triangle ref to collision mesh: appends to list at +0x430, sets back-pointer at +0x08 |
| 0x4564C0 | Ball_AdvancePositionOrCollision | CORE PHYSICS FUNCTION: Advances ball position with collision detection. Uses SpatialTree for mesh traversal, accumulates velocity (gravity + scale), handles sub-step interpolation with position += direction * (1 - step), collision response via vtable+0x1C callback, material tracking. Offsets: +0x14=hasTrailData, +0x18=trail data, +0x430=triangles, +0x848=vertex normals, +0xC64=scale, +0xC68=friction, +0xC70=max_distance, +0xC74=accumulated_distance, +0xC90-C98=gravity, +0xCA4=position(XYZ), +0xC7C=useGravityCallback flag |
| 0x463330 | SpatialTree_ctor | SpatialTree (octree) constructor for collision spatial partitioning. vtable 0x4D9038. Init values: +0x0C=0.1f (scale), +0x10=6 (max_depth), +0x14=0.9f (0x3F666666, min_extent). Flags +0x18-0x1E=1 (enable all axes) |
| 0x4632E0 | SpatialTree_Free | Free spatial tree leaf nodes + cleanup |
Pattern: Each Board constructor calls Board_ctor, sets vtable, name strings ("Board (Name)"), level unlock flag, Vec3_Init for background color, Matrix_Identity, LoadRaceData, then creates Level_ctor + Level_Clone for split-screen, and sometimes MeshNode_ctor for static geometry.
| Map Case | Level | Board Address | Levels Loaded |
|---|---|---|---|
| 1 | Warm-Up | 0x41CA40 | none (uses existing data) |
| 2 | Intermediate | 0x41CB20 | Level2-Bridge (Intermediate) |
| 3 | Dizzy | 0x41D060 (known) | Level3-Swirl |
| 5 | Tower | 0x41E340 | Level4-Catapult, Level4-Drawbridge, Level4-Mace, Level4-Windmill, Level4-Turret + YellowLink, Chomper meshes |
| 8 | Expert | 0x41EA40 | Level5-Bridge + 3x hammyjudge meshes |
| 9 | Odd | 0x41ED80 | none (single level) |
| 12 | Wobbly | 0x41F110 | Level7-Wobbly1 through Level7-Wobbly7 (7 levels!) |
| Address | Function | Notes |
|---|---|---|
| 0x46E0B0 | Input_IsKeyDown | Multi-device key check (keyboard=0, mouse=1, joystick=3-7) |
| 0x46EC30 | Ball_GetInputForce | Get ball input direction from keyboard/mouse/joystick |
| 0x46EE10 | Input_Init | DirectInput8 init |
| 0x46C110 | Input_Create | Creates Input object, stores at App+0x180 |
| 0x46F010 | Input_Cleanup | Input object cleanup/release |
| Address | Function | Notes |
|---|---|---|
| 0x456390 | Mesh_Clear | Free vertex/index buffers, clear AthenaLists |
| 0x46F340 | MeshBuffer_Cleanup | Cleanup mesh buffer resources, vtable 0x4D9C48 |
| 0x46F670 | Mesh_SaveAndFree | Save mesh data to file (magic 0xBEEF), free buffers |
| 0x46FD60 | Mesh_AddVertex | Add vertex (8 floats: pos+normal+uv), dedup, error "Too many vertices" |
| 0x46C970 | Texture_SetDimensions | Set texture size, compute UV scale factors |
| Address | Function | Notes |
|---|---|---|
| 0x418760 | Scene_CreateObject_Gear | Create GEAR/BigGear object (0x1514 bytes) |
| 0x418930 | Gear_AdvanceAlongPath | 8-direction gradient descent path following |
| Address | Function | Notes |
|---|---|---|
| 0x4143d0 | CreateSpinny | Factory: matches "SPINNY" → Rotator_ctor (0x1508 bytes) |
| 0x414a20 | CreateLifter | Factory: matches "LIFTER" → Lifter_ctor (0x436920, 0x10f4 bytes), falls through to CreatePlatformOrStands |
| 0x415460 | CreateWobbly1 | Factory: matches "WOBBLY1" → GameLevel_ctor (0x1524 bytes), falls through to CreatePlatformOrStands |
| 0x436920 | Lifter_ctor | Lifter object constructor (0x10f4 bytes) |
| 0x413fc0 | ArenaBoard_RenderMultiView | Render 4 dynamic objects with zoom step adjustment |
| 0x4151e0 | ArenaBoard_RenderMultiView5 | Render 5 dynamic objects with zoom step adjustment |
| 0x4155d0 | RumbleBoard_InitScene4 | Init scene objects for 4-player split, assign materials + Level_SetObjectTransform |
| 0x415d90 | RumbleBoard_InitScene5 | Init scene objects for 5-player split, assign materials + Level_SetObjectTransform |
| Address | Function | Notes |
|---|---|---|
| 0x463790 | Vec3_CrossProduct | Cross product: this × param2 → param1 |
| 0x467750 | Array_CopyDWords | Copy N dwords ptr-to-ptr |
| 0x470650 | Array_FillDWords | Fill N dwords with same value |
| 0x472DA0 | Transform_ctor | Transform object constructor |
| 0x4730C0 | Registry_ReadFloat | Read float value from Windows registry |
| 0x473740 | StdString_AppendCharN | Append N copies of a char |
| 0x4738B0 | StdString_AppendN | Append N chars with strncat |
| 0x473B10 | AthenaString_AssignFormatted | Format and assign AthenaString |
| Address | Function | Notes |
|---|---|---|
| 0x456150 | Ray_SetDirection | Set ray direction, normalize, compute length |
| 0x456E80 | Font_WordWrap | Word-wrap text to fit pixel width |
| 0x459610 | Scene_RenderIfVisible | Render scene object only if visible flag set |
| 0x466AC0 | Scene_UpdateChildren | Traverse scene tree, update children |
| 0x4602F0 | Scene_CollectByNameFilter | Collect scene objects by name string filter |
| 0x440DD0 | Graphics_DrawRectAndReset | Draw rect then reset matrix to identity |
| Address | Function | Notes |
|---|---|---|
| 0x429520 | Game_SetInProgress | Set game in-progress flag (+0x200=1) |
| 0x44BE80 | ScoreObject_ctor | Score display constructor (vtable 0x4D6C70, score=200000) |
| 0x446B80 | RegisterDialog_ValidateSerial | Validate serial via XOR cipher key "54138" |
| Address | Function | Notes |
|---|---|---|
| 0x40B090 | Level_InitScene | Init scene (projection, fog, find CAMERALOCUS) |
| 0x40B420 | Level_RenderDynamicObjects | Render moving objects via Timer positions |
| 0x40B570 | Level_RenderObjects | Iterate objects calling vtable+0x0C |
| 0x40B600 | Level_UpdateAndRender | Full update: merge lists, pre-render, shadow, cleanup |
| 0x40B9C0 | Level_SetObjectTransform | Set world transform from position |
| 0x40ACA0 | Level_SelectCameraProfile | Select camera by difficulty (4-15) |
+0x00: vtable pointer
+0x04: hInstance (Windows HINSTANCE)
+0x08: cmdShow (nCmdShow)
+0x54: registryKey (Config* pointer)
+0x5A: frameTimeMs (frame delta)
+0x5B: fpsDenominator
+0x5C: targetFPS
+0x5D: renderTarget (render target ptr)
+0x65: frameCounter (per-second count)
+0x84: profilingSection (char* current section name)
+0x15A: someFlag
+0x15C: width (window width)
+0x158: windowed (windowed mode flag)
+0x159: quitFlag (1 = game should quit)
+0x160: height (window height)
+0x174: Graphics* pointer (D3D device wrapper)
+0x17C: audioSystem pointer (BASS audio)
+0x180: Input* pointer (DirectInput8 subsystem, 0x438 bytes)
+0x1B4: versionString
+0x1CC: loadedCount (objects counter)
+0x200: initialized flag
+0x208: char[256] debug_string (current init step name)
+0x240: HCURSOR blank_cursor
+0x278: void* shadow_texture
+0x534: MusicPlayer* music handle
+0x538: int music_channel2
+0x53C: int music_channel1
+0x550: gameMode1 (mode value 1)
+0x554: gameMode2 (mode value 2)
+0x558: gameMode3 (mode value 4)
+0x55C: gameMode4 (mode value 5)
+0x914: int play_count (shareware trial)
Steps 1-12 are in App_Initialize (0x46BB40):
Steps 13-26 are in App_Initialize_Full (0x429530): 13. Set graphics->initialized = true 14. Load "BLANKCURSOR" 15. Set display mode 800x600 16. Configure D3D render states 17. N/A (skipped) 18. Load "shadow.png" texture 19. Load "music\music.mo3" 20. Load "jukebox.xml" 21. Set up music channel 1 22. Set up music channel 2 23. Config::Load + read "PlayCount" 24. Set initialized flag 25-28. Create 4 game mode objects (values 1,2,4,5) 29. Config save 30. vtable+0xA0: final init callback
| 0x478680 | Texture_dtor | 4 | Destroy texture: free sub-textures, pixel buffers, palette, alpha, vtable release | | 0x46DB10 | App_Shutdown | 4 | Application cleanup: destroy window, release 5 COM objects, CoUninitialize, free string buffer | | 0x473640 | AthenaString_Find | 4 | strstr wrapper: returns offset of substring or -1 | | 0x473600 | AthenaString_Length | 4 | Recalculate and cache string length, clear dirty flag at +0x10 | | 0x469B20 | UIWidget_HitTest | 3 | Hit-test UI widget list against (x,y) point | | 0x477670 | Vec3_ClosestPointOnLine | 3 | Project point onto line segment, clamp t to [0,lineLength] | | 0x475DC0 | CRC32_Compute | 3 | CRC32 using 256-entry lookup table at DAT_004F7534 | | 0x472990 | Gadget_LabelCtor | 8 | Gadget_Label constructor: vtable 0x4D9E68, name at +0x878, value at +0x87C | | 0xA00000 | Key Vtable Addresses | | | | 0x4DA65C | D3DX_RegistryGetter_vtable | | DirectX registry path struct vtable | | 0x4E998C | AthenaList_ctor_vtable | | AthenaList constructor vtable | | 0x4D9750 | App_Shutdown_vtable | | App shutdown vtable |
Opens file with _open(path, 0x8000), reads: material_count → materials (with extended/texture data) → mesh_buffers → game_objects → bounding_box → vertex_array. See SKILL.md for full format spec.
| Index | Address | Function | Description |
|---|---|---|---|
| 0 | 0x419770 | Scene_DeletingDtor | Destructor (scalar deleting) |
| 1 | 0x419C00 | Scene_Update | Main update tick |
| 2 | 0x41A2E0 | Scene_Render | Main render tick |
| 3 | 0x41C620 | Scene_HandleInput | Input dispatch (menu navigation, ball control) |
| 4 | 0x41C7D0 | Scene_ActivateCurrentItem | Activate selected menu item |
| 6 | 0x41CA00 | Scene_SelectCurrentItem | Select/highlight menu item |
| 7 | 0x409D90 | (3-byte nop stub) | Unused vtable slot |
| 11 | 0x41CB60 | Scene_ClearCurrentItem[1] | Clear item selection variant 1 |
| 12 | 0x41CBF0 | Scene_ClearCurrentItem[2] | Clear item selection variant 2 |
| 14 | 0x419900 | Scene_DestroyScene | Destroy scene + cleanup |
| 15 | 0x419A40 | Scene_NotifyObjects | Notify all scene objects of event |
| 16 | 0x419970 | Scene_SetDestroyed | Set destroyed flag (+0x2C) |
| 17 | 0x4198E0 | Scene_SaveAndCleanup | Save state + cleanup |
| 19 | 0x419E20 | Scene_HandleRaceEnd | Handle race completion |
| 20 | 0x41A180 | Scene_UpdateBallsAndState | Update ball physics + race state |
| 22 | 0x41A050 | Scene_ProcessRaceEnd | 3-2-1-GO countdown logic |
| 23 | 0x419E80 | Scene_HandleBallFinish | Ball finish state machine (5 states) |
| 27 | 0x41A560 | Scene_RenderScoreHUD | Render score HUD overlay |
| 28 | 0x41A680 | Scene_RenderTimerHUD | Render timer HUD overlay |
| 32 | 0x41C5B0 | Scene_SpawnBallsAndObjects | Create game objects (balls, traps, secrets, flags) |
| 35 | 0x41A9A0 | Scene_ComputeInputForceDirection | Computes 3D force vector from strongest player input across balls in slot (Ghidra label "Scene_ComputeLighting" is a MISNOMER) |
| Phase | Description | Key Offsets |
|---|---|---|
| 1 | Frame counter++ | this+0xD88 (= 0x3620) |
| 2 | Demo timer countdown | this+0x10D6 (active), +0x10D7 (frames left), +0x10D8 (elapsed), +0x10D9 (counter) |
| 3 | ESC check → Scene_CreateGameOverMenu | this+0x10DA (ignore ESC), App+0x5FC (input mode) |
| 4 | Ball position propagation | this+0xA6C (flag), iterate +0xA75 ball list |
| 5 | Gear path follow (single player) | this+0xFC7 (gear enabled), +0xFC8 (path data) |
| 6 | RumbleBoard timer ticks | this+0x221, this+0x226 (two RumbleBoard timers) |
| 7 | Camera shake decay | this+0xE93 (shake active), +0xA6E (magnitude, ±800, decay -10/frame) |
| 8 | Scene object update+render | iterate this+0x22E, vtable[4]=Update, vtable[0]=Render |
| 9 | Per-frame update pipeline | vtable[0x4C]=Scene_HandleRaceEnd, [0x50]=Scene_UpdateBallsAndState (ball update+respawn), [0x54]=NoOp, [0x58]=Scene_ProcessRaceEnd + physics objects at this+0xD8B |
| 10 | Post-physics callback | this+0xEBF+0x04() |
state 0: start → set counter=150.0f, advance to state 1
state 1: countdown → decrement counter by dt, when ≤0 advance to state 2
state 2: finish → save race time, play "Goal!" music, advance to state 3
state 3: popup → show finish popup, advance to state 4
state 4: done → wait for popup dismissal
| Index | Address | Function | Description |
|---|---|---|---|
| 1 | 0x46B560 | SceneObject_SetPosition | Set world position (XYZ) |
| 2 | 0x46B5A0 | SceneObject_SetScale | Set scale vector |
| 3 | 0x46B4B0 | SceneObject_Render | Render scene object |
| 4 | 0x46B4D0 | SceneObject_SetVisible | Toggle visibility flag |
| 7 | 0x46B860 | SceneObject_BaseDtor | Base dtor: iterates child list, calls each child's dtor(1), clears list |
| 8 | 0x46B650 | SceneObject_DeletingDtor | Scalar deleting destructor |
15-case switch creates specific Board constructors. Key discovery: CreateExpertLevelObjects (0x40E250) is the MASTER ARENA OBJECT FACTORY — creates ALL arena hazard types (Sawblade, TowerLevel, Spinner, Gear/Judge, Tipper/Bell, Bonk/Hammer) by name prefix matching.
| Case | Level | Board Constructor | Size |
|---|---|---|---|
| 1 | Warm-Up | LevelBoard_WarmUp_ctor | 0x436C |
| 2 | Beginner | LevelBoard_Beginner_ctor | 0x644C |
| 3 | Intermediate | LevelBoard_Intermediate_ctor | 0x438C |
| 4 | Dizzy | LevelBoard_Dizzy_ctor | 0x4BE0 |
| 5 | Tower | LevelBoard_Tower_ctor | 0x5418 |
| 6 | Up | LevelBoard_Up_ctor | 0x4790 |
| 7 | Neon Race | Board_NeonRace_ctor | 0x4394 |
| 8 | Expert | LevelBoard_Expert_ctor | 0x4FD8 |
| 9 | Odd | LevelBoard_Odd_ctor | 0x43B0 |
| 10 | Toob Race | LevelBoard_Toob_ctor | 0x646C |
| 11 | Wobbly | LevelBoard_Wobbly_ctor | 0x4388 |
| 12 | Glass | Board_Glass_ctor | 0x4390 |
| 13 | Sky | LevelBoard_Sky_ctor | 0x47F8 |
| 14 | Master | BoardLevel_Master_Ctor | 0x6498 |
| 15 | Impossible | Board_Impossible_ctor | 0x4380 |
Also saves score, computes difficulty time bonus (+1000ms normal, +500ms frenzied), stores race timestamps. When param_1=true, creates TourneyMenu instead (retry current race).
"Practice Menu" scene with 14 race items and 14 thumbnail textures (practice-level1..practice-impossible.png). Lock check via App+0x851-0x865 boolean flags.
| Offset | Level |
|---|---|
| +0x851 | Level 1 (Warm-Up) |
| +0x852 | Level 2 (Beginner) |
| +0x853 | Level 3 (Intermediate) |
| +0x854 | Level 4 (Dizzy) |
| +0x855 | Level 5 (Tower) |
| +0x856 | Level 6 (King of the Hill) |
| +0x857 | Level 7 (Up) |
| +0x858 | Level 8 (Expert) |
| +0x859 | Level 9 (Odd) |
| +0x85A | Level 10 (Toob Race) |
| +0x85B | Level 11 (Sky) |
| +0x85C | Level 12 (Wobbly) |
| +0x85D | Level 13 (Master) |
| +0x85E | Level 14 (Race of Ages) |
| +0x85F | Level 15 (Impossible) |
| +0x860-0x865 | Arena unlock flags |
Creates GameObject instances (0xC60 bytes) for each ball, sets start positions via START%d-%d naming, scans SAFESPOT/SAFEPOS objects, creates BadBall/MouseTrap/SecretObjects/Flags/Signs/DynamicObjects.
Creates results screen scene (0x87C bytes). Called after tournament race completion.
Returns time bonus multiplier based on difficulty level.
| Arena | Init Function | Level Path | Special Logic |
|---|---|---|---|
| WarmUp | ArenaLevel_WarmUp_Init (0x413C20) | levels\arena-WarmUp | — |
| Beginner | RumbleBoard_Beginner_Init (0x414180) | levels\arena-intermediate | — |
| Intermediate | ArenaLevel_Intermediate_Init (0x414180) | levels\arena-Intermediate | — |
| Dizzy | ArenaLevel_Dizzy_Init (0x414240) | levels\arena-dizzy | Loads bonus level Levels\Level3-Swirl |
| Tower | ArenaLevel_Tower_Init | levels\arena-Tower | — |
| Up | ArenaLevel_Up_Init | levels\arena-Up | — |
| Expert | ArenaLevel_Expert_Init (0x414B10) | levels\arena-expert | — |
| Odd | ArenaLevel_Odd_Init (0x414CE0) | levels\arena-Odd | — |
| Sky | ArenaLevel_Sky_Init (0x4158C0) | levels\arena-Sky | "PILLAR" object collection |
| Neon | ArenaLevel_Neon_Init (0x416F40) | levels\arena-neon | 2x AthenaList transforms + boundary sphere |
| Glass | ArenaLevel_Glass_Init (0x417DF0) | levels\arena-Glass | — |
| Master | ArenaLevel_Master_Init (0x416080) | levels\arena-Master | — |
| Race of Ages | (Cascade) | levels\arena-Cascade | — |
| Impossible | ArenaLevel_Impossible_Init (0x418540) | levels\arena-impossible | — |
Scene vtable at 0x4D0260 has 36 entries (144 bytes). Read memory at the vtable address, decode as 36 little-endian 32-bit pointers. Some entries may be 3-byte nop stubs (0x409D90) — these are unused slots. Plate comments can ONLY be set at function addresses, not data addresses (failed at 0x4D0260 and 0x4D934C).
search_functions_enhanced with has_custom_name=false, min_xrefs=15, sort_by=xrefs_desc to find undocumented functions with many callersget_function_callers or get_xrefs_to on data items to understand usage contextlist_data_items_by_xrefs reveals high-value globals like g_renderIndex, string constants like s_BACK| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x42f810 | TimeTrialMenu_ctor | 3 | "Time Trial Menu" — extends PracticeMenu with race items + lock checks |
| 0x42fc10 | PartyMenu_ctor | 3 | "CHOOSE A PARTY RACE!" — extends PracticeMenu, vtable 0x4D4738 |
| 0x42fc40 | ArenaMenu_ctor | 3 | Arena menu with 14 arena items (Warm-Up to Impossible), lock icons, vtable 0x4D47B8 |
| 0x4326d0 | MPMenu_ctor | 3 | Multiplayer menu: Party Race, Rodent Rumble, controller config (1-4P) |
| 0x44aa40 | Scene_FindTextureByName | 4 | Find texture by case-insensitive name, return ptr+dimensions+width |
| 0x44ab00 | Scene_FindTextureDimensions | 4 | Find texture and measure text width via Font_MeasureText |
| 0x44abf0 | Scene_AddTextureToList | 4 | Add texture reference to scene list by name, mark dirty at +0xCBC |
| 0x443ac0 | SceneObject_RenderScaled | 3 | Render object scaled (ScaleX, SetPosition, vtable callbacks + Timer) |
| 0x4410c0 | SceneObject_FreeStrings | 3 | Free 2 string ptrs, re-init BaseObject, call SceneObject_dtor |
| 0x453c50 | Texture_RemoveRef | 3 | Decrement texture refcount, remove from cache and free when 0 |
| 0x457a50 | Graphics_DisableRenderState | 3 | Thunk → Graphics_SetRenderState (disable mode) |
| 0x428c50 | App_StartPracticeRace | 3 | Start practice/tournament race: calls App_StartRace, PlayerProfile, Tournament_AdvanceRace |
| 0x434580 | Sound_InitChannels | 3 | Allocate sound channels, get next sample, play 3D positioned, set timer 0x140 |
| 0x43b6f0 | Rotator_AddBall | 3 | Find score by ID and set to 10, or create new entry with value 10 |
| 0x44bef0 | Timer_Decrement | 4 | Timer tick: value = end - 100, set flag at +0x2A |
| 0x448620 | ScoreDisplay_DeletingDtor | 3 | ScoreDisplay scalar deleting destructor |
| 0x4470d0 | ScoreDisplay_dtor | 3 | Clean up: free strings, timers, 5 BaseObjects, SceneObject_dtor |
| 0x44acb0 | UIList_Clear | 4 | Empty stub (returns 0) |
App offsets for arena unlock flags (0-based, boolean): +0x852=Level 2 (Beginner), +0x853=Intermediate, +0x85A=Dizzy, +0x85B=Tower, +0x85C=Up, +0x85D=Expert, +0x85E=Odd, +0x85F=Toob, +0x860=Wobbly, +0x867=Glass, +0x861=Sky, +0x862=Master, +0x868=Impossible
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x466C70 | AthenaString_Format | 98 | sprintf wrapper returning internal buffer |
| 0x4BBDFD | AthenaString_Sprintf | - | Internal sprintf using FILE struct trick |
| 0x469990 | Scene_AddObject | 77 | Add SceneObject to scene (uniqueness check, vtable notify) |
| 0x46F390 | Scene_BeginFrame | 39 | Begin rendering frame (Graphics_BeginFrame + vtable callback) |
| 0x460DA0 | Scene_RenderFrame | 38 | Full scene render pipeline with z-buffer interleaving |
| 0x461370 | Scene_RenderOpaque | 38 | Render opaque pass (object vtable[0x28] + mesh buffers) |
| 0x461890 | Scene_LoadMeshWorld | 38 | Load .meshworld from stream (materials, textures, objects) |
| 0x461F00 | Scene_Subdivide | 38 | Subdivide scene space into regular 3D grid |
| 0x462100 | Scene_SubdivideRandom | 38 | Subdivide scene with random grid positions |
| 0x4629E0 | Scene_LoadCached | 37 | Load .cached scene file (binary format) |
| 0x46A750 | Window_Notify | 37 | Send WM_COPYDATA message to window |
| 0x498200 | BitStream_ReadBits | 95 | Read N bits from byte-aligned stream |
| 0x4792FB | D3DX_DetectShaderProfile | 48 | Detect pixel shader version (1.x/2.x/3.x) |
| 0x492BDA | Vertex_Transform | 32 | Transform vertices between coordinate spaces |
| 0x4737F0 | AthenaString_Assign | 52 | String copy/assign operator |
| 0x4605E0 | AthenaHashTable_Lookup | 36 | Case-insensitive hash table lookup |
| 0x4691C0 | SceneObject_dtor | 31 | SceneObject destructor |
| 0x4693C0 | Scene_AddAllObjects | 25 | Batch add all SceneObjects |
| 0x469600 | MWParser_ReadTag | 24 | XML/SGML tag parser for MW files |
| 0x4695D0 | StreamReader_dtor | 24 | Close file handle, free buffer |
| 0x45D450 | Sprite_DrawColoredRect | 23 | Draw colored quad with random vertex colors |
| 0x472AF0 | AthenaString_Init | 18 | Default string constructor |
| 0x472F30 | RegKey_Close | 18 | RegCloseKey wrapper |
| 0x473670 | AthenaString_CopyCtor | 16 | String copy constructor |
| 0x4740D0 | AthenaString_WriteTag | 16 | Build XML tag content |
| 0x489610 | Pool_FreeList | 16 | Walk free list, decrement refcounts |
| 0x459B24 | Graphics_InitShaderDispatch | 19 | D3DX shader init dispatch thunk |
| 0x45A439 | Graphics_SetRenderState | 29 | Render state dispatch thunk |
| 0x4AB9B8 | DivCeil | 15 | Ceiling division (a-1+b)/b |
| 0x4D2334 | s_BACK | 15 | "BACK" string constant |
| 0x5341CC | g_renderIndex | - | Global render index counter |
references/batch-doc-workflow.md for efficient decompile→identify→rename→comment→commit cycle| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x4B22AB | BitStream_ReadBitsSSE2 | 19 | SSE2 bitstream reader: reads N bits in 8-bit chunks, 0xFF marker handling, maintains stream state |
| 0x4A0F3A | Matrix_Inverse4x4_SSE2 | 9 | SSE2 4x4 matrix inverse via cofactor expansion + Newton-Raphson reciprocal. Returns NULL if singular |
| 0x4A6B80 | SSE2_SetFPControlWord | 9 | Store FPU control word to global DAT_00535280 |
| 0x4ABA49 | Mem_Zero | 8 | Optimized memset-to-zero: dwords first (count>>2), then bytes (count&3) |
| 0x4AD576 | Malloc_OrLongjmp | 9 | Safe malloc: calls malloc, longjmps with "Out of Memory" on failure. Returns NULL if ptr/size==0 |
| 0x4BC360 | StrCat_Fast | 9 | Optimized strcat: dword-aligned null detection (0x7efefeff trick), fast copy |
| 0x4B8564 | BitStream_CopyToOutput | 8 | Copy data from bitstream to output buffer with checksum callback. Double-buffered streaming |
| 0x4C183E | Noop2 | 8 | Empty no-op function |
| 0x4C7152 | ReturnZero | 8 | Returns 0 (stub/trap) |
| 0x459D96 | D3DX_ShaderDispatch0 | 3 | D3DX shader dispatch via PTR_FUN_004F7194 vtable |
| 0x459E34 | D3DX_ShaderDispatch1 | 4 | D3DX shader dispatch via PTR_FUN_004F71B8 vtable |
| 0x459ED1 | D3DX_ShaderDispatch2 | 4 | D3DX shader dispatch via PTR_FUN_004F71CC vtable |
| 0x467E40 | AthenaList_Ctor | 3 | Init AthenaList with vtable 0x4E998C, StdString_Substr copy |
| 0x46A0D0 | Audio_StopChannel | 9 | Stop BASS audio channel (BASS_ChannelStop on handle at +0x08) |
| 0x46DB10 | App_Shutdown | 4 | Application cleanup: destroy window, release 5 COM objects, CoUninitialize, free string buffer |
| 0x46DFA0 | NetworkConnection_Ctor | 4 | Init network connection: "Not Connected", id at +0x04, +0x0C=1.0f |
| 0x472340 | Font_RenderToTextureComplex | 4 | Complex text→texture rendering using D3D vertex buffers and shaders |
| 0x472990 | Gadget_LabelCtor | 8 | Gadget_Label constructor: vtable 0x4D9E68, name string at +0x878, value at +0x87C |
| 0x469B20 | UIWidget_HitTest | 3 | Hit-test UI widget list against (x,y) point. Checks +0x420 rect first, else iterates children by bounds |
| 0x46B840 | SceneObject_EmptyListCtor | 3 | Init SceneObject with DeletingDtor vtable 0x4D9368 + empty AthenaList |
| 0x473480 | AthenaString_Reserve | 4 | Reserve string capacity: alloc new buffer, copy old, free old |
| 0x473600 | AthenaString_Length | 4 | Recalculate and cache string length, clear dirty flag at +0x10 |
| 0x473640 | AthenaString_Find | 4 | strstr wrapper: returns offset of substring or -1 |
| 0x477010 | D3DX_RegistryGetter | 4 | Set vtable 0x4DA65C (DirectX registry path struct) |
| 0x475DC0 | CRC32_Compute | 3 | CRC32 using 256-entry lookup table at DAT_004F7534 |
| 0x477670 | Vec3_ClosestPointOnLine | 3 | Project point onto line segment. Clamp t to [0,lineLength] |
| 0x477970 | Texture_StreamRead | 3 | Stream read from bitstream with 0x400-byte blocks and progress callback |
| 0x477AC0 | Texture_BinarySearch | 4 | Binary search in texture archive with checksum and callback |
| 0x477D60 | Texture_LoadFromStream | 4 | Texture loading from stream: init pool, loop with D3DX_Uninit/retry, up to 3 attempts |
| 0x478680 | Texture_dtor | 4 | Destroy texture: free sub-textures, pixel buffers, palette, alpha, vtable release |
| 0x48A160 | Texture_SetPool | 6 | Set texture pool reference (param_2), clear struct |
| 0x48A180 | Pool_dtor | 6 | Free pool list, zero 8 dwords |
| 0x48A560 | Texture_ValidateOrBuild | 5 | Validate/assemble texture from stream chunks, checksum mismatch → -0xF3 |
| 0x48A900 | Mesh_dtor | 6 | Destroy mesh: free D3D vertex buffers, release texture refs, free allocations |
| 0x489DF0 | Pool_Free | 4 | Free pool entries, zero 0x14 dwords |
| 0x489E20 | Texture_ComputeChecksum | 4 | Compute texture checksum from stream bytes (0xFF skip marker, accumulate sum) |
| 0x48A860 | Texture_DestroyBuffers | 5 | Free D3D index/vertex buffers, free allocation ptrs |
| 0x48A8D0 | Mesh_Init | 6 | Zero mesh struct, calloc 0xCA8 bytes for mesh data |
| 0x48B1C0 | Pool_Alloc | 6 | Pool allocator: allocate aligned block, chain old block, return offset |
| 0x48B2A0 | VertexDeclaration_dtor | 3 | Free vertex declaration, release D3D decl, zero struct |
| 0x48B530 | VertexShader_dtor | 3 | Free vertex shader: release D3D shader, free texture/palette/alpha buffers |
| 0x480C4D | Gfx_ResizeBuffers | 5 | D3D texture/surface buffer resize with format detection (D3DFMT checking) |
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x4BA754 | __ftol2 | 358 | CRT float-to-int64 conversion (compiler intrinsic) — MOST CALLED FUNCTION |
| 0x45DD60 | RNG_Rand | 193 | PRNG with 55-entry circular buf (Mitchell & Moore). Returns (buf[read]+buf[write])>>6 % range. Signed mode: param_2=1 negates 50% of results |
| 0x453250 | Vec3List_Free | 283 | Free Vec3List + member data at +0x103 |
| 0x44B840 | Noop | 280 | Empty stub (vtable placeholder) |
| 0x4736B0 | AthenaString_dtor | 85 | AthenaString destructor — frees buffer, sets vtable to base dtor (0x4D290C) |
| 0x473500 | AthenaString_AssignCStr | 75 | AthenaString assign from C string |
| 0x536A0 | AthenaList_GetSize | 60 | Return count at +4 |
| 0x453640 | AthenaList_FindByValue | - | Linear search, returns index or -1 |
| 0x443990 | AthenaString_Clear | 14 | Free buf, reset to 15-char inline capacity |
| 0x4531E0 | Vec3_Init | 13 | Set Vec3 vtable + zero + w=255.0 |
| 0x4BC0D1 | strtok | 50 | CRT strtok — thread-safe string tokenizer |
| 0x4BAC20 | strstr | 22 | CRT strstr — string search with SIMD optimization |
| 0x4BAE43 | AthenaString_SprintfToBuffer | 71 | sprintf into char buffer via fake FILE struct |
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x448F20 | SimpleMenu_ctor | 15 | "Simple Menu" base with item list + scrollers |
| 0x4490A0 | UIListItem_ctor | - | Init 0x444-byte item (Vec3 + AthenaList) |
| 0x4497F0 | UIList_AddItem | 86 | Add named item (text, subtext, colors, icon, height) |
| 0x449430 | UIList_AddSpacer | 29 | Add empty row with height |
| 0x4494D0 | UIList_ScrollUpdate | 17 | Scroll, mouse wheel, vtable dispatch |
| 0x449B00 | UIList_Cleanup | 27 | Free all items (strings, SceneObjects, Vec3Lists) |
| 0x449C20 | UIList_HandleKeyNav | 18 | Up/down/pgup/pgdn key navigation |
| 0x449D40 | UIList_Render | 18 | Draw items: gradient bars, text, icons, scroll arrows |
| 0x44A570 | UIList_Layout | 18 | Compute widths, position SceneObjects, set scrollers |
| 0x44A8B0 | UIList_SetTextByName | 27 | Find item by subtext, replace display text |
| 0x449750 | UIList_ActivateCurrentItem | 18 | Activate: Back→sound 650, Continue→50, else callback |
UIListItem (0x444 bytes): +0x00=display_text, +0x04=subtext, +0x0C-0x0F=RGBA, +0x1C4=icon, +0x244=height, +0x110=is_icon_row flag, +0x441=highlighted flag
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x4217B0 | ArenaBoard_ctor | 15 | Init with "RumbleBoard", timer, base score=6000 |
| 0x421880 | ArenaBoard_dtor | 24 | Cleanup timer, release SceneObjects, Scene_dtor |
| 0x421910 | ArenaBoard_Render | 16 | Timer bar, round ".%d", "TIE BREAKER!" |
| 0x421FE0 | ArenaBoard_Update | 16 | Check round end, resolve ties, "Game Over" music |
| 0x458E60 | ToggleTimer_Init | 12 | Initialize round timer |
| 0x458E80 | ToggleTimer_Cleanup | 32 | Cleanup round timer |
| 0x458E90 | ToggleTimer_Tick | 12 | Tick countdown |
| 0x44AD50 | ArenaScoreParticle_ctor | 12 | Init vtable + difficulty scale [0.02, 0.03, 0.04] |
Key RumbleBoard offsets: +0x47AC=base_score(6000), +0x47C5=is_tie_breaker, +0x47CC=tie_active, +0x47D0=max_rounds(25), +0x11EB=round_end_timer, +0x11F1=game_over_flag, +0x11ED-0x11F0=4 player scores
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x457B10 | Matrix44_Zero | 12 | Clear 4x4, set diagonals to 1.0 |
| 0x457B50 | Gfx_SetPosition | 69 | D3D SetTransform world translate |
| 0x457BB0 | Gfx_RotateY | 15 | Rotation around Y axis |
| 0x457C60 | Gfx_ScaleX | 40 | Scale X axis |
| 0x457C90 | Gfx_ScaleY | 35 | Scale Y axis |
| 0x457CC0 | Gfx_ScaleZ | 26 | Scale Z axis |
| 0x457FD0 | Matrix4_Identity | 40 | Set identity vtable |
| 0x425FE0 | Gfx_SetAlphaBlendState | 9 | D3DRS_SRCBLEND/DESTBLEND mode 3 |
| 0x427940 | Gfx_SetCullMode | 13 | D3DRS_CULLMODE (none/CW/CCW) |
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x457DA0 | Wave_Sin | 38 | sin(time * freq * 2π/360) |
| 0x457DC0 | Wave_Cos | 23 | cos(time * freq * 2π/360) |
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x45D0C0 | Sprite_ctor | 30 | Init with texture, RenderContext, material defaults |
| 0x45D660 | Sprite_RenderQuad | 15 | Render textured quad via material + DrawPrimitive |
Sprite (0xD4 bytes): +0x00=vtable, +0x04=gfx, +0x08-0x60=RenderContext, +0x50=texture, +0xC8=width, +0xCC=height, +0xD0=visible(1), +0xD1=flag
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x45E0E0 | Scene_RenderAllObjects | 33 | Main 3D render: BeginFrame → sort (opaque/alpha/shadow) → draw |
| 0x460450 | Scene_RenderBallShadow | 38 | Ball shadow with depth bias pass |
| 0x45DF80 | SceneObject_CallUpdate | 47 | Dispatch +0x434 vtable[1] (Update) |
| 0x45DF90 | SceneObject_CallRender | 47 | Dispatch +0x434 vtable[2] (Render) |
| 0x437130 | Scene_StartCountdown | 11 | Start countdown (3..2..1, 400 or 50 frames) |
Object flags in Scene_RenderAllObjects: +0x85F=shadow, +0x860=alpha, +0x862=deferred, +0x863=skip
struct RNGState {
VTable* vtable; // +0x00
int read_ptr; // +0x04 (wraps at 55)
int write_ptr; // +0x08 (wraps at 55)
uint32_t buffer[55]; // +0x0C..0xE4 (circular buf)
};
// Algorithm: buf[read] = (buf[read] + buf[write]) & 0x3FFFFFFF
// Return: (result >> 6) % range
// Signed: if param_2==1 && Rand(2)==0, negate
| Address | Name | Xrefs | Description |
|---|---|---|---|
| 0x440E70 | OkayDialog_ctor | 12 | "Okay Dialog" with caption + "OKAY!" button |
| 0x401480 | GameObject_dtor | 9 | Release timers, free Vec3Lists, cleanup matrices |
After achieving 100% function naming (3781/3781), the project moved to comprehensive system documentation. Key docs created:
| Doc | Content |
|---|---|
| docs/RUMBLEBOARD_SYSTEM.md | 15-race tournament order, all 9 arena asset paths, timer system, HUD layout, menu commands, mirror unlock, time bonus |
| docs/UI_MENU_SYSTEM.md | Menu hierarchy (SimpleMenu/UIList), all dispatch tables (Main/Pause/Difficulty), color system, dialog classes |
| docs/ARENA_HAZARD_SYSTEM.md | 6 hazard types (Sawblade/Tower/Spinner/Gear/Bell/Bonk), CreateExpertLevelObjects factory, name suffix modifiers, difficulty values |
| docs/LEVEL_OBJECTS.md | Catapult/Trapdoor/ScoreDisplay/HighScore/Damage/JumpPad, RNG (55-element LFSR), Ball trail particles |
| docs/COLLISION_SYSTEM.md | CollisionFace/MeshBuffer structs, .COL binary format, Arena/Level/GameObject event dispatch |
# Start the headless server (run once per session):
export GHIDRA_HOME=/opt/ghidra_12.0.4_PUBLIC
MCP_JAR=/home/evan/.config/ghidra/ghidra_12.0.4_PUBLIC/Extensions/GhidraMCP/lib/GhidraMCP-5.12.0.jar
CLASSPATH="$MCP_JAR"
for jar in $GHIDRA_HOME/Ghidra/Framework/*/lib/*.jar; do CLASSPATH="${CLASSPATH}:${jar}"; done
for jar in $GHIDRA_HOME/Ghidra/Features/*/lib/*.jar; do CLASSPATH="${CLASSPATH}:${jar}"; done
for jar in $GHIDRA_HOME/Ghidra/Processors/*/lib/*.jar; do CLASSPATH="${CLASSPATH}:${jar}"; done
java -Xmx4g -XX:+UseG1GC \
-Dghidra.home=$GHIDRA_HOME -Dapplication.name=GhidraMCP \
-classpath "$CLASSPATH" \
com.xebyte.headless.GhidraMCPHeadlessServer \
--port 8089 --bind 127.0.0.1 &
# Load binary and run analysis:
curl -s -X POST -d "file=/home/evan/hamsterball-re/originals/installed/extracted/Hamsterball.exe" http://127.0.0.1:8089/load_program
curl -s -X POST http://127.0.0.1:8089/run_analysis
# Read operations (GET):
curl -s "http://127.0.0.1:8089/decompile_function?address=0x004278E0"
curl -s "http://127.0.0.1:8089/list_functions?limit=50"
curl -s "http://127.0.0.1:8089/search_strings?search_term=App&limit=10"
curl -s "http://127.0.0.1:8089/get_xrefs_to?address=0x004D9384"
curl -s "http://127.0.0.1:8089/list_imports?limit=200"
# Write operations (POST with JSON body — form-encoded does NOT work):
curl -s -X POST -H "Content-Type: application/json" \
-d '{"function_address":"0x00455380","new_name":"Graphics_Initialize"}' \
http://127.0.0.1:8089/rename_function_by_address
# IMPORTANT: API param naming uses underscores (function_address, new_name, search_term)
# IMPORTANT: POST writes require JSON body, form-encoded params silently fail
cd ~/hamsterball-re/originals/installed/extracted
r2 -q -c "aaa; afl" Hamsterball.exe | wc -l # 1869 functions
r2 -q -c "aaa; s 0x429530; pdf" Hamsterball.exe # App::Initialize
r2 -q -c "aaa; axt @ sym.imp.d3d8.dll_Direct3DCreate8" Hamsterball.exe
[Geometry Section] — vertex/face data, variable length
[Object Section] — starts with uint32 count, then objects
[Trailer] — closing data
Each object: [uint32 str_len][type_string][data...][optional_texture_string]
Simple objects (START, SAFESPOT): 28 bytes after type string
Complex objects (FLAG, PLATFORM, BUMPER): variable size
START1-1, START2-1, START2-2, START2-3, START2-4 — Player start positions FLAG02, FLAG04, FLAG06, FLAG07 — Checkpoint flags SAFESPOT — Safe landing zones CAMERALOOKAT (CameraLocus) — Camera targets PLATFORM, N:SINKPLATFORM — Level geometry N:BUMPER1/2/3/4 — Bumpers E:NODIZZYN — Anti-dizzy zones E:LIMIT — Arena limits E:GROWSOUND — Sound triggers
The format is NOT straightforward sequential parsing — object data sizes vary per type and can't be determined without knowing the type. The correct approach is:
cd ~/hamsterball-re/reimpl
# Build and test (86 levels, 1533 objects confirmed)
./build/level_viewer ~/hamsterball-re/originals/installed/extracted/Levels/Level1.MESHWORLD
# Headless test with Xvfb:
DISPLAY=:99 timeout 3 ./build/level_viewer <level_file>
cd ~/hamsterball-re/originals/installed/extracted
wine Hamsterball.exe # Needs X display for rendering
1. while (!quitFlag):
2. if PeekMessage(&msg, NULL, 0, 0, PM_REMOVE):
3. if msg == WM_QUIT: quitFlag = 1; break
4. TranslateMessage(&msg); DispatchMessage(&msg)
5. else:
6. frameStart = GetTickCount()
7. this->Update() // vtable call (0x469CF0 = GameUpdate)
8. this->Render() // vtable call
9. this->Present() // vtable call (0x455A90 = Graphics_PresentOrEnd)
10. frameEnd = GetTickCount()
11. elapsed = frameEnd - frameStart
12. if elapsed < targetFrameTime: Sleep(targetFrameTime - elapsed)
13. if elapsed > 0: this->fpsDenominator = 33 / elapsed // frame scaling
ghidra-rename-export skill to backup to analysis/ghidra/renames_backup.jsonghidra-restore-renames skill to restore from docs/FUNCTION_MAP.mdghidra-mcp-headless skill with --program /Hamsterball.exe (leading slash required)ghidra-rename-backup, ghidra-restore-renames, ghidra-mcp-headless, ghidra-rename-export[uint32 length][ASCII data] pattern is very robust for finding object boundaries-d "key=value") silently fail with "parameter required" errors. Must use -H "Content-Type: application/json" -d '{"key":"value"}'function_address not functionAddress, new_name not newName, search_term not searchTermget_xrefs_to won't work on them. Use the string name (e.g., "BASS_Init") to find the IAT thunk, then find xrefs to the thunk.GhidraMCPHeadlessServer) is much more reliable for automation./tmp/ghidra-mcp-{user}/*.sock. The headless server uses TCP on port 8089 instead. Set GHIDRA_SERVER_URL env var or use connect_instance with the TCP URL.create_struct fields MUST be JSON array format: [{"name":"field_name","offset":0,"type":"float"},...] — colon/comma string formats all fail with "No valid fields provided"add_struct_field works with explicit offset: Use for incremental building of large structsimport_data_types not implemented: Returns "Import functionality not yet implemented"run_script_inline broken: "BundleHost null" OSGi error since session 47 — use create_struct/add_struct_field insteadfloat, int, uint, byte, char, uint32, int32, byte[20], uint (pointer), or any Ghidra built-in type namecreate_struct with initial minimal fields (0-32 bytes), then add_struct_field per field with explicit offset — this avoids size conflicts when adding overlapping fields-readOnly flag in analyzeHeadless discards labels/analysis on exit. Always omit it.search_strings?search_term=Xget_xrefs_to?address=0x{addr}decompile_function?address=0x{addr}rename_function_by_address with JSON bodyGhidraMCP's get_xrefs_from only returns ONE xref per data address (the first entry). To discover ALL vtable method pointers, scan each 4-byte offset:
for i in range(0, 0x40, 4):
result = get_xrefs_from(address=vtable_base + i)
# Each result links to a function pointer
"No function found" for undefined thunks — try decompiling nearby addresses to find the real targetThe App global (0x4FD680) has few direct xrefs because the game typically passes this pointers through function calls. To find code accessing specific App fields:
param_1 or this+0x00: vtable pointer
+0x04: Input* back-pointer (parent Input object)
+0x08: IDirectInputDevice8* device
+0x0C: key_state_old[256] (DWORD[0x40] — previous frame key states)
+0x10C: key_bindings[256] (pointer[0xFF] — DIK code → action object mapping)
+0x143: KeyDown DIK code (uint8, DIK_* code for Down action)
+0x144: KeyUp DIK code
+0x145: KeyLeft DIK code
+0x146: KeyRight DIK code
+0x147: KeyAction1 DIK code
+0x148: KeyAction2 DIK code
+0x184: ptr to key action object
+0x1CC: ptr to key action object
+0x1C4: ptr to key action object
+0x18C: ptr to key action object
+0x154: ptr to key action object
+0x194: ptr to key action object
+0x198: ptr to key action object
+0x1A0: ptr to key action object
+0x1A4: ptr to key action object
+0x1D0: ptr to key action object
+0x1D4: ptr to key action object
+0x168: ptr to key action object
+0x16C: ptr to key action object
+0x170: ptr to key action object
+0x14C: ptr to key action object
+0x158: ptr to key action object
+0x188: ptr to key action object
+0x15C: ptr to key action object
+0x138: vtable/data pointer
+0x00: vtable pointer
+0x04: AthenaList* object_list (game objects)
+0x08: int object_count
+0x18: AthenaList* (secondary list)
+0x410: void** object_array (pointer to list data)
+0x41C: GameObject* tracked_object_1 (e.g., player ball)
+0x420: GameObject* tracked_object_2
+0x424: AthenaList* managed_list
+0x428: AthenaList* (object pool)
+0x430: uint8 flag_1
+0x431: uint8 flag_2
+0x4368: LevelData* (secondary level — clone for split screen)
+0x436C: Level* level_mesh_1 (MeshWorld)
+0x4370: Level* level_clone_1
+0x4374: Level* level_mesh_2 (secondary)
+0x4378: AthenaList* objects_list_1
+0x4790: AthenaList* objects_list_2
+0x480: LevelState* state (ball/physics state, 0x10D4 bytes)
+0x484: uint8 flag
+0x844: LevelContext* context (shared rendering state)
+0x868: char* level_name (e.g., "Board (Dizzy)")
+0x878: App* back-pointer
+0x04: uint8 enable_flag
+0x14: int counter
+0x18: uint32 value (0xf = 15, possibly ball size param)
+0x1C: AthenaList* (objects)
+0x440: void* collision_data (freed on cleanup)
+0x444: IDirect3DVertexBuffer8* vb_1 (freed on cleanup)
+0x448: IDirect3DVertexBuffer8* vb_2 (freed on cleanup)
+0x44C: IDirect3DVertexBuffer8* vb_3 (freed on cleanup)
+0x450: unknown struct (0x14 bytes)
+0x464: unknown struct (0x14 bytes)
+0x478: AthenaList* (game entities)
+0x894: AthenaList* (render entities)
+0xCAC: AthenaList* (physic entities)
+0x10C4: uint8 flag
+0x10C8: uint32 extra_data (freed on cleanup)
+0x10D0: uint8 flag
+0x00: destructor (called with param=1 for free)
+0x04: Update() — main update tick (called for active objects)
+0x08: Release() / IUnknown Release
+0x30: Render/Cleanup method (called when object removed)
+0x3C: Render(context) — render with level context param
+0x00: vtable pointer
+0x0B: uint8 removed_flag (non-zero = pending removal)
+0x21D: uint8 inactive_flag (non-zero = skip update)
+0x21A: int object_id (used as context during updates)
+0x21C: padding/flags
The ball is a GameObject subclass with dedicated physics. Base class is GameObject at 0x4CF314.
| Offset | Address | Function | Description |
|---|---|---|---|
| +0x00 | 0x4027F0 | Ball_dtor | Destructor (calls Ball_Cleanup) |
| +0x04 | 0x405100 | (thunk→0x405190) | Update method |
| +0x08 | 0x402DE0 | Ball_CollisionCheck | Per-frame collision check |
| +0x0C | 0x402A70 | (not defined) | Render setup |
| +0x10 | 0x408390 | (not defined) | Unknown |
| +0x14 | 0x401590 | (not defined) | Unknown |
| +0x18 | 0x402650 | Ball_ApplyForce | Apply directional force |
| +0x1C | 0x402C10 | (not defined) | Unknown |
| +0x20 | 0x409480 | (not defined) | Unknown |
| Offset | Type | Description |
|---|---|---|
| 0x00 | void** | Vtable pointer (0x4CF3A0) |
| 0x04 | int | Level reference (parent) |
| 0x08 | int | Parent object reference |
| 0x0C | float[6] | Collision planes (4 floats each: ax+by+cz+d) |
| 0x42 | Timer | Per-ball timer |
| 0x59 | float | Ball X position (legacy) |
| 0x5A | float | Ball Y position (legacy) |
| 0x5B | float | Ball Z position (legacy) |
| 0x62 | float | Ball size (default 3.0f = 0x40400000) |
| 0x69 | void* | Sound object |
| 0x96 | int | Sound handle |
| 0x154 | IDirect3DDevice8* | D3D device pointer |
| 0x164 | float | X position (authoritative) |
| 0x168 | float | Y position (authoritative) |
| 0x16C | float | Z position (authoritative) |
| 0x170 | float | X velocity (zeroed on collision) |
| 0x174 | float | Y velocity (zeroed on collision) |
| 0x178 | float | Z velocity (zeroed on collision) |
| 0x17C | float | Acceleration X (zeroed on collision) |
| 0x180 | float | Acceleration Y (zeroed on collision) |
| 0x184 | float | Acceleration Z (zeroed on collision) |
| 0x1A4 | int | Level state reference |
| 0x284 | float | Ball radius / height offset |
| 0x2CC | byte | Force disable flag |
| 0x2DC | float | X position (secondary) |
| 0x2E0 | float | Y position (secondary) |
| 0x2E4 | float | Z position (secondary) |
| 0x2F0 | int | Frame counter (affects force scaling) |
| 0x2F8 | byte | Active flag |
| 0x2F9 | byte | Collision occurred flag |
| 0x300 | int | Value 0x96 (150) - mass/timer constant |
| 0x310 | byte | State flag (1 = active) |
| 0x314 | float | Home X position |
| 0x318 | float | Home Y position |
| 0x324 | byte | Shrunk/special state flag |
| 0x440 | void* | Collision data |
| 0x6FC | int | Render initialized flag |
| 0x700 | byte | Render mode (affects lighting) |
| 0x708 | int | Render parameter |
| 0x70D | byte | Render state initialized |
| 0x734 | byte | Sub-render mode |
| 0x748 | int | Collision mesh pointer |
| 0x76A | byte | Flag (zeroed on collision) |
| 0x7C8 | int | Render call counter |
| 0x808 | int | Freeze counter (skip force if > 0) |
| 0x810 | AthenaList | Force application list |
| 0xA1 | float | Ball radius (from SIZE param) |
| 0xC74 | int | Collision counter |
| 0xC84 | Quaternion | Object rotation |
| Address | Value | Description |
|---|---|---|
| 0x4CF368 | float | Collision radius threshold |
| 0x4CF374 | float | Force multiplier (C5C flag set) |
| 0x4CF378 | float | Force multiplier (shrunk state) |
| 0x4CF380 | float | Force scaling (first frame) |
| 0x4CF3E8 | float | Force direction multiplier |
| 0x4CF48C | float | Gravity/height constant |