Skip to content

Commit 19bd024

Browse files
authored
Merge pull request #21 from monkeyman192/update/155759.0
Update for 155759 and lots more
2 parents 4d283ca + fcb4f38 commit 19bd024

File tree

13 files changed

+20066
-18922
lines changed

13 files changed

+20066
-18922
lines changed

README.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ The author of this library does not condone any use of this code for any purpose
1414
**Note:**
1515
It is recommended that you download python from the [official site](https://www.python.org/downloads) as the windows store version may have issues, as well as the managed python which [uv](https://docs.astral.sh/uv/) installs.
1616

17+
**Note:**
18+
Python 3.14 is not yet supported. This will come in time but requires some changes to a dependency in pyMHF.
19+
1720
The recommended way to install NMS.py is to simply run `python -m pip install nmspy`. This will install NMS.py and its' dependency [`pyMHF`](https://github.com/monkeyman192/pyMHF) into your system python. You can of course install it in a venv or as a dependency using uv if you prefer.
1821

1922
## Usage
@@ -34,7 +37,22 @@ If you want to stop NMS, you can press `ctrl + C` in the window you started the
3437

3538
Currently the best way to see how to write a mod is to look at the `example_mods` folder, as well as looking at the [pyMHF docs](https://monkeyman192.github.io/pyMHF/) which has comprehensive details on how to use pyMHF.
3639

37-
### Credits
40+
## Contributing
41+
42+
NMS.py can always be better! If you are interested in improving it, the best way to do so is to either make mods using it, or, if you have reverse engineering skills, you can contribute either new functions or new field offsets.
43+
44+
### Adding new functions to hook
45+
46+
To add a new function to hook you need to add the details to `tools/data.json` as well as the `nmspy/data/types.py` file.
47+
The data for these can generally be found by comparing the structure of the function in the latest exe to either the mac binary or the 4.13 binary.
48+
It is recommended to use either [IDA Fusion](https://github.com/senator715/IDA-Fusion) or [SigmakerEX](https://github.com/kweatherman/sigmakerex) for IDA or some other plugin for any other disassembler to get the unique byte pattern for the function you are providing.
49+
50+
### Adding new struct fields
51+
52+
This is a bit trickier and will often involve a good amount of reverse engineering of the exe, comparing the data to the 4.13 binary as well as potentially the mac binary.
53+
It is best if a comment is made above the added field so that it's possible to find it again when the game updates so that it's possible to check if the offset has changed.
54+
55+
## Credits
3856

3957
Thanks to the developers of minhook, cyminhook and pymem, all of which are instrumental in making this framework possible.
4058

nmspy/_internal_mods/singletons.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from pymhf.core.hooking import one_shot, hook_manager
88
from pymhf.core.memutils import map_struct, get_addressof
99
from pymhf import ModState
10-
import nmspy.common as nms_state
10+
from nmspy.common import gameData
1111
import nmspy.data.types as nms
1212
import nmspy.data.basic_types as basic
1313
from pymhf import Mod
@@ -35,7 +35,7 @@ class _INTERNAL_LoadSingletons(Mod):
3535
def fsm_state_change(self, this: ctypes._Pointer[nms.cTkFSM], *args):
3636
# One shot to instantiate the cGcApplication object.
3737
# This logic looks funny, but it's because the cGcApplication has cTkFSM as a base class.
38-
nms_state.GcApplication = map_struct(get_addressof(this), nms.cGcApplication)
38+
gameData.GcApplication = map_struct(get_addressof(this), nms.cGcApplication)
3939

4040
@nms.cTkFSMState.StateChange.after
4141
def state_change(

nmspy/common.py

Lines changed: 28 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,30 @@
1+
from typing import Optional
2+
13
import nmspy.data.types as nms
24

3-
# TODO:
4-
# Store all the globals here like this so that we may access them easily and
5-
# from anywhere.
6-
GcAISpaceshipGlobals = None
7-
GcAtlasGlobals = None
8-
GcAudioGlobals = None
9-
GcBuildingGlobals = None
10-
GcCameraGlobals = None
11-
GcCharacterGlobals = None
12-
GcCreatureGlobals = None
13-
GcDebugOptions = None
14-
GcEffectsGlobals = None
15-
GcEnvironmentGlobals = None
16-
GcFleetGlobals = None
17-
GcFreighterBaseGlobals = None
18-
GcGalaxyGlobals = None
19-
GcGameplayGlobals = None
20-
GcGraphicsGlobals = None
21-
GcMultiplayerGlobals = None
22-
GcPlacementGlobals = None
23-
GcPlayerGlobals = None
24-
GcRichPresenceGlobals = None
25-
GcRobotGlobals = None
26-
GcSceneOptions = None
27-
GcScratchpadGlobals = None
28-
GcSettlementGlobals = None
29-
GcSimulationGlobals = None
30-
GcSkyGlobals = None
31-
GcSmokeTestOptions = None
32-
GcSolarGenerationGlobals = None
33-
GcSpaceshipGlobals = None
34-
GcTerrainGlobals = None
35-
GcUIGlobals = None
36-
GcVehicleGlobals = None
37-
GcWaterGlobals = None
38-
39-
GcApplication: nms.cGcApplication = None # type: ignore
5+
6+
class GameData:
7+
GcApplication: nms.cGcApplication = None # type: ignore
8+
9+
@property
10+
def game_state(self) -> Optional[nms.cGcGameState]:
11+
if self.GcApplication is not None:
12+
return self.GcApplication.mpData.contents.mGameState
13+
14+
@property
15+
def simulation(self) -> Optional[nms.cGcSimulation]:
16+
if self.GcApplication is not None:
17+
return self.GcApplication.mpData.contents.mSimulation
18+
19+
@property
20+
def player(self) -> Optional[nms.cGcPlayer]:
21+
if (sim := self.simulation) is not None:
22+
return sim.mPlayer
23+
24+
@property
25+
def player_state(self) -> Optional[nms.cGcPlayerState]:
26+
if (game_state := self.game_state) is not None:
27+
return game_state.mPlayerState
28+
29+
30+
gameData = GameData()

nmspy/data/basic_types.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ class Vector3f(ctypes.Structure):
107107
("_padding", ctypes.c_byte * 0x4),
108108
]
109109

110+
def __init__(self, x: float, y: float, z: float):
111+
self.x = x
112+
self.y = y
113+
self.z = z
114+
110115
def __iadd__(self, other: "Vector3f"):
111116
self.x += other.x
112117
self.y += other.y
@@ -208,6 +213,9 @@ class cTkPhysRelVec3(ctypes.Structure):
208213
("offset", Vector3f),
209214
]
210215

216+
def __str__(self):
217+
return f"<{self.__qualname__}; local: {str(self.local)}, offset: {str(self.offset)}"
218+
211219

212220
class GcResource(ctypes.Structure):
213221
ResourceID: int
@@ -593,3 +601,5 @@ def __str__(self):
593601
cTkFixedString0x200 = cTkFixedString[0x200]
594602
cTkFixedString0x400 = cTkFixedString[0x400]
595603
cTkFixedString0x800 = cTkFixedString[0x800]
604+
# Vector type aliases
605+
cTkBigPos = cTkPhysRelVec3

nmspy/data/enums/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@
115115
cGcRainbowType,
116116
cGcPlayerConflictData,
117117
cGcSolarSystemClass,
118-
cGcPlanetSentinelLevel,
119118
cGcPlanetSize,
120119
cGcPlanetClass,
120+
cGcPlanetSentinelLevel,
121121
cGcBiomeType,
122122
cGcBiomeSubType,
123123
cGcSolarSystemLocatorTypes,
@@ -252,21 +252,21 @@
252252
cGcRarity,
253253
cGcProceduralProductCategory,
254254
cGcProductCategory,
255+
cGcNameGeneratorSectorTypes,
255256
cGcNameGeneratorTypes,
256257
cGcProceduralTechnologyCategory,
257258
cGcModularCustomisationResourceType,
258259
cGcMaintenanceElementGroups,
259-
cGcNameGeneratorSectorTypes,
260260
cGcInventoryType,
261-
cGcInventoryLayoutSizeType,
262261
cGcItemNeedPurpose,
262+
cGcInventoryLayoutSizeType,
263263
cGcInventorySpecialSlotType,
264264
cGcLegality,
265265
cGcInventoryStackSizeGroup,
266266
cGcFrigateClass,
267267
cGcDiscoveryTrimGroup,
268-
cGcDiscoveryTrimScoringCategory,
269268
cGcFrigateTraitStrength,
269+
cGcDiscoveryTrimScoringCategory,
270270
cGcDiscoveryType,
271271
cGcInventoryClass,
272272
cGcExpeditionCategory,

nmspy/data/enums/external_enums.py

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3515,13 +3515,6 @@ class cGcSolarSystemClass(IntEnum):
35153515
GameStart = 0x3
35163516

35173517

3518-
class cGcPlanetSentinelLevel(IntEnum):
3519-
Low = 0x0
3520-
Default = 0x1
3521-
Aggressive = 0x2
3522-
Corrupt = 0x3
3523-
3524-
35253518
class cGcPlanetSize(IntEnum):
35263519
Large = 0x0
35273520
Medium = 0x1
@@ -3536,6 +3529,13 @@ class cGcPlanetClass(IntEnum):
35363529
InInitialSystem = 0x2
35373530

35383531

3532+
class cGcPlanetSentinelLevel(IntEnum):
3533+
Low = 0x0
3534+
Default = 0x1
3535+
Aggressive = 0x2
3536+
Corrupt = 0x3
3537+
3538+
35393539
class cGcBiomeType(IntEnum):
35403540
Lush = 0x0
35413541
Toxic = 0x1
@@ -5675,6 +5675,21 @@ class cGcProductCategory(IntEnum):
56755675
ExhibitBone = 0xA
56765676

56775677

5678+
class cGcNameGeneratorSectorTypes(IntEnum):
5679+
Generic = 0x0
5680+
Elevated = 0x1
5681+
Low = 0x2
5682+
Trees = 0x3
5683+
LushTrees = 0x4
5684+
Lush = 0x5
5685+
Wet = 0x6
5686+
Cave = 0x7
5687+
Dead = 0x8
5688+
Buildings = 0x9
5689+
Water = 0xA
5690+
Ice = 0xB
5691+
5692+
56785693
class cGcNameGeneratorTypes(IntEnum):
56795694
Generic = 0x0
56805695
Mineral = 0x1
@@ -5721,27 +5736,21 @@ class cGcMaintenanceElementGroups(IntEnum):
57215736
RobotHeads = 0x9
57225737

57235738

5724-
class cGcNameGeneratorSectorTypes(IntEnum):
5725-
Generic = 0x0
5726-
Elevated = 0x1
5727-
Low = 0x2
5728-
Trees = 0x3
5729-
LushTrees = 0x4
5730-
Lush = 0x5
5731-
Wet = 0x6
5732-
Cave = 0x7
5733-
Dead = 0x8
5734-
Buildings = 0x9
5735-
Water = 0xA
5736-
Ice = 0xB
5737-
5738-
57395739
class cGcInventoryType(IntEnum):
57405740
Substance = 0x0
57415741
Technology = 0x1
57425742
Product = 0x2
57435743

57445744

5745+
class cGcItemNeedPurpose(IntEnum):
5746+
None_ = 0x0
5747+
Crafting = 0x1
5748+
Building = 0x2
5749+
Repairing = 0x3
5750+
Charging = 0x4
5751+
Paying = 0x5
5752+
5753+
57455754
class cGcInventoryLayoutSizeType(IntEnum):
57465755
SciSmall = 0x0
57475756
SciMedium = 0x1
@@ -5790,15 +5799,6 @@ class cGcInventoryLayoutSizeType(IntEnum):
57905799
CorvetteStorage = 0x2C
57915800

57925801

5793-
class cGcItemNeedPurpose(IntEnum):
5794-
None_ = 0x0
5795-
Crafting = 0x1
5796-
Building = 0x2
5797-
Repairing = 0x3
5798-
Charging = 0x4
5799-
Paying = 0x5
5800-
5801-
58025802
class cGcInventorySpecialSlotType(IntEnum):
58035803
Broken = 0x0
58045804
TechOnly = 0x1
@@ -5848,17 +5848,6 @@ class cGcDiscoveryTrimGroup(IntEnum):
58485848
Boring = 0x3
58495849

58505850

5851-
class cGcDiscoveryTrimScoringCategory(IntEnum):
5852-
IsNamedSystem = 0x0
5853-
RecentlyVisitedSystem = 0x1
5854-
RecentDiscoveryInSystem = 0x2
5855-
NumDiscoveredPlanetsInSystem = 0x3
5856-
IsNamedPlanet = 0x4
5857-
NumBasesOnPlanet = 0x5
5858-
NumWondersOnPlanet = 0x6
5859-
NumNamedDiscoveries = 0x7
5860-
5861-
58625851
class cGcFrigateTraitStrength(IntEnum):
58635852
NegativeLarge = 0x0
58645853
NegativeMedium = 0x1
@@ -5872,6 +5861,17 @@ class cGcFrigateTraitStrength(IntEnum):
58725861
Primary = 0x9
58735862

58745863

5864+
class cGcDiscoveryTrimScoringCategory(IntEnum):
5865+
IsNamedSystem = 0x0
5866+
RecentlyVisitedSystem = 0x1
5867+
RecentDiscoveryInSystem = 0x2
5868+
NumDiscoveredPlanetsInSystem = 0x3
5869+
IsNamedPlanet = 0x4
5870+
NumBasesOnPlanet = 0x5
5871+
NumWondersOnPlanet = 0x6
5872+
NumNamedDiscoveries = 0x7
5873+
5874+
58755875
class cGcDiscoveryType(IntEnum):
58765876
Unknown = 0x0
58775877
SolarSystem = 0x1

0 commit comments

Comments
 (0)