[v0.1.0-beta.2] Added static variables; improved UI
- Added api.getVersion() - Added static variables - Improved UI - Added buttons to show/hide UI elements - Added button to reload managers
This commit is contained in:
parent
5449b8f03d
commit
393a9fcd2f
21 changed files with 846 additions and 101 deletions
|
@ -1,4 +1,10 @@
|
|||
# Changelog
|
||||
## v0.1.0-beta.2 (2023/07/24)
|
||||
- Added api.getVersion()
|
||||
- Added static variables
|
||||
- Improved UI
|
||||
- Added buttons to show/hide UI elements
|
||||
- Added button to reload managers
|
||||
## v0.1.0-beta.1 (2023/07/23)
|
||||
- Implemented ObjectViewer
|
||||
- load/reload `ObjectParameters/*.json`
|
||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -854,7 +854,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||
|
||||
[[package]]
|
||||
name = "sup-smsac"
|
||||
version = "0.1.0-beta.1"
|
||||
version = "0.1.0-beta.2"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"encoding_rs",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "sup-smsac"
|
||||
version = "0.1.0-beta.1"
|
||||
version = "0.1.0-beta.2"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["sup39 <sms@sup39.dev>"]
|
||||
|
|
47
res/ObjectParameters/J3DFrameCtrl.json
Normal file
47
res/ObjectParameters/J3DFrameCtrl.json
Normal file
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"J3DFrameCtrl": {
|
||||
"size": 20,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "0",
|
||||
"type": "void*",
|
||||
"name": "vtable",
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "4",
|
||||
"type": "u8",
|
||||
"name": "field_0x4",
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "5",
|
||||
"type": "u8",
|
||||
"format": "hex",
|
||||
"name": "Flag",
|
||||
"notes": "",
|
||||
"hidden": false
|
||||
},
|
||||
{
|
||||
"offset": "8",
|
||||
"type": "s16",
|
||||
"name": "Length",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "c",
|
||||
"type": "float",
|
||||
"name": "Step",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "10",
|
||||
"type": "float",
|
||||
"name": "Counter",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -12,7 +12,8 @@
|
|||
"offset": "20",
|
||||
"type": "JStage::TActor",
|
||||
"name": "Inherited fields",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "24",
|
||||
|
|
31
res/ObjectParameters/MActor.json
Normal file
31
res/ObjectParameters/MActor.json
Normal file
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"MActor": {
|
||||
"size": 72,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "0",
|
||||
"type": "MActorAnmData*",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "4",
|
||||
"type": "J3DModel*",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "c",
|
||||
"type": "MActorAnmBck*",
|
||||
"name": "Current Animation",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "28",
|
||||
"type": "MActorAnmBck**",
|
||||
"name": "Animations?",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
19
res/ObjectParameters/MActorAnmBck.json
Normal file
19
res/ObjectParameters/MActorAnmBck.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"MActorAnmBck": {
|
||||
"size": 60,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "0",
|
||||
"type": "s32",
|
||||
"name": "id",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "4",
|
||||
"type": "J3DFrameCtrl",
|
||||
"name": "Frame *",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
59
res/ObjectParameters/TApplication.json
Normal file
59
res/ObjectParameters/TApplication.json
Normal file
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"TApplication": {
|
||||
"size": 80,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "0",
|
||||
"type": "TApplication*",
|
||||
"name": "Pointer to itself",
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "4",
|
||||
"type": "TDirector*",
|
||||
"name": "Current director",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "8",
|
||||
"type": "u8",
|
||||
"name": "Director type",
|
||||
"notes": "Director type\n2: [null] setup for 3? (after boot)\n3: [TGCLogoDir] Show Nintendo logo\n4: [TMovieDirector] Title screen FMV\n5: [TMarDirector/TMovieDirector] Regular game play (including most FMV)\n6: [TMovieDirector] other independent FMV (e.g. FLUDD tutorial, BH2 warp zone)\n7: Error? \n8: [TSelectDir] shine select\n9: [TMenuDirector] level select (for debug purpose?)"
|
||||
},
|
||||
{
|
||||
"offset": "a",
|
||||
"type": "u16",
|
||||
"format": "hex",
|
||||
"name": "Previous area",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "e",
|
||||
"type": "u16",
|
||||
"format": "hex",
|
||||
"name": "Current area",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "12",
|
||||
"type": "u16",
|
||||
"format": "hex",
|
||||
"name": "Next area",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "18",
|
||||
"type": "u16",
|
||||
"name": "Current FMV",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "38",
|
||||
"type": "s32",
|
||||
"name": "Current save file",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
55
res/ObjectParameters/TBiancoGateKeeper.json
Normal file
55
res/ObjectParameters/TBiancoGateKeeper.json
Normal file
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"TBiancoGateKeeper": {
|
||||
"size": 672,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "0",
|
||||
"type": "TSpineEnemy",
|
||||
"name": "Inherited fields",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "13c",
|
||||
"type": "u8",
|
||||
"name": "HP",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "154",
|
||||
"type": "u32",
|
||||
"name": "Received water count",
|
||||
"notes": "Increased in TGateKeeperBase::receiveMessage()\nReset in TGateKeeperBase::perform() every frame"
|
||||
},
|
||||
{
|
||||
"offset": "17c",
|
||||
"type": "s16",
|
||||
"name": "Total water damage (~255)",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "17e",
|
||||
"type": "s16",
|
||||
"name": "Water damage timer (QF)",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "292",
|
||||
"type": "u8",
|
||||
"name": "GateKeeper type",
|
||||
"notes": "[0] in AP\n[1] in BH [2] BH unlock\n[3] RH unlock\n[4] GB unlock"
|
||||
},
|
||||
{
|
||||
"offset": "296",
|
||||
"type": "s16",
|
||||
"name": "Death count",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "298",
|
||||
"type": "s16",
|
||||
"name": "Timer to launch Goro (BH only)",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -61,14 +61,15 @@
|
|||
"offset": "60",
|
||||
"type": "float",
|
||||
"name": "Entry radius",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "64",
|
||||
"type": "u32",
|
||||
"format": "hex",
|
||||
"name": "Collision flag\n (0) active / (1) no collision",
|
||||
"notes": ""
|
||||
"name": "Collision flag",
|
||||
"notes": "(0) active / (1) no collision"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -18,32 +18,22 @@
|
|||
{
|
||||
"offset": "74",
|
||||
"type": "MActor*",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
"name": "MActor",
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": ["74", "c", "0"],
|
||||
"type": "s32",
|
||||
"name": "Animation id",
|
||||
"type": "MActorAnmBck",
|
||||
"name": "Current Animation *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": ["74", "28", "0", "14"],
|
||||
"type": "float",
|
||||
"name": "Animation frame counter",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": ["74", "28", "0", "c"],
|
||||
"type": "s16",
|
||||
"name": "Animation frame length",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": ["74", "28", "0", "10"],
|
||||
"type": "float",
|
||||
"name": "Animation frame rate",
|
||||
"notes": ""
|
||||
"offset": ["74", "28", "0", "0"],
|
||||
"type": "MActorAnmBck",
|
||||
"name": "Animations[0] *",
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "7c",
|
||||
|
@ -70,7 +60,8 @@
|
|||
"offset": "8c",
|
||||
"type": "TSpineBase<TLiveActor>*",
|
||||
"name": "AI",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": ["8c", "1c", "0"],
|
||||
|
@ -94,37 +85,43 @@
|
|||
"offset": "94",
|
||||
"type": "JGeometry::TVec3<float>",
|
||||
"name": "* Movement (unit/step)",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "ac",
|
||||
"type": "JGeometry::TVec3<float>",
|
||||
"name": "* Speed (unit/step)",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "bc",
|
||||
"type": "float",
|
||||
"name": "Wall hitbox width",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "c4",
|
||||
"type": "TBGCheckData*",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
"name": "Ground",
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "c8",
|
||||
"type": "float",
|
||||
"name": "Ground Height",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "cc",
|
||||
"type": "float",
|
||||
"name": "Gravity (unit/step²)",
|
||||
"notes": ""
|
||||
"notes": "",
|
||||
"hidden": true
|
||||
},
|
||||
{
|
||||
"offset": "f0",
|
||||
|
|
62
res/ObjectParameters/TMarDirector TDemoInfo.json
Normal file
62
res/ObjectParameters/TMarDirector TDemoInfo.json
Normal file
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"TMarDirector::TDemoInfo": {
|
||||
"size": 34,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "0",
|
||||
"type": "string",
|
||||
"name": "BCK name",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": ["4", "0"],
|
||||
"type": "JGeometry::TVec3<float>",
|
||||
"name": "location.*",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "8",
|
||||
"type": "s32",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "c",
|
||||
"type": "float",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "10",
|
||||
"type": "u8",
|
||||
"name": "",
|
||||
"notes": "bool"
|
||||
},
|
||||
{
|
||||
"offset": "14",
|
||||
"type": "void*",
|
||||
"name": "Callback",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "18",
|
||||
"type": "u32",
|
||||
"format": "hex",
|
||||
"name": "Callback parameter",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "1c",
|
||||
"type": "TActor*",
|
||||
"name": "TActor*",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "20",
|
||||
"type": "s16",
|
||||
"name": "flag",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
166
res/ObjectParameters/TMarDirector.json
Normal file
166
res/ObjectParameters/TMarDirector.json
Normal file
|
@ -0,0 +1,166 @@
|
|||
{
|
||||
"TMarDirector": {
|
||||
"size": 616,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "4c",
|
||||
"type": "u16",
|
||||
"format": "hex",
|
||||
"name": "Game mode flags",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "4e",
|
||||
"type": "u16",
|
||||
"format": "hex",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "50",
|
||||
"type": "u16",
|
||||
"format": "hex",
|
||||
"name": "",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "54",
|
||||
"type": "u32",
|
||||
"name": "QF synchronizer",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "58",
|
||||
"type": "u32",
|
||||
"name": "Game QF",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "5c",
|
||||
"type": "u32",
|
||||
"name": "Global QF",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "60",
|
||||
"type": "u32",
|
||||
"name": "Started QF of cutscene",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "64",
|
||||
"type": "u8",
|
||||
"name": "Game state",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "7c",
|
||||
"type": "s8",
|
||||
"name": "Current stage",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "7d",
|
||||
"type": "s8",
|
||||
"name": "Current episode",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "b4",
|
||||
"type": "u8",
|
||||
"name": "Next director type",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "124",
|
||||
"type": "u8",
|
||||
"name": "Current game mode state",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "125",
|
||||
"type": "u8",
|
||||
"name": "Previous game mode state",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "126",
|
||||
"type": "u8",
|
||||
"name": "Next game mode state",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "12c",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot0 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "150",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot1 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "174",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot2 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "198",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot3 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "1bc",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot4 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "1e0",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot5 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "204",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot6 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "228",
|
||||
"type": "TMarDirector::TDemoInfo",
|
||||
"name": "slot7 *",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "24c",
|
||||
"type": "u8",
|
||||
"name": "Next cutscene index to assign",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "24d",
|
||||
"type": "u8",
|
||||
"name": "Next cutscene index to play",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "260",
|
||||
"type": "u8",
|
||||
"name": "Stage load done",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "261",
|
||||
"type": "s8",
|
||||
"name": "Prompt id",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
19
res/ObjectParameters/TMarDirector@QF.json
Normal file
19
res/ObjectParameters/TMarDirector@QF.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"TMarDirector@QF": {
|
||||
"size": 616,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "58",
|
||||
"type": "u32",
|
||||
"name": "Game QF",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "5c",
|
||||
"type": "u32",
|
||||
"name": "Global QF",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
131
res/ObjectParameters/TMario.json
Normal file
131
res/ObjectParameters/TMario.json
Normal file
|
@ -0,0 +1,131 @@
|
|||
{
|
||||
"TMario": {
|
||||
"size": 17040,
|
||||
"offsets": [
|
||||
{
|
||||
"offset": "0",
|
||||
"type": "TTakeActor",
|
||||
"name": "Inherited fields",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "7c",
|
||||
"type": "u32",
|
||||
"format": "hex",
|
||||
"name": "Current state",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "80",
|
||||
"type": "u32",
|
||||
"format": "hex",
|
||||
"name": "Previous state",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "84",
|
||||
"type": "u16",
|
||||
"name": "Substate",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "86",
|
||||
"type": "u16",
|
||||
"name": "Substate timer",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "8c",
|
||||
"type": "float",
|
||||
"name": "Base acceleration (0-32)",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "90",
|
||||
"type": "s16",
|
||||
"name": "Acceleration direction",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "94",
|
||||
"type": "s16",
|
||||
"name": "X angle",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "96",
|
||||
"type": "s16",
|
||||
"name": "Y angle (yaw)",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "98",
|
||||
"type": "s16",
|
||||
"name": "Z angle",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "a4",
|
||||
"type": "JGeometry::TVec3<float>",
|
||||
"name": "* Speed",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "b0",
|
||||
"type": "float",
|
||||
"name": "Forward speed",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "e0",
|
||||
"type": "TBGCheckData*",
|
||||
"name": "Floor triangle under Mario",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "e8",
|
||||
"type": "float",
|
||||
"name": "Height of the ceiling above Mario",
|
||||
"notes": "9,999,999 if none"
|
||||
},
|
||||
{
|
||||
"offset": "ec",
|
||||
"type": "float",
|
||||
"name": "Height of the floor below Mario",
|
||||
"notes": "-32,767 if none"
|
||||
},
|
||||
{
|
||||
"offset": "f0",
|
||||
"type": "float",
|
||||
"name": "Height of the water surface at Mario’s position",
|
||||
"notes": "Y position if none"
|
||||
},
|
||||
{
|
||||
"offset": "118",
|
||||
"type": "u32",
|
||||
"name": "Mario flags",
|
||||
"format": "hex",
|
||||
"notes": "& 0x00000001: \n& 0x00000002: Above a sewer floor?\n& 0x00000004: Is visible?\n& 0x00000008: Talking to NPC?\n& 0x00000010: Left water recently?\n& 0x00000020: Is in shadow\n& 0x00000040: Is in goop\n& 0x00000080: Filling FLUDD\n& 0x00000100: Is NOT on wire (starts off?)\n& 0x00000200: \n& 0x00000400: Is \"TOO BAD!\"?\n& 0x00000800: Is Ground Pound sit up?\n& 0x00001000: Has helmet?\n& 0x00002000: \n& 0x00004000: Is turbo boosting\n& 0x00008000: Has FLUDD?\n& 0x00010000: Is standing in water?\n& 0x00020000: Is in water?\n& 0x00040000: Is above sand?\n& 0x00080000: \n& 0x00100000: \n& 0x00200000: \n& 0x00400000: \n& 0x00800000: \n& 0x01000000: \n& 0x02000000: \n& 0x04000000: \n& 0x08000000: \n& 0x10000000: \n& 0x20000000: \n& 0x40000000: \n& 0x80000000: \n"
|
||||
},
|
||||
{
|
||||
"offset": "11C",
|
||||
"type": "u32",
|
||||
"format": "hex",
|
||||
"name": "Previous Mario flags",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "120",
|
||||
"type": "s16",
|
||||
"name": "HP",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"offset": "12c",
|
||||
"type": "float",
|
||||
"name": "Underwater HP",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -263,6 +263,11 @@ pub async fn handle_command(
|
|||
})
|
||||
},
|
||||
|
||||
"getVersion" => {
|
||||
let_dolphin!(d);
|
||||
Ok(json!(d.ver().to_string()))
|
||||
},
|
||||
|
||||
"reload" => {
|
||||
let mut lock_obj_params = env.obj_params_result.lock().await;
|
||||
load_obj_params(&env.obj_params_dir)
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
pub enum SMSVersion {
|
||||
GMSJ01, GMSE01, GMSP01, GMSJ0A,
|
||||
}
|
||||
pub mod vt;
|
||||
impl std::fmt::Display for SMSVersion {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
std::fmt::Debug::fmt(self, fmt)
|
||||
}
|
||||
}
|
||||
|
||||
use crate::addr::Addr;
|
||||
use crate::dolphin::{DolphinMemory, Dolphin};
|
||||
|
@ -20,6 +24,7 @@ impl Dolphin for SMSDolphin {
|
|||
}
|
||||
}
|
||||
|
||||
pub mod vt;
|
||||
impl SMSDolphin {
|
||||
#[inline]
|
||||
pub fn pid(&self) -> usize {
|
||||
|
|
20
www/api.js
20
www/api.js
|
@ -4,6 +4,7 @@
|
|||
// @ts-check
|
||||
/**
|
||||
* @typedef {number|number[]} ReqAddr
|
||||
* @typedef {'GMSJ01'|'GMSE01'|'GMSP01'|'GMSJ0A'} SMSVersion
|
||||
*/
|
||||
|
||||
/** @param {string} s */
|
||||
|
@ -27,10 +28,10 @@ function Client({onClose = null}={}) {
|
|||
/**
|
||||
* @template T
|
||||
* @param {string} action
|
||||
* @param {any} payload
|
||||
* @param {any} [payload]
|
||||
* @returns {Promise<T>}
|
||||
*/
|
||||
const request = (action, payload) => new Promise((rsv, rjt) => {
|
||||
const request = (action, payload=null) => new Promise((rsv, rjt) => {
|
||||
if (ws == null) throw Error('Client is not connected to server. Use `client.connect()` first.');
|
||||
const id = nextId++;
|
||||
reqs.set(id, {rsv, rjt});
|
||||
|
@ -62,7 +63,7 @@ function Client({onClose = null}={}) {
|
|||
/**
|
||||
* @returns {Promise<number|null>}
|
||||
*/
|
||||
init: () => request('init', null),
|
||||
init: () => request('init'),
|
||||
|
||||
/**
|
||||
* @param {ReqAddr} addr
|
||||
|
@ -123,16 +124,19 @@ function Client({onClose = null}={}) {
|
|||
*/
|
||||
getFields: type => request('getFields', type),
|
||||
|
||||
getManagers: () => request('getManagers', 0)
|
||||
.then((/**@type{[addr: number, cls: string, name: string, count: number][]}*/rows) =>
|
||||
rows.map(row => ({addr: row[0], cls: row[1], name: row[2], count: row[3]}))),
|
||||
getManagers: () => request('getManagers')
|
||||
.then((/**@type{[addr: number, type: string, name: string, count: number][]|null}*/rows) =>
|
||||
rows?.map(row => ({addr: row[0], type: row[1], name: row[2], count: row[3]})) ?? []),
|
||||
|
||||
/**
|
||||
* @param {ReqAddr} addr
|
||||
*/
|
||||
getManagees: addr => request('getManagees', addr)
|
||||
.then((/**@type{[addr: number, cls: string, name: string][]}*/rows) =>
|
||||
rows.map(row => ({addr: row[0], cls: row[1], name: row[2]}))),
|
||||
.then((/**@type{[addr: number, type: string, name: string][]|null}*/rows) =>
|
||||
rows?.map(row => ({addr: row[0], type: row[1], name: row[2]})) ?? []),
|
||||
|
||||
/** @returns {Promise<SMSVersion>} */
|
||||
getVersion: () => request('getVersion'),
|
||||
|
||||
reload: () => request('reload', null),
|
||||
},
|
||||
|
|
|
@ -36,18 +36,28 @@ table.list td {
|
|||
padding: 1px 0.3em;
|
||||
white-space: pre-line;
|
||||
}
|
||||
#managers td:nth-child(1),
|
||||
#managers td:nth-child(5) {
|
||||
#managerList td:nth-child(1),
|
||||
#managerList td:nth-child(5) {
|
||||
padding: 0 2px
|
||||
}
|
||||
#managers tr.managee > td:nth-child(2) {
|
||||
#managerList tr.managee > td:nth-child(2) {
|
||||
padding-left: 1em
|
||||
}
|
||||
|
||||
#fields-viewer td:nth-child(1),
|
||||
#fields-viewer td:nth-child(3) {
|
||||
#fieldsViewer h3 {
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 0.5em;
|
||||
}
|
||||
#fieldsViewer td:nth-child(1),
|
||||
#fieldsViewer td:nth-child(3) {
|
||||
text-align: right;
|
||||
}
|
||||
#fieldsViewer td:nth-child(4) {
|
||||
display: none;
|
||||
}
|
||||
#fieldsViewer.showNotes td:nth-child(4) {
|
||||
display: unset;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
|
@ -55,11 +65,23 @@ table.list td {
|
|||
}
|
||||
.flex > * {
|
||||
margin-right: 4px;
|
||||
margin-block-end: 1em;
|
||||
}
|
||||
body.wrap .flex {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
main {
|
||||
visibility: hidden;
|
||||
}
|
||||
body.ready main {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
|
@ -68,7 +90,7 @@ body {
|
|||
font-size: 14px;
|
||||
padding: 4px;
|
||||
}
|
||||
body.disconnected #msg {
|
||||
body.error #msg {
|
||||
background: var(--bg-red);
|
||||
}
|
||||
h1 {
|
||||
|
@ -83,6 +105,10 @@ header {
|
|||
margin-block-end: 0.75em;
|
||||
padding-left: 4px;
|
||||
}
|
||||
section {
|
||||
margin-block-end: 0.5em;
|
||||
}
|
||||
|
||||
details {
|
||||
border: solid 1px var(--fg);
|
||||
padding: 0.5em 1em;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SMS Web Object Viewer (v0.1.0-beta.1)</title>
|
||||
<title>SMS Web Object Viewer (v0.1.0-beta.2)</title>
|
||||
<link rel="stylesheet" type="text/css" href="index.css">
|
||||
<link rel="icon" type="image/svg+xml" href="icon.svg">
|
||||
<script src="api.js"></script>
|
||||
<script src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SMS Web Object Viewer</h1>
|
||||
<h1>SMS Web Object Viewer (v0.1.0-beta.2)</h1>
|
||||
<header>
|
||||
<div id="msg"></div>
|
||||
<details>
|
||||
|
@ -20,13 +20,27 @@
|
|||
<iframe id="license" src="/LICENSE.html" title="license"></iframe>
|
||||
</details>
|
||||
</header>
|
||||
<section>
|
||||
<button id="btn-reload" class="hidden">Reload Object Parameters</button>
|
||||
</section>
|
||||
<section class="flex-wrapper">
|
||||
<div class="flex">
|
||||
<table id="managers" class="list"></table>
|
||||
<table id="fields-viewer" class="list"></table>
|
||||
</div>
|
||||
</section>
|
||||
<main>
|
||||
<section>
|
||||
<button id="btnReloadManagers">Reload Managers</button>
|
||||
<input type="checkbox" id="cbShowManagers" checked>
|
||||
<label for="cbShowManagers">Show Manager List</label>
|
||||
<input type="checkbox" id="cbShowObjParamsNotes">
|
||||
<label for="cbShowObjParamsNotes">Show Notes of Object Parameters</label>
|
||||
<input type="checkbox" id="cbWrapFlex">
|
||||
<label for="cbWrapFlex">Wrap UI</label>
|
||||
<button id="btnReloadObjParams">Reload ObjectParameters</button>
|
||||
</section>
|
||||
<section class="flex-wrapper">
|
||||
<div class="flex">
|
||||
<div id="managerList">
|
||||
<table class="list"></table>
|
||||
</div>
|
||||
<div id="fieldsViewer" class="hidden">
|
||||
<h3></h3>
|
||||
<table class="list"></table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
|
|
179
www/index.js
179
www/index.js
|
@ -3,8 +3,8 @@
|
|||
|
||||
// @ts-check
|
||||
/**
|
||||
* @typedef {{addr: number, cls: string, name: string, count: number}} Manager
|
||||
* @typedef {{addr: number, cls: string, name: string}} Managee
|
||||
* @typedef {{addr: number, type: string, name: string, count: number}} Manager
|
||||
* @typedef {{addr: number, type: string, name: string}} Managee
|
||||
* @typedef {(td: HTMLTableCellElement) => void} CellFactory
|
||||
* @typedef {CellFactory[]} RowFactory
|
||||
* @typedef {{name: string, notes: string, offset: string|string[], type: string}} Field
|
||||
|
@ -16,12 +16,6 @@
|
|||
const fmt = {
|
||||
/** @param {number} x */
|
||||
hex: x => x.toString(16).toUpperCase(),
|
||||
/** @type {(x: number) => string} */
|
||||
float: (LOG_10_2 => x => {
|
||||
const u = Math.floor(LOG_10_2*(Math.log2(Math.abs(x))-23));
|
||||
return x === 0 ? '0.0' : u > 0 || u < -8 ? x.toExponential(7) :
|
||||
x.toFixed(-u);
|
||||
})(Math.log10(2)),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -29,38 +23,113 @@ const fmt = {
|
|||
* @param {((td: HTMLTableCellElement)=>void)[][]} gTable
|
||||
*/
|
||||
function initTable(table, gTable) {
|
||||
const nRow = gTable.length;
|
||||
for (let r=table.rows.length; r<nRow; r++) table.insertRow();
|
||||
for (let r=table.rows.length; r>nRow; r--) table.deleteRow(-1);
|
||||
gTable.forEach((gRow, r) => {
|
||||
const row = table.rows[r];
|
||||
const nCol = gRow.length;
|
||||
for (let c=row.cells.length; c<nCol; c++) row.insertCell();
|
||||
for (let c=row.cells.length; c>nCol; c--) row.deleteCell(-1);
|
||||
gRow.forEach((g, c) => g(row.cells[c]));
|
||||
const nRow0 = table.rows.length;
|
||||
for (let r=0; r<nRow0; r++) table.deleteRow(-1);
|
||||
gTable.forEach(gRow => {
|
||||
const row = table.insertRow();
|
||||
gRow.forEach(g => g(row.insertCell()));
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
const elmMsg = /**@type {HTMLDivElement}*/(document.getElementById('msg'));
|
||||
const btnReload = /**@type {HTMLButtonElement}*/(document.getElementById('btn-reload'));
|
||||
const elmManagers = /**@type {HTMLTableElement}*/(document.getElementById('managers'));
|
||||
const elmFieldsViewer = /**@type {HTMLTableElement}*/(document.getElementById('fields-viewer'));
|
||||
const btnReloadObjParams = /**@type {HTMLButtonElement}*/(document.getElementById('btnReloadObjParams'));
|
||||
const btnReloadManagers = /**@type {HTMLButtonElement}*/(document.getElementById('btnReloadManagers'));
|
||||
const cbShowManagers = /**@type {HTMLInputElement}*/(document.getElementById('cbShowManagers'));
|
||||
const cbShowObjParamsNotes = /**@type {HTMLInputElement}*/(document.getElementById('cbShowObjParamsNotes'));
|
||||
const cbWrapFlex = /**@type {HTMLInputElement}*/(document.getElementById('cbWrapFlex'));
|
||||
|
||||
/** @param {string} msg */
|
||||
function showError(msg) {
|
||||
elmMsg.textContent = msg;
|
||||
document.body.classList.add('error');
|
||||
}
|
||||
const client = Client({
|
||||
onClose() {
|
||||
elmMsg.textContent = `Disconnected from server. Please reload the page.`;
|
||||
document.body.classList.add('disconnected');
|
||||
},
|
||||
onClose: () => showError(`Disconnected from server. Please reload the page.`),
|
||||
});
|
||||
const {api} = client;
|
||||
Object.assign(window, {client, api}); // TODO
|
||||
|
||||
/**************** UI definition ****************/
|
||||
/**
|
||||
* @param {HTMLTableElement} elm
|
||||
* @param {HTMLElement|null} elm
|
||||
*/
|
||||
function ManagerList(elm) {
|
||||
if (elm == null) throw new Error('ManagerList not found');
|
||||
const elmTable = (() => {
|
||||
const e = elm.querySelector('table');
|
||||
if (e == null) throw new Error('table should present in ManagerList');
|
||||
return e;
|
||||
})();
|
||||
// TODO put in a json file
|
||||
const staticVariables = [
|
||||
{
|
||||
name: 'gpApplication',
|
||||
addrs: [0x803E6000, 0x803E9700, 0x803E10C0, 0x803DA8E0],
|
||||
type: 'TApplication',
|
||||
},
|
||||
{
|
||||
name: 'gpMarDirector',
|
||||
addrs: [0x8040A2A8, 0x8040E178, 0x80405840, 0x803FF018],
|
||||
type: 'TMarDirector*',
|
||||
},
|
||||
{
|
||||
name: 'QF',
|
||||
addrs: [0x8040A2A8, 0x8040E178, 0x80405840, 0x803FF018],
|
||||
type: 'TMarDirector@QF*',
|
||||
},
|
||||
{
|
||||
name: 'マリオ',
|
||||
addrs: [0x8040A378, 0x8040E0E8, 0x804057B0, 0x803FEF88],
|
||||
type: 'TMario*',
|
||||
},
|
||||
];
|
||||
return {
|
||||
get classList() {
|
||||
return elm.classList;
|
||||
},
|
||||
async reload() {
|
||||
const [vars, managers] = await Promise.all([
|
||||
api.getVersion().then(async ver => {
|
||||
const iver = ['GMSJ01', 'GMSE01', 'GMSP01', 'GMSJ0A'].indexOf(ver);
|
||||
return await Promise.all(staticVariables.map(async o => {
|
||||
const ptrlv = o.type.match(/\*+$/)?.[0].length ?? 0;
|
||||
const type = o.type.substring(0, o.type.length-ptrlv);
|
||||
const addr0 = o.addrs[iver];
|
||||
const addr = ptrlv === 0 ? addr0 : await api.readBytes(
|
||||
[addr0].concat(...Array(ptrlv-1).fill(0)), 4,
|
||||
).then(dv => dv?.getUint32(0) ?? 0);
|
||||
return {name: o.name, addr, type};
|
||||
}));
|
||||
}),
|
||||
api.getManagers(),
|
||||
]);
|
||||
fieldsViewer.reset();
|
||||
initTable(elmTable, [
|
||||
...vars.map(o => makeManageesRowFactory(o)),
|
||||
...managers.map(makeManagersRowFactory),
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
const managerList = ManagerList(document.getElementById('managerList'));
|
||||
|
||||
/**
|
||||
* @param {HTMLElement|null} elm
|
||||
*/
|
||||
function FieldsViewer(elm) {
|
||||
if (elm == null) throw new Error('FieldsViewer not found');
|
||||
const elmTitle = (() => {
|
||||
const e = elm.querySelector('h3');
|
||||
if (e == null) throw new Error('h3 should present in FieldsViewer');
|
||||
return e;
|
||||
})();
|
||||
const elmTable = (() => {
|
||||
const e = elm.querySelector('table');
|
||||
if (e == null) throw new Error('table should present in FieldsViewer');
|
||||
return e;
|
||||
})();
|
||||
// states
|
||||
const tdidxVal = 2;
|
||||
let hAnm = NaN;
|
||||
let t0 = 0;
|
||||
|
@ -68,19 +137,22 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
let target = null;
|
||||
async function readValues() {
|
||||
if (target == null) return [];
|
||||
const values = await api.read([target.addr], target.cls);
|
||||
const values = await api.read([target.addr], target.type);
|
||||
return values instanceof Array ? values : [values];
|
||||
}
|
||||
/** @param {DOMHighResTimeStamp} t */
|
||||
async function render(t) {
|
||||
if (t-t0 >= 33) { // TODO configurable fps
|
||||
(await readValues())
|
||||
.forEach((s, i) => elm.rows[i].cells[tdidxVal].textContent = s);
|
||||
.forEach((s, i) => elmTable.rows[i].cells[tdidxVal].textContent = s);
|
||||
t0 = t;
|
||||
}
|
||||
hAnm = requestAnimationFrame(render);
|
||||
}
|
||||
const methods = {
|
||||
get classList() {
|
||||
return elm.classList;
|
||||
},
|
||||
reload() {
|
||||
api.reload().then(() => {
|
||||
target != null && methods.view(target);
|
||||
|
@ -88,34 +160,55 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
elmMsg.textContent = err;
|
||||
});
|
||||
},
|
||||
reset() {
|
||||
elm.classList.add('hidden');
|
||||
},
|
||||
/** @param {Manager|Managee} o */
|
||||
async view(o) {
|
||||
btnReload.classList.remove('hidden');
|
||||
cancelAnimationFrame(hAnm);
|
||||
target = o;
|
||||
const fields = await api.getFields(o.cls);
|
||||
elmTitle.textContent = `${o.name} (${o.type}) [${fmt.hex(o.addr)}]`;
|
||||
const fields = await api.getFields(o.type);
|
||||
const values = readValues();
|
||||
initTable(elm, fields.map((r, i) => [
|
||||
initTable(elmTable, fields.map((r, i) => [
|
||||
td => td.textContent = r[0],
|
||||
td => td.textContent = r[1],
|
||||
td => td.textContent = values[i],
|
||||
// td => td.textContent = r[2], // TODO
|
||||
td => td.textContent = r[2],
|
||||
td => td.textContent = r[3],
|
||||
td => td.textContent = r[4],
|
||||
]));
|
||||
elm.classList.remove('hidden');
|
||||
if (fields.length) hAnm = requestAnimationFrame(render);
|
||||
},
|
||||
}
|
||||
return methods;
|
||||
}
|
||||
const fieldsViewer = FieldsViewer(elmFieldsViewer);
|
||||
btnReload.addEventListener('click', () => {
|
||||
const fieldsViewer = FieldsViewer(document.getElementById('fieldsViewer'));
|
||||
btnReloadObjParams.addEventListener('click', () => {
|
||||
fieldsViewer.reload();
|
||||
});
|
||||
btnReloadManagers.addEventListener('click', async () => {
|
||||
managerList.reload();
|
||||
});
|
||||
cbShowManagers.addEventListener('change', function () {
|
||||
managerList.classList[this.checked ? 'remove' : 'add']('hidden');
|
||||
});
|
||||
cbShowObjParamsNotes.addEventListener('change', function () {
|
||||
fieldsViewer.classList[this.checked ? 'add' : 'remove']('showNotes');
|
||||
});
|
||||
cbWrapFlex.addEventListener('change', function () {
|
||||
// TODO
|
||||
document.body.classList[this.checked ? 'add' : 'remove']('wrap');
|
||||
});
|
||||
|
||||
/** @type {(o: Manager) => RowFactory} */
|
||||
const makeManagersRowFactory = o => [
|
||||
td => {
|
||||
// remove old button
|
||||
const btn0 = td.querySelector('button');
|
||||
if (btn0) td.removeChild(btn0);
|
||||
// create new button
|
||||
const btn = document.createElement('button');
|
||||
let open = false;
|
||||
/** @type {HTMLTableRowElement[] | null} */
|
||||
|
@ -146,22 +239,26 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
td.appendChild(btn);
|
||||
},
|
||||
td => td.textContent = `${o.name} (${o.count})`,
|
||||
td => td.textContent = `${o.cls}`,
|
||||
td => td.textContent = `${o.type}`,
|
||||
td => td.textContent = `${fmt.hex(o.addr)}`,
|
||||
makeViewerNavigatorFactory(o),
|
||||
];
|
||||
|
||||
/** @type {(o: Managee, i: number) => ((td: HTMLTableCellElement) => void)[]} */
|
||||
/** @type {(o: Managee, i?: number) => ((td: HTMLTableCellElement) => void)[]} */
|
||||
const makeManageesRowFactory = (o, i) => [
|
||||
_ => {},
|
||||
td => td.textContent = `${i}: (${o.name})`,
|
||||
td => td.textContent = `${o.cls}`,
|
||||
td => td.textContent = i == null ? o.name : `${i}: ${o.name}`,
|
||||
td => td.textContent = `${o.type}`,
|
||||
td => td.textContent = `${fmt.hex(o.addr)}`,
|
||||
makeViewerNavigatorFactory(o),
|
||||
];
|
||||
|
||||
/** @type {(o: Manager|Managee) => CellFactory} */
|
||||
const makeViewerNavigatorFactory = o => td => {
|
||||
// remove old button
|
||||
const btn0 = td.querySelector('button');
|
||||
if (btn0) td.removeChild(btn0);
|
||||
// create new button
|
||||
const btn = document.createElement('button');
|
||||
btn.textContent = '>';
|
||||
btn.addEventListener('click', () => {
|
||||
|
@ -175,11 +272,11 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||
await client.connect(); // TODO url
|
||||
const pid = await api.init();
|
||||
console.log('pid:', pid);
|
||||
document.body.classList.add('ready');
|
||||
|
||||
const managers = await api.getManagers();
|
||||
initTable(elmManagers, managers.map(makeManagersRowFactory));
|
||||
await managerList.reload();
|
||||
} catch(e) {
|
||||
elmMsg.textContent = e;
|
||||
return; // TODO
|
||||
console.log(e);
|
||||
showError(e);
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue