Lots of changes, expand to read
- Add notes folder with MDBook documentation (the NOTES.md file was getting kind of large) - Add rz_analyze.py, does the same a r2_analyze.py just with Rizin instead of radare2 so the project can be loaded in Cutter (*and* it's faster) - Add Scrap.rzdb, Rizin database for the Scrap.exe executable - Add Scrapper_rs, Rust version of .packed extractor and repacker - replace helplib.txt with helplib.md - add Py_Docs folder which contains generated documentation for the binary python modules built into Scrap.exe
This commit is contained in:
parent
43c01e81d2
commit
7afdfb5869
50 changed files with 483086 additions and 1709 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -261,9 +261,13 @@ __pycache__/
|
|||
*.pyc
|
||||
|
||||
.history
|
||||
**/.history
|
||||
|
||||
ScrapHacks/build/*
|
||||
ScrapHacks/src/D3D8_VMT.hpp
|
||||
.vscode/c_cpp_properties.json
|
||||
tools/*.log
|
||||
frida/*.mp
|
||||
frida/dump.mp.xz
|
||||
terms.py
|
||||
hist_restore.py
|
||||
|
|
121
NOTES.md
121
NOTES.md
|
@ -11,7 +11,6 @@
|
|||
- `-dedicated`: start in mutliplayer dedicated server mode (needs to be used with `-server`)
|
||||
- `-server`: start in multiplayer server mode
|
||||
|
||||
# Functions identified:
|
||||
|
||||
## Ingame-Console (Ctrl+\^ or right click on titlebar and select "switch console") (Handler@`0x402190`):
|
||||
|
||||
|
@ -134,56 +133,57 @@ struct GameVar {
|
|||
|
||||
Types
|
||||
|
||||
| Value | Type |
|
||||
|-------|-----------------|
|
||||
| `0x1` | const char* |
|
||||
| `0x2` | int32_t |
|
||||
| `0x3` | List of Defines |
|
||||
| `0x4` | float |
|
||||
| `0x5` | function |
|
||||
| `0x6` | Script function |
|
||||
Value | Type
|
||||
----- | ---------------
|
||||
`0x1` | const char*
|
||||
`0x2` | int32_t
|
||||
`0x3` | List of Defines
|
||||
`0x4` | float
|
||||
`0x5` | function
|
||||
`0x6` | Script function
|
||||
|
||||
## Game World/State Pointer @ `0x7fe944`
|
||||
|
||||
Points to World struct
|
||||
|
||||
| Offset | Type | Description |
|
||||
|--------|--------------------------|----------------------------------------|
|
||||
| 0x0000 | `void**` | Virtual Method Table |
|
||||
| 0x0004 | `uint32_t` | Size of Entity Hashtable |
|
||||
| 0x0008 | `void**` | Pointer to Entity Hashtable |
|
||||
| 0x0288 | `pyEntity*` | UsrEntity[0] |
|
||||
| 0x028C | `pyEntity*` | UsrEntity[1] |
|
||||
| 0x0290 | `pyEntity*` | UsrEntity[2] |
|
||||
| 0x0294 | `pyEntity*` | UsrEntity[3] |
|
||||
| 0x02B8 | `uint32_t` | Number of entity lists |
|
||||
| 0x02BC | `void**` | Pointer to entity list Hashtable |
|
||||
| 0x0330 | `float[3]` | Time (why 3 times?) |
|
||||
| 0x1C6C | `float` | Alarm level |
|
||||
| 0x1C68 | `float` | Alarm Grow Level |
|
||||
| 0x2158 | `float` | Used in `World_Init` |
|
||||
| 0x2170 | `???` | Used in `World_Init` |
|
||||
| 0x2180 | `float` | Used in `World_Init` |
|
||||
| 0x2188 | `void*` | Used in `World_Init` |
|
||||
| 0x218C | `void*` | Used in `World_Init` |
|
||||
| 0x2190 | `float` | Used in `World_Init` |
|
||||
| 0x2198 | `void*` | Used in `World_Init` |
|
||||
| 0x219C | `void*` | Used in `World_Init` |
|
||||
| 0x21A0 | `void**` | Used in `World_Init` (VTable pointer?) |
|
||||
| 0x21B4 | `void**` | Used in `World_Init` (VTable pointer?) |
|
||||
| 0x21C8 | `???` | Used in `World_Init` |
|
||||
| 0x2204 | `uint32_t` or `uint16_t` | Used in `World_Init` |
|
||||
| 0x2230 | `float` | Used in `World_Init` |
|
||||
| 0x2238 | `???` | Used in `World_Init` |
|
||||
| 0x2254 | `float` | Used in `World_Init` |
|
||||
Offset | Type | Description
|
||||
------ | ------------------------ | --------------------------------------
|
||||
0x0000 | `void**` | Virtual Method Table
|
||||
0x0004 | `uint32_t` | Size of Entity Hashtable
|
||||
0x0008 | `void**` | Pointer to Entity Hashtable
|
||||
0x00B0 | `??` | Pointer to Ground Object (?)
|
||||
0x0288 | `pyEntity*` | UsrEntity[0]
|
||||
0x028C | `pyEntity*` | UsrEntity[1]
|
||||
0x0290 | `pyEntity*` | UsrEntity[2]
|
||||
0x0294 | `pyEntity*` | UsrEntity[3]
|
||||
0x02B8 | `uint32_t` | Number of entity lists
|
||||
0x02BC | `void**` | Pointer to entity list Hashtable
|
||||
0x0330 | `float[3]` | Time (why 3 times?)
|
||||
0x1C6C | `float` | Alarm level
|
||||
0x1C68 | `float` | Alarm Grow Level
|
||||
0x2158 | `float` | Used in `World_Init`
|
||||
0x2170 | `???` | Used in `World_Init`
|
||||
0x2180 | `float` | Used in `World_Init`
|
||||
0x2188 | `void*` | Used in `World_Init`
|
||||
0x218C | `void*` | Used in `World_Init`
|
||||
0x2190 | `float` | Used in `World_Init`
|
||||
0x2198 | `void*` | Used in `World_Init`
|
||||
0x219C | `void*` | Used in `World_Init`
|
||||
0x21A0 | `void**` | Used in `World_Init` (VTable pointer?)
|
||||
0x21B4 | `void**` | Used in `World_Init` (VTable pointer?)
|
||||
0x21C8 | `???` | Used in `World_Init`
|
||||
0x2204 | `uint32_t` or `uint16_t` | Used in `World_Init`
|
||||
0x2230 | `float` | Used in `World_Init`
|
||||
0x2238 | `???` | Used in `World_Init`
|
||||
0x2254 | `float` | Used in `World_Init`
|
||||
|
||||
## cPyEntity structure
|
||||
|
||||
| Offset | Type | Description |
|
||||
|--------|----------|----------------------|
|
||||
| 0x0000 | `void**` | Virtual Method Table |
|
||||
| 0x0004 | `char*` | Name |
|
||||
| 0x0008 | `void*` | ??? |
|
||||
Offset | Type | Description
|
||||
------ | -------- | --------------------
|
||||
0x0000 | `void**` | Virtual Method Table
|
||||
0x0004 | `char*` | Name
|
||||
0x0008 | `void*` | ???
|
||||
|
||||
|
||||
## Entity Hash Table
|
||||
|
@ -202,12 +202,12 @@ struct HT_Entry {
|
|||
|
||||
Data format:
|
||||
|
||||
| Offset | Type | Description |
|
||||
|--------|---------------|--------------------------|
|
||||
| 0x0 | `void**` | Virtual Method Table (?) |
|
||||
| 0x4 | `const char*` | name as string |
|
||||
| 0x14 | `void*` | pointer to self (why?) |
|
||||
| 0x28 | `float[3]` | Position in Game World |
|
||||
Offset | Type | Description
|
||||
------ | ------------- | ------------------------
|
||||
0x0 | `void**` | Virtual Method Table (?)
|
||||
0x4 | `const char*` | name as string
|
||||
0x14 | `void*` | pointer to self (why?)
|
||||
0x28 | `float[3]` | Position in Game World
|
||||
|
||||
## EntityList Hash Table
|
||||
|
||||
|
@ -254,16 +254,28 @@ Player Join Packet
|
|||
09c9e028 35 2c 31 35 2c 31 02 00 00 00 5,15,1....
|
||||
```
|
||||
|
||||
| Message | Description |
|
||||
|------------------------------------------|-------------------------------------------------------------------|
|
||||
| `5c68625c32383230395c73637261706c616e64` | "Scrapland Server" announcement broadcast (`\hb\28209\scrapland`) |
|
||||
| `7f01000007` | Retrieve Game info |
|
||||
| `48423d35323932322c3235363a323830383600` | Connection Information (`HB=52922,256:28086`) |
|
||||
Message | Description
|
||||
---------------------------------------- | -----------------------------------------------------------------
|
||||
`5c68625c32383230395c73637261706c616e64` | "Scrapland Server" announcement broadcast (`\hb\28209\scrapland`)
|
||||
`7f01000007` | Retrieve Game info
|
||||
`48423d35323932322c3235363a323830383600` | Connection Information (`HB=52922,256:28086`)
|
||||
|
||||
# [Notes](NOTES.md)
|
||||
|
||||
## File Formats
|
||||
|
||||
File Extension | Description | Chunked
|
||||
-------------- | ------------------------ | -------
|
||||
.packed | Game Data Archive | n
|
||||
.cm3 | Animation file | y
|
||||
.sm3 | 3d model file | y
|
||||
.dum | Dummy (map object) file | y
|
||||
.pth | AI Path | n
|
||||
.emi | Emission maps/Materials? | y
|
||||
.amc | Collision Data | y
|
||||
.ini | Configuration | n
|
||||
.txa | Texture Animation Config | n
|
||||
|
||||
- [Chunked](file_formats/chunked.md)
|
||||
- [Packed](file_formats/packed.md)
|
||||
- [AI Pathfinding Graph](file_formats/ai_path.md)
|
||||
|
@ -290,7 +302,6 @@ check `r2_analyze.py` for full list
|
|||
# How to enable External Console:
|
||||
|
||||
1. Right click on the title bar (in windowed mode) and click "Switch Console"
|
||||
2. or Use a custom Content Pack (**untested!**)
|
||||
|
||||
# How to enable Scenegraph debugging console
|
||||
|
||||
|
|
118
Py_Docs/SAI.md
Normal file
118
Py_Docs/SAI.md
Normal file
|
@ -0,0 +1,118 @@
|
|||
# SAI
|
||||
|
||||
- *GetStateChar*: `<built-in function GetStateChar>`
|
||||
```
|
||||
GetStateChar(string nameEntity) : Devuelve el estado de la IA del personaje.
|
||||
|
||||
```
|
||||
- *AddVehicleRace*: `<built-in function AddVehicleRace>`
|
||||
```
|
||||
bool AddVehicleRace(cWithLifeEntity *entidad) : Asigna entidad como perteneciente a carrera.
|
||||
```
|
||||
- *SetStateVehicle*: `<built-in function SetStateVehicle>`
|
||||
```
|
||||
SetStateVehicle(0, string nameAgent) : Estado sin movimiento ni disparo.
|
||||
SetStateVehicle(1, float posObjX, float posObjY, float posObjZ, radiusObj, string nameAgent) : Estado alcanzar posición.
|
||||
SetStateVehicle(2, string nameObjectiveDin, string nameAgent) : Estado persecución enemigo.
|
||||
SetStateVehicle(3, string nameAgent) : Estado en ruta.
|
||||
SetStateVehicle(4, string nameAgent) : Estado tráfico.
|
||||
SetStateVehicle(5, string nameAgent, float distStop) : Estado huída con parada.
|
||||
SetStateVehicle(6, float posObjX, float posObjY, float posObjZ, radiusObj, string nameAgent) : Estado alcanzar meta en carrera.
|
||||
SetStateVehicle(7, string nameObjectiveDin, string nameAgent) : Estado persecución.
|
||||
SetStateVehicle(8, string nameObjectiveDin, string nameAgent) : Estado persecución enemigo con uso de hook.
|
||||
|
||||
```
|
||||
- *EnableAIChar*: `<built-in function EnableAIChar>`
|
||||
```
|
||||
EnableAIChar(string nameEntity, int enable, int stupidPathfinding) : Habilita la IA del personaje indicando características asociadas al movimiento).
|
||||
```
|
||||
- *BuildGraph*: `<built-in function BuildGraph>`
|
||||
```
|
||||
BuildGraph(int numNodesRadius, float sizeNodeX, float sizeNodeY, float sizeNodeZ) : Crea el Grafo asociado al Pathfinding
|
||||
```
|
||||
- *AnalizeMap*: `<built-in function AnalizeMap>`
|
||||
```
|
||||
AnalizeMap(float sizeNode) : Analiza características mapa.
|
||||
```
|
||||
- *SetStateChar*: `<built-in function SetStateChar>`
|
||||
```
|
||||
SetStateChar(0, string nameAgent) : Estado sin movimiento ni disparo.
|
||||
SetStateChar(1, string nameAgent, float vel, int withStopTemp) : Estado en ruta.
|
||||
SetStateChar(2, string nameAgent, string nameObjective, float vel) : Estado en persecución objetivo con acción.
|
||||
SetStateChar(3, string nameAgent, string nameObjective, float vel) : Estado en persecución objetivo sin acción.
|
||||
SetStateChar(4, string nameAgent, float posObjX, float posObjY, float posObjZ, float orientX, float orientY, float orientZ, float radiusObj, float velObj) : Estado ir a un punto con orientación final.
|
||||
SetStateChar(5, string nameAgent, string nameObjective, float vel) : Estado huída de otro personaje.
|
||||
SetStateChar(6, string nameAgent, float centerPatrolZone.x, float centerPatrolZone.y, float centerPatrolZone.z, float radiusPatrolZone, float vel, int withStopTemp) : Estado patrulla de zona.
|
||||
SetStateChar(7, string nameAgent, string nameObjective, float vel) : Estado en persecución objetivo con acción.
|
||||
```
|
||||
- *AnalizeGraph2D*: `<built-in function AnalizeGraph2D>`
|
||||
```
|
||||
AnalizeGraph2D() : Analiza características grafo 2D.
|
||||
```
|
||||
- *GetNextRacePoint*: `<built-in function GetNextRacePoint>`
|
||||
```
|
||||
(point) GetNextRacePoint(initialPoint, minDist, maxDist) : A partir de un punto inicial 'initialPoint', una distancia mínima 'minDist' y una distancia máxima 'maxDist', devuelve un punto aleatorio a partir del grafo 3D de la IA en el interior actual.
|
||||
```
|
||||
- *GetOD*: `<built-in function GetOD>`
|
||||
- *SetRotStaticObj*: `<built-in function SetRotStaticObj>`
|
||||
```
|
||||
bool SetRotStaticObj(float maxVelRot, float limIncVelRot) : Asigna rotaciones para movimiento hacia objetivo estático.
|
||||
```
|
||||
- *IniAI*: `<built-in function IniAI>`
|
||||
```
|
||||
IniAI(levelPath) : Inicializa AI para un nivel.
|
||||
```
|
||||
- *GetStateVehicle*: `<built-in function GetStateVehicle>`
|
||||
```
|
||||
GetStateVehicle(string nameEntity) : Devuelve el estado de la IA del vehículo.
|
||||
0 : Estado sin movimiento ni disparo.
|
||||
1 : Estado alcanzar posición.
|
||||
2 : Estado persecución enemigo.
|
||||
3 : Estado en ruta.
|
||||
4 : Estado tráfico.
|
||||
5 : Estado huída con parada.
|
||||
6 : Estado alcanzar meta en carrera.
|
||||
7 : Estado persecución.
|
||||
8 : Estado persecución enemigo con uso de hook.
|
||||
9 : Estado sin movimiento ni disparo por estar objetivo en posición inválida.
|
||||
|
||||
```
|
||||
- *BuildGraph2D*: `<built-in function BuildGraph2D>`
|
||||
```
|
||||
BuildGraph2D(int numNodesRadius, float sizeNodeX, float sizeNodeY, float sizeNodeZ) : Crea el Grafo asociado al Pathfinding 2D
|
||||
```
|
||||
- *AnalizeTraffic*: `<built-in function AnalizeTraffic>`
|
||||
```
|
||||
AnalizeTraffic() : Analiza características tráfico.
|
||||
```
|
||||
- *SetInertia*: `<built-in function SetInertia>`
|
||||
```
|
||||
void SetInertia(bool inertia) : Indica si la nave tiene inercia.
|
||||
```
|
||||
- *InitVehicleRace*: `<built-in function InitVehicleRace>`
|
||||
```
|
||||
InitVehicleRace() : Inicializa carrera de vehículos.
|
||||
```
|
||||
- *GetNearestItemLife*: `<built-in function GetNearestItemLife>`
|
||||
```
|
||||
(itemName) GetNearestItemLife(vehicleName)) : Devuelve el item de vida más cercano a una nave dada.
|
||||
```
|
||||
- *EnableAIVehicle*: `<built-in function EnableAIVehicle>`
|
||||
```
|
||||
EnableAIVehicle(string nameEntity, int enable, int controlStrafe, int controlBrake, int stupidPathfinding) : Habilita la IA del vehículo indicando características asociadas al movimiento).
|
||||
```
|
||||
- *AnalizeGraph*: `<built-in function AnalizeGraph>`
|
||||
```
|
||||
AnalizeGraph() : Analiza características grafo.
|
||||
```
|
||||
- *GetRandomVisibilityPoint*: `<built-in function GetRandomVisibilityPoint>`
|
||||
```
|
||||
(point) GetRandomVisibilityPoint() : Devuelve un punto aleatorio del grafo de puntos de visibilidad.
|
||||
```
|
||||
- *GetReposCharPos*: `<built-in function GetReposCharPos>`
|
||||
```
|
||||
(x,y,z) GetReposCharPos((x,y,z) ,EntityClass,[,EntityName]) : Obtiene un punto de reposicion de personaje (si entidad, se asigna).
|
||||
Retorna (None) si falla
|
||||
```
|
||||
|
||||
|
40
Py_Docs/SAct.md
Normal file
40
Py_Docs/SAct.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# SAct
|
||||
|
||||
- *CreateClass*: `<built-in function CreateClass>`
|
||||
```
|
||||
CreateClass(classname) : Crea una clase de objeto animado
|
||||
```
|
||||
- *CreateAction*: `<built-in function CreateAction>`
|
||||
```
|
||||
CreateAction(varname) : Crea una accion en la clase actual.
|
||||
```
|
||||
- *DelClass*: `<built-in function DelClass>`
|
||||
```
|
||||
DelClass(classname) : Crea una clase de objeto animado
|
||||
```
|
||||
- *SetCls*: `<built-in function SetCls>`
|
||||
```
|
||||
SetCls(varname,value) : Modifica el valor de una variable de una clase objeto animado
|
||||
```
|
||||
- *GetCls*: `<built-in function GetCls>`
|
||||
```
|
||||
GetCls(varname) : Obtiene el valor de una variable de una clase objeto animado
|
||||
```
|
||||
- *GetClass*: `<built-in function GetClass>`
|
||||
```
|
||||
GetClass(classname) : Activa una clase de objeto animado
|
||||
```
|
||||
- *SetAct*: `<built-in function SetAct>`
|
||||
```
|
||||
SetAct(varname,value) : Modifica el valor de una variable de la accion de una clase objeto animado
|
||||
```
|
||||
- *GetAct*: `<built-in function GetAct>`
|
||||
```
|
||||
GetAct(varname) : Obtiene el valor de una variable de la accion de una clase objeto animado
|
||||
```
|
||||
- *GetAction*: `<built-in function GetAction>`
|
||||
```
|
||||
GetAction(varname) : Obtiene una accion de la clase actual.
|
||||
```
|
||||
|
||||
|
28
Py_Docs/SFX.md
Normal file
28
Py_Docs/SFX.md
Normal file
|
@ -0,0 +1,28 @@
|
|||
# SFX
|
||||
|
||||
- *CharacterConversor*: `<built-in function CharacterConversor>`
|
||||
```
|
||||
FXCharacterConversor(CharacterName, ConversorName, phase) : Conversor de Personajes.
|
||||
```
|
||||
- *BishopSellLife*: `<built-in function BishopSellLife>`
|
||||
```
|
||||
FXBishopSellLife(AttackerName, AttackedName) : Efecto de dar vida (de Attacker [Bishop ó NULL] a Attacked[Usuario]).
|
||||
```
|
||||
- *CharacterConversion*: `<built-in function CharacterConversion>`
|
||||
```
|
||||
FXCharacterConversion(AttackerName, AttackedName, time) : Conversión de Personajes.
|
||||
```
|
||||
- *EmbeddedSet*: `<built-in function EmbeddedSet>`
|
||||
```
|
||||
FXEmbeddedSet(EntityName, FXType) : Asigna el controlador de efectos embedidos.
|
||||
```
|
||||
- *ShipExplosion*: `<built-in function ShipExplosion>`
|
||||
```
|
||||
ShipExplosion(V3D Pos, V3D Vel, float scale, float time, float nflames, bool bcolision) : Explosión de la nave.
|
||||
```
|
||||
- *MoneyTransfer*: `<built-in function MoneyTransfer>`
|
||||
```
|
||||
FXMoneyTransfer(EntityFrom, EntityTo, Time) : Transferencia de dinero
|
||||
```
|
||||
|
||||
|
84
Py_Docs/SInput.md
Normal file
84
Py_Docs/SInput.md
Normal file
|
@ -0,0 +1,84 @@
|
|||
# SInput
|
||||
|
||||
- *Rumble*: `<built-in function Rumble>`
|
||||
```
|
||||
Rumble(iPlayer,Left,Right,Time) : Inicializa el rumble de un pad.
|
||||
```
|
||||
- *GetActionSet*: `<built-in function GetActionSet>`
|
||||
```
|
||||
string GetActionSet() : Obtiene el set de acciones Actual...
|
||||
```
|
||||
- *SetInputFunc*: `<built-in function SetInputFunc>`
|
||||
```
|
||||
SetInputFunc(iPlayer,modfunc) : agrega la funcion callback de entrada modfunc(iPlayer,string)
|
||||
```
|
||||
- *GetCursorChar*: `<built-in function GetCursorChar>`
|
||||
```
|
||||
GetCursorChar(iPlayer) : Obtiene el (x,y,caracter) que indican el estado del cursor.
|
||||
```
|
||||
- *GetDefinedEntry*: `<built-in function GetDefinedEntry>`
|
||||
```
|
||||
value GetDefinedEntry(iPlayer,ActionSet,Action,Device) : obtiene una cadena con la primera definicion del control que encuentre
|
||||
```
|
||||
- *AbortListenToDefine*: `<built-in function AbortListenToDefine>`
|
||||
```
|
||||
AbortListenToDefine() : Aborta la redefinicion en curso
|
||||
```
|
||||
- *ResetToDefault*: `<built-in function ResetToDefault>`
|
||||
```
|
||||
ResetToDefault(iPlayer,ActionSet,Action) : Pone todas las entradas de los controles a valores por defecto.
|
||||
```
|
||||
- *ResetToSplit*: `<built-in function ResetToSplit>`
|
||||
```
|
||||
ResetToSplit() : Resetea el sistema de entrada de datos para iniciar el modo split screen.
|
||||
```
|
||||
- *GetDefinedList*: `<built-in function GetDefinedList>`
|
||||
```
|
||||
GetDefinedList(iPlayer,ActionSet,Action) : obtiene una cadena con la definicion de controles
|
||||
```
|
||||
- *AssingEntry*: `<built-in function AssingEntry>`
|
||||
```
|
||||
int AssingEntry(Device,Entry,Player,ActionSet,Action) : Asigna una entrada... retorna 0 o el Nro de parametro erroneo
|
||||
```
|
||||
- *CheckPadButton*: `<built-in function CheckPadButton>`
|
||||
```
|
||||
CheckPadButton() : Chequea el estado de un determinado botón del pad.
|
||||
```
|
||||
- *ListenToDefine*: `<built-in function ListenToDefine>`
|
||||
```
|
||||
ListenToDefine(iPlayer,ActionSet,Action,LaFunction) : Espera a que el usuario mueva un control y lo redefine
|
||||
```
|
||||
- *SetString*: `<built-in function SetString>`
|
||||
```
|
||||
SetString(iPlayer,String) : Modifica la cadena de entrada de texto.
|
||||
```
|
||||
- *Bind*: `<built-in function Bind>`
|
||||
```
|
||||
Bind(iPlayer,ActionSet,Action) : obtiene una cadena con la definicion de controles
|
||||
```
|
||||
- *GetEntry*: `<built-in function GetEntry>`
|
||||
```
|
||||
(Player,Action) GetEntry(Device,Entry,ActionSet) : Obtiene una entrada, (0,) si vacia
|
||||
```
|
||||
- *GetVirtualKeyboard*: `<built-in function GetVirtualKeyboard>`
|
||||
```
|
||||
GetVirtualKeyboard() : Obtiene el (W,H,Board) que son datos del keyboard virtual.
|
||||
```
|
||||
- *GetString*: `<built-in function GetString>`
|
||||
```
|
||||
GetString(iPlayer) : Obtiene la cadena de entrada de texto.
|
||||
```
|
||||
- *SetVirtualKeyboard*: `<built-in function SetVirtualKeyboard>`
|
||||
```
|
||||
SetVirtualKeyboard(tipo de teclado) : Cambia el teclado virtual
|
||||
```
|
||||
- *SetActionSet*: `<built-in function SetActionSet>`
|
||||
```
|
||||
SetActionSet(string name) : Pone el set de acciones requerido...
|
||||
```
|
||||
- *ClearDefinedList*: `<built-in function ClearDefinedList>`
|
||||
```
|
||||
ClearDefinedList(iPlayer,ActionSet,Action) : Elimina todas las entradas de un control.
|
||||
```
|
||||
|
||||
|
52
Py_Docs/SLogic.md
Normal file
52
Py_Docs/SLogic.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
# SLogic
|
||||
|
||||
- *IsEnemyActive*: `<built-in function IsEnemyActive>`
|
||||
```
|
||||
int IsEnemyActive() : Indica si hay un enemigo activo, mirando el contenido de la lista de enemigos y en el tráfico.
|
||||
```
|
||||
- *ChangeZoneState*: `<built-in function ChangeZoneState>`
|
||||
```
|
||||
void ChangeZoneState(zoneId, state): Pone el estado de una zona de dominación
|
||||
```
|
||||
- *SetOnFloor*: `<built-in function SetOnFloor>`
|
||||
```
|
||||
void SetOnFloor(Entity) : Pone una entidad en el suelo
|
||||
```
|
||||
- *ChangeBatonState*: `<built-in function ChangeBatonState>`
|
||||
```
|
||||
void ChangeBatonState(pos, state): Actualiza el estado del testigo en el modo dominación
|
||||
```
|
||||
- *SetShipToRegenerate*: `<built-in function SetShipToRegenerate>`
|
||||
```
|
||||
void SetShipToRegenerate(shipName, regSpeed,regEndSpeed): Pone una nave a regenerarse
|
||||
```
|
||||
- *SetDominationZones*: `<built-in function SetDominationZones>`
|
||||
```
|
||||
void SetDominationZones(zonesList): Establece la lista de posiciones de las zonas de dominación en la super apuesta de dominación
|
||||
```
|
||||
- *Flash*: `<built-in function Flash>`
|
||||
```
|
||||
void Flash((x,y,z),radius) : Pone una entidad en el suelo
|
||||
```
|
||||
- *GetNearestShip*: `<built-in function GetNearestShip>`
|
||||
```
|
||||
Name GetNearestShip(pos) : Cicla por varias listas y devuelve la nave más cercana a la posición indicada
|
||||
```
|
||||
- *SendSentinelToWatch*: `<built-in function SendSentinelToWatch>`
|
||||
```
|
||||
void SendSentinelToWatch(Pos) : Envia a un centinela a inspeccionar una posicion
|
||||
```
|
||||
- *UpdateTauntEndTime*: `<built-in function UpdateTauntEndTime>`
|
||||
```
|
||||
void UpdateTauntEndTime(time): Tiempo final del taunt
|
||||
```
|
||||
- *GearAttack*: `<built-in function GearAttack>`
|
||||
```
|
||||
void GearAttack(Entity) : Envia a un Gear a atacar a una entidad
|
||||
```
|
||||
- *SetCharState*: `<built-in function SetCharState>`
|
||||
```
|
||||
void SetCharState(entityName, state, entityTargetName) : Pone a una entidad en un estado de logica y un target determinados
|
||||
```
|
||||
|
||||
|
94
Py_Docs/SNet.md
Normal file
94
Py_Docs/SNet.md
Normal file
|
@ -0,0 +1,94 @@
|
|||
# SNet
|
||||
|
||||
- *SendUsrString*: `<built-in function SendUsrString>`
|
||||
```
|
||||
SendUsrString(id,string) : -1 significa a todo el mundo en modo servidor, para cliente id se ignora
|
||||
```
|
||||
- *InitServer*: `<built-in function InitServer>`
|
||||
```
|
||||
InitServer(LevelPath,MaxPlayers,ipport) : Intenta inicializar el servidor.
|
||||
```
|
||||
- *GetBotName*: `<built-in function GetBotName>`
|
||||
```
|
||||
GetBotName() : Obtiene un nombre valido de entidad jugador manejada por el servidor (bot o jugador local)
|
||||
```
|
||||
- *CloseServer*: `<built-in function CloseServer>`
|
||||
```
|
||||
CloseServer(LevelPath) : Acaba el servidor y carga un nivel.
|
||||
```
|
||||
- *IsClient*: `<built-in function IsClient>`
|
||||
```
|
||||
IsClient() : 1 si esta activado el sistema cliente
|
||||
NOTA: Scrap.GetNetFlags() tiene el flag cliente activado
|
||||
si la coneccion se hizo efectiva
|
||||
```
|
||||
- *ServerChangeLevel*: `<built-in function ServerChangeLevel>`
|
||||
```
|
||||
ServerChangeLevel(resource name) : carga el siguiente nivel.
|
||||
```
|
||||
- *DoneBrowser*: `<built-in function DoneBrowser>`
|
||||
```
|
||||
DoneBrowser() : Cierra el browser de red local.
|
||||
```
|
||||
- *InitClient*: `<built-in function InitClient>`
|
||||
```
|
||||
InitClient(ipAddress,ipport) : Inicia el proceso de coneccion con el servidor.
|
||||
```
|
||||
- *CloseClient*: `<built-in function CloseClient>`
|
||||
```
|
||||
CloseClient(LevelPath) : Acaba el cliente y carga un nivel.
|
||||
```
|
||||
- *IsServer*: `<built-in function IsServer>`
|
||||
```
|
||||
IsServer() : 1 si esta activado el sistema servidor
|
||||
NOTA: es para depuracion, mejor use Scrap.GetNetFlags()
|
||||
```
|
||||
- *GetObjName*: `<built-in function GetObjName>`
|
||||
```
|
||||
GetObjName() : Obtiene un nombre valido de Objeto cualesquiera.
|
||||
```
|
||||
- *GetMyClientShip*: `<built-in function GetMyClientShip>`
|
||||
```
|
||||
GetMyClientShip() : retorna el nombre de su nave.
|
||||
```
|
||||
- *ModifyUsrData*: `<built-in function ModifyUsrData>`
|
||||
```
|
||||
ModifyUsrData(ClientId) :
|
||||
modifica desde el servidor los datos locales.
|
||||
```
|
||||
- *AddResource*: `<built-in function AddResource>`
|
||||
```
|
||||
resourceid AddResource(resource name) : intenta agregar un recurso si este no existe. -1 si el pool esta lleno
|
||||
```
|
||||
- *SendChatString*: `<built-in function SendChatString>`
|
||||
```
|
||||
SendChatString(id,string) : -1 significa a todo el mundo en modo servidor, para cliente id se ignora
|
||||
```
|
||||
- *GetClientData*: `<built-in function GetClientData>`
|
||||
```
|
||||
GetClientData() : Obtiene la tupla (ipaddress,ipport)
|
||||
```
|
||||
- *InitBrowser*: `<built-in function InitBrowser>`
|
||||
```
|
||||
InitBrowser(port) : Inicializa el browser de red local.
|
||||
```
|
||||
- *SendMasterString*: `<built-in function SendMasterString>`
|
||||
```
|
||||
SendMasterString(string) : envia una cadena al master. Si retorna cero no hay master.
|
||||
```
|
||||
- *PingInetSvrs*: `<built-in function PingInetSvrs>`
|
||||
```
|
||||
PingInetSvrs() : 1 exitoso. revisa el estado de los servidores en internet. Se realiza despues de browse.
|
||||
```
|
||||
- *IsMaster*: `<built-in function IsMaster>`
|
||||
```
|
||||
IsMaster() : 1 si esta activado el sistema cliente
|
||||
NOTA: Scrap.GetNetFlags() tiene el flag cliente activado
|
||||
si la coneccion se hizo efectiva
|
||||
```
|
||||
- *GetServerData*: `<built-in function GetServerData>`
|
||||
```
|
||||
GetServerData() : Obtiene la tupla (Hostname,ipaddress,ipport)
|
||||
```
|
||||
|
||||
|
132
Py_Docs/SScorer.md
Normal file
132
Py_Docs/SScorer.md
Normal file
|
@ -0,0 +1,132 @@
|
|||
# SScorer
|
||||
|
||||
- *SetCursor*: `<built-in function SetCursor>`
|
||||
```
|
||||
SetCursor(playernumber,CursorName) : pone un item del scorer como cursor
|
||||
```
|
||||
- *Show*: `<built-in function Show>`
|
||||
```
|
||||
Show(playernumber) : Activa el scorer.
|
||||
```
|
||||
- *GetTextArea*: `<built-in function GetTextArea>`
|
||||
```
|
||||
(width, height) GetTextArea(fontType, text) : obtiene la anchura y la altura de un texto dado el texto y el tipo de fuente
|
||||
```
|
||||
- *AddModel*: `<built-in function AddModel>`
|
||||
```
|
||||
AddModel(playernumber,Name,ModelFile,numanim,Radius) : Pre carga un modelo para usarse en el scorer.
|
||||
```
|
||||
- *SetOnPrev*: `<built-in function SetOnPrev>`
|
||||
```
|
||||
SetOnPrev(playernumber,CancelEvent) : Define la funcion que se ejecutara en el caso del pagina abajo.
|
||||
```
|
||||
- *AddChatMsg*: `<built-in function AddChatMsg>`
|
||||
```
|
||||
AddChatMsg(string,red,gree,blue) : Agrega un mensaje a la lista de chat.
|
||||
```
|
||||
- *GetOnCancel*: `<built-in function GetOnCancel>`
|
||||
```
|
||||
(CancelEvent)GetOnCancel(playernumber) : Devuelve la funcion que se ejecutara en el caso del escape.
|
||||
```
|
||||
- *GetDefault*: `<built-in function GetDefault>`
|
||||
```
|
||||
DefaultItemName GetDefault(playernumber) Obtiene el nombre del item por defecto
|
||||
```
|
||||
- *SetOnSpecialHint*: `<built-in function SetOnSpecialHint>`
|
||||
```
|
||||
SetOnSpecialHint(playernumber,SpecialHintTestFunc,SpecialHint) : Define la funcion que decide si se muestra un hint especial
|
||||
```
|
||||
- *SetConsole*: `<built-in function SetConsole>`
|
||||
```
|
||||
SetConsole(show) : oculta/ muestra la consola de pantalla completa
|
||||
```
|
||||
- *Get2DPos*: `<built-in function Get2DPos>`
|
||||
```
|
||||
(x,y) Get2DPos(vector3d) : obtiene en cordenadas de pantalla una posicion del escenario
|
||||
```
|
||||
- *GetActual*: `<built-in function GetActual>`
|
||||
```
|
||||
ActualItem GetActual(playernumber) Obtiene el item actual
|
||||
```
|
||||
- *SetOnNext*: `<built-in function SetOnNext>`
|
||||
```
|
||||
SetOnNext(playernumber,CancelEvent) : Define la funcion que se ejecutara en el caso del pagina abajo.
|
||||
```
|
||||
- *SetMPFunc*: `<built-in function SetMPFunc>`
|
||||
```
|
||||
SetMPFunc(playernumber,Callback) : Callback(id,showmpscorer) (muestra oculta el scorer multiplayer)
|
||||
```
|
||||
- *Set*: `<built-in function Set>`
|
||||
```
|
||||
Set(playernumber,itemname,varname,value) : Modifica el valor de una variable de un item
|
||||
```
|
||||
- *SetSpeechCallback*: `<built-in function SetSpeechCallback>`
|
||||
```
|
||||
SetSpeechCallback(Callback) : Especifica la funcion callback que será llamada
|
||||
```
|
||||
- *Get*: `<built-in function Get>`
|
||||
```
|
||||
Get(playernumber,itemname,varname) : Obtiene el valor de una variable de un item
|
||||
```
|
||||
- *GetMenuAccept*: `<built-in function GetMenuAccept>`
|
||||
```
|
||||
GetMenuAccept(id) : Devuelve el estado de la acción de menu aceptar.
|
||||
```
|
||||
- *SetDefault*: `<built-in function SetDefault>`
|
||||
```
|
||||
SetDefault(playernumber,DefaultItemName) : pone un item del scorer 'por defecto'
|
||||
```
|
||||
- *CancelSpeech*: `<built-in function CancelSpeech>`
|
||||
```
|
||||
CancelSpeech(time) : cancela un mensaje remoto
|
||||
```
|
||||
- *SetHeadMonitor*: `<built-in function SetHeadMonitor>`
|
||||
```
|
||||
SetHeadMonitor(Head,Msg,anm) : Especifica la cabeza que será usada en el monitor y el mensaje.
|
||||
```
|
||||
- *Add*: `<built-in function Add>`
|
||||
```
|
||||
Add(playernumber,itemname,itemtype,AtEnd) : Agrega un item al scorer.
|
||||
```
|
||||
- *SetMarkerSprite*: `<built-in function SetMarkerSprite>`
|
||||
```
|
||||
SetMarkerSprite(id,SpriteName) : pone un sprite como marcador
|
||||
```
|
||||
- *PreloadTexture*: `<built-in function PreloadTexture>`
|
||||
```
|
||||
PreloadTexture(filename) : Precarga una textura
|
||||
```
|
||||
- *SetMsgText*: `<built-in function SetMsgText>`
|
||||
```
|
||||
SetMsgText(text,time) : Muestra un mensaje de sistema y lo desactiva en el tiempo de mundo time
|
||||
```
|
||||
- *Fade*: `<built-in function Fade>`
|
||||
```
|
||||
Fade(id,(r,g,b,a),(r,g,b,a),time) : Realiza un fade de rgba a rgba.
|
||||
```
|
||||
- *Clear*: `<built-in function Clear>`
|
||||
```
|
||||
Clear(playernumber) : Limpia el scorer completamente.
|
||||
```
|
||||
- *SetCinema*: `<built-in function SetCinema>`
|
||||
```
|
||||
SetCinema(id,status,time) : Activa el modo escena de cine.
|
||||
```
|
||||
- *SetSpeechText*: `<built-in function SetSpeechText>`
|
||||
```
|
||||
SetSpeechText(text,time,r,g,b) : activa el texto de una conversacion que durará un tiempo espeficico
|
||||
```
|
||||
- *SetOnCancel*: `<built-in function SetOnCancel>`
|
||||
```
|
||||
SetOnCancel(playernumber,CancelEvent) : Define la funcion que se ejecutara en el caso del escape.
|
||||
```
|
||||
- *Hide*: `<built-in function Hide>`
|
||||
```
|
||||
Hide(playernumber) : Desactiva el scorer.
|
||||
```
|
||||
- *SetLabelText*: `<built-in function SetLabelText>`
|
||||
```
|
||||
SetLabelText(text,time) : Muestra un rotulo de sistema y lo desactiva en el tiempo de mundo time
|
||||
```
|
||||
|
||||
|
72
Py_Docs/SSound.md
Normal file
72
Py_Docs/SSound.md
Normal file
|
@ -0,0 +1,72 @@
|
|||
# SSound
|
||||
|
||||
- *StopVoice*: `<built-in function StopVoice>`
|
||||
```
|
||||
StopVoice(channel) : stop a voice on the specified channel.
|
||||
```
|
||||
- *Play*: `<built-in function Play>`
|
||||
```
|
||||
Play('soundname'[,vol,pan]) : ejecuta un sonido con el volumen deseado.
|
||||
```
|
||||
- *PlaySound*: `<built-in function PlaySound>`
|
||||
```
|
||||
PlaySound((x,y,z),'soundname',vol[,AttenIni,AttenEnd,Doppler]) : ejecuta un sonido en la posicion dada con el volumen deseado.
|
||||
```
|
||||
- *SetMusic*: `<built-in function SetMusic>`
|
||||
```
|
||||
SetMusic('soundname'[,vol]) : set the file of the background music.
|
||||
```
|
||||
- *LoadSound*: `<built-in function LoadSound>`
|
||||
```
|
||||
LoadSound(soundfile) : carga un sonido desde un archivo empaquetado.
|
||||
```
|
||||
- *OpenVoice*: `<built-in function OpenVoice>`
|
||||
```
|
||||
OpenVoice(name,channel) : Preload a voice on a channel.
|
||||
```
|
||||
- *StopAllSounds*: `<built-in function StopAllSounds>`
|
||||
```
|
||||
SCRAP_StopAllSounds() : para todos los sonidos.
|
||||
```
|
||||
- *SetMusicVolume*: `<built-in function SetMusicVolume>`
|
||||
```
|
||||
SetMusicVolume(vel) : set the volume of the background music.
|
||||
```
|
||||
- *UsePS*: `<built-in function UsePS>`
|
||||
```
|
||||
UsePS(Name) : Crea un nuevo sonido posicional
|
||||
```
|
||||
- *SetVoiceString*: `<built-in function SetVoiceString>`
|
||||
```
|
||||
SetVoiceString(channel,volume) : set string user data of a voice.
|
||||
```
|
||||
- *VoiceRemain*: `<built-in function VoiceRemain>`
|
||||
```
|
||||
VoiceRemain(channel,time) : return the remaining time of a voice.
|
||||
```
|
||||
- *CreatePS*: `<built-in function CreatePS>`
|
||||
```
|
||||
CreatePS(Name) : Crea un nuevo sonido posicional
|
||||
```
|
||||
- *SetPS*: `<built-in function SetPS>`
|
||||
```
|
||||
SetPS(name,varname,value) : Modifica el valor de una variable de un sonido posicional
|
||||
```
|
||||
- *PlayVoice*: `<built-in function PlayVoice>`
|
||||
```
|
||||
PlayVoice(channel) : play a voice loaded on the specified channel.
|
||||
```
|
||||
- *DeleteAllSounds*: `<built-in function DeleteAllSounds>`
|
||||
```
|
||||
SCRAP_DeleteAllSounds() : elimina todos los sonidos.
|
||||
```
|
||||
- *GetPS*: `<built-in function GetPS>`
|
||||
```
|
||||
GetPS(name,varname) : Obtiene el valor de una variable de un sonido posicional
|
||||
```
|
||||
- *SetVoiceVolume*: `<built-in function SetVoiceVolume>`
|
||||
```
|
||||
SetVoiceVolume(channel,volume) : set the volume of a voice.
|
||||
```
|
||||
|
||||
|
52
Py_Docs/SVec.md
Normal file
52
Py_Docs/SVec.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
# SVec
|
||||
|
||||
- *Mod*: `<built-in function Mod>`
|
||||
```
|
||||
res Mod(v1) : Devuelve el modulo de un vector
|
||||
```
|
||||
- *Norm*: `<built-in function Norm>`
|
||||
```
|
||||
res Norm(v1) : Normaliza un vector
|
||||
```
|
||||
- *Prod*: `<built-in function Prod>`
|
||||
```
|
||||
res Prod(v1,f) : multiplica un vector por un numero
|
||||
```
|
||||
- *ModSqr*: `<built-in function ModSqr>`
|
||||
```
|
||||
res ModSqr(v1) : Devuelve el modulo (al cuadrado) de un vector
|
||||
```
|
||||
- *DProd*: `<built-in function DProd>`
|
||||
```
|
||||
res DProd(v1,v2) : Calcula el producto escalar de dos vectores
|
||||
```
|
||||
- *Add*: `<built-in function Add>`
|
||||
```
|
||||
res Add(v1,v2) : suma dos vectores 3D
|
||||
```
|
||||
- *CProd*: `<built-in function CProd>`
|
||||
```
|
||||
vRes CProd(v1,v2) : Calcula el producto vectorial de dos vectores
|
||||
```
|
||||
- *NormAng*: `<built-in function NormAng>`
|
||||
```
|
||||
rAng NormAng(Ang) : Normaliza un angulo
|
||||
```
|
||||
- *Sub*: `<built-in function Sub>`
|
||||
```
|
||||
res Sub(v1,v2) : Resta dos vectores 3D
|
||||
```
|
||||
- *GetRotAng*: `<built-in function GetRotAng>`
|
||||
```
|
||||
AngX,AngY GetRotAng(vec) : Obtiene la rotacion de un vector
|
||||
```
|
||||
- *Rotate3D*: `<built-in function Rotate3D>`
|
||||
```
|
||||
res Rotate3D(src,rot) : Rota un vector
|
||||
```
|
||||
- *GetAngle*: `<built-in function GetAngle>`
|
||||
```
|
||||
Ang GetAngle(vec) : Obtiene el ángulo entre dos vectores
|
||||
```
|
||||
|
||||
|
64
Py_Docs/SWeap.md
Normal file
64
Py_Docs/SWeap.md
Normal file
|
@ -0,0 +1,64 @@
|
|||
# SWeap
|
||||
|
||||
- *SetSWeap*: `<built-in function SetSWeap>`
|
||||
```
|
||||
SetSWeap(numammo,svar,svalue) : Pone un valor cadena de las municiones
|
||||
```
|
||||
- *FillPriority*: `<built-in function FillPriority>`
|
||||
```
|
||||
FillPriority() : Inizializa las prioridades de las armas
|
||||
```
|
||||
- *GetSAmmo*: `<built-in function GetSAmmo>`
|
||||
```
|
||||
GetSAmmo(numammo,svar) : Obtiene un valor cadena de las municiones
|
||||
```
|
||||
- *SetFAmmo*: `<built-in function SetFAmmo>`
|
||||
```
|
||||
GetFAmmo(numammo,svar,fvalue) : Pone un valor numerico de las municiones
|
||||
```
|
||||
- *InitWeap*: `<built-in function InitWeap>`
|
||||
```
|
||||
InitWeap(num) : Inicializa las armas num es el numero de armas
|
||||
```
|
||||
- *GetNWeap*: `<built-in function GetNWeap>`
|
||||
```
|
||||
GetNWeap() : Obtiene el numero de armas en el juego
|
||||
```
|
||||
- *GetFAmmo*: `<built-in function GetFAmmo>`
|
||||
```
|
||||
GetFAmmo(numammo,svar) : Obtiene un valor numerico de las municiones
|
||||
```
|
||||
- *GetFirstiWeap*: `<built-in function GetFirstiWeap>`
|
||||
```
|
||||
GetFirstiWeap() : Obtiene el arma primera y por defecto
|
||||
```
|
||||
- *GetSWeap*: `<built-in function GetSWeap>`
|
||||
```
|
||||
GetSWeap(numWeap,svar) : Obtiene un valor cadena de las municiones
|
||||
```
|
||||
- *GetFWeap*: `<built-in function GetFWeap>`
|
||||
```
|
||||
GetFWeap(numWeap,svar) : Obtiene un valor numerico de las municiones
|
||||
```
|
||||
- *GetNAmmo*: `<built-in function GetNAmmo>`
|
||||
```
|
||||
GetNAmmo() : Obtiene el numero de municiones en el juego
|
||||
```
|
||||
- *NetExec*: `<built-in function NetExec>`
|
||||
```
|
||||
NetExec() : Rutinas de red de los misiles
|
||||
```
|
||||
- *SetFWeap*: `<built-in function SetFWeap>`
|
||||
```
|
||||
GetFWeap(numWeap,svar,fvalue) : Pone un valor numerico de las municiones
|
||||
```
|
||||
- *InitAmmo*: `<built-in function InitAmmo>`
|
||||
```
|
||||
InitAmmo(num) : Inicializa las municiones del juego
|
||||
```
|
||||
- *SetSAmmo*: `<built-in function SetSAmmo>`
|
||||
```
|
||||
SetSAmmo(numammo,svar,svalue) : Pone un valor cadena de las municiones
|
||||
```
|
||||
|
||||
|
425
Py_Docs/Scrap.md
Normal file
425
Py_Docs/Scrap.md
Normal file
|
@ -0,0 +1,425 @@
|
|||
# Scrap
|
||||
|
||||
- *DropDebris*: `<built-in function DropDebris>`
|
||||
```
|
||||
DropDebris(name,size) : Lanza un objeto que cae y rebota hasta que desaparece
|
||||
```
|
||||
- *ShowGVars*: `<built-in function ShowGVars>`
|
||||
```
|
||||
ShowGVars() : Muestra una lista de todas las variables globales
|
||||
```
|
||||
- *GetNewLevelPath*: `<built-in function GetNewLevelPath>`
|
||||
```
|
||||
levelpath GetNewLevelPath() : Obtiene el path del próximo nivel
|
||||
```
|
||||
- *SetTime*: `<built-in function SetTime>`
|
||||
```
|
||||
SetTime(Time) : Cambia el tiempo del mundo en segundos
|
||||
```
|
||||
- *SetCam*: `<built-in function SetCam>`
|
||||
```
|
||||
SetCam(i,name) : Activa una camara
|
||||
```
|
||||
- *PreloadAnm*: `<built-in function PreloadAnm>`
|
||||
```
|
||||
PreloadAnm(ObjFilename,AnmFilename) : Precarga una animacion
|
||||
```
|
||||
- *GetCam*: `<built-in function GetCam>`
|
||||
```
|
||||
name GetCam(i) : Obtiene el nombre de una entidad camara
|
||||
```
|
||||
- *GetLockAlarm*: `<built-in function GetLockAlarm>`
|
||||
```
|
||||
Scrap.GetLockAlarm() : verdadero si la alarma esta bloqueada
|
||||
```
|
||||
- *AddItem*: `<built-in function AddItem>`
|
||||
```
|
||||
Scrap.AddItem(Life, string name, int life) : Add Item Life.
|
||||
Scrap.AddItem(Ammo, string name, int typeAmmo, int ammo) : Add Item Ammo.
|
||||
```
|
||||
- *Verbose*: `<built-in function Verbose>`
|
||||
```
|
||||
Verbose(string name) : Muestra un mensaje de parloteo por la consola
|
||||
```
|
||||
- *StartDummySearch*: `<built-in function StartDummySearch>`
|
||||
```
|
||||
StartDummySearch(name,usewildcards) : Inicia la busqueda de dummies en el mapa
|
||||
```
|
||||
- *OpenPack*: `<built-in function OpenPack>`
|
||||
```
|
||||
OpenPack(string PackPath) : Abre un archivo *.packed
|
||||
```
|
||||
- *LaunchDashboard*: `<built-in function LaunchDashboard>`
|
||||
```
|
||||
int Scrap.LaunchDashboard() : sale del juego y ejecuta el dashboard.
|
||||
```
|
||||
- *SaveGameVars*: `<built-in function SaveGameVars>`
|
||||
```
|
||||
Scrap.SaveGameVars(str, str) : Salva un juego en un archivo, con un nombre opcional
|
||||
```
|
||||
- *SphereCall*: `<built-in function SphereCall>`
|
||||
```
|
||||
Scrap.SphereCall(x,y,z,radius,´strmask´,´callback´,[IgnoreGeometry=1]) : testea una esfera y llama a ´callback´ por cada entidad que colisiona
|
||||
```
|
||||
- *SetTimeSpeed*: `<built-in function SetTimeSpeed>`
|
||||
```
|
||||
SetTimeSpeed(TimeSpeed) : Cambia la velocidad del tiempo del mundo.
|
||||
```
|
||||
- *Preload3DObject*: `<built-in function Preload3DObject>`
|
||||
```
|
||||
Preload3DObject(filename,scalex,scaley,scalez) : Precarga un objeto 3D
|
||||
```
|
||||
- *TestLine*: `<built-in function TestLine>`
|
||||
```
|
||||
((x,y,z),s) TestLine((x,y,z),(fz,fy,fz),'strmask') : testea una linea y devuelve el nombre de la entidad y punto de colision o '' si es el mapa o None
|
||||
```
|
||||
- *GetFreeBlocks*: `<built-in function GetFreeBlocks>`
|
||||
```
|
||||
int Scrap.GetFreeBlocks() : obtiene los bloques libres en el disco duro.
|
||||
```
|
||||
- *IncSaveVar*: `<built-in function IncSaveVar>`
|
||||
```
|
||||
int Scrap.IncSaveVar(str[,value]) : incrementa un contador de estadisticas
|
||||
```
|
||||
- *DeselectProfile*: `<built-in function DeselectProfile>`
|
||||
```
|
||||
Scrap.DeselectProfile() : deselecciona el profile actual.
|
||||
```
|
||||
- *GetMoney*: `<built-in function GetMoney>`
|
||||
```
|
||||
money Scrap.GetMoney() : Devuelve el liquido disponible.
|
||||
```
|
||||
- *CreateElements*: `<built-in function CreateElements>`
|
||||
```
|
||||
CreateElements() : Crea una lista de elementos estaticos que pertenecen al mapa.
|
||||
```
|
||||
- *NextDummySearch*: `<built-in function NextDummySearch>`
|
||||
```
|
||||
(s(ddd)(dd)i) NextDummySearch() : Obtiene el proximo dummy, sino None
|
||||
```
|
||||
- *CreateEntity*: `<built-in function CreateEntity>`
|
||||
```
|
||||
CreateEntity(name,x,y,z,type) : Crea una una entidad
|
||||
```
|
||||
- *LoadLevel*: `<built-in function LoadLevel>`
|
||||
```
|
||||
LoadLevel(string name) : Carga un nivel en el siguiente frame
|
||||
```
|
||||
- *DebugInput*: `<built-in function DebugInput>`
|
||||
```
|
||||
string DebugInput() : Detiene todo para iniciar la depuracion
|
||||
```
|
||||
- *GetLangStr*: `<built-in function GetLangStr>`
|
||||
```
|
||||
str Scrap.GetLangStr(Name) : Obtiene una cadena de lenguaje. '' si no existe.
|
||||
```
|
||||
- *Get*: `<built-in function Get>`
|
||||
```
|
||||
Get('GlobalVar') : Obtiene el valor de una variable global
|
||||
```
|
||||
- *SetAlarmChar*: `<built-in function SetAlarmChar>`
|
||||
```
|
||||
Scrap.SetAlarmChar(El_que_se_busca) : Modifica el personaje que se busca.
|
||||
```
|
||||
- *GetEntity*: `<built-in function GetEntity>`
|
||||
```
|
||||
GetEntity(string name) : Retorna una entidad
|
||||
```
|
||||
- *GetLanguage*: `<built-in function GetLanguage>`
|
||||
```
|
||||
lang Scrap.GetLanguage() : Obtiene la lengua actual. None si no fue inicializada.
|
||||
```
|
||||
- *Print*: `<built-in function Print>`
|
||||
```
|
||||
Print(string name) : Muestra un mensaje por la consola siempre
|
||||
```
|
||||
- *SwitchMissionArrows*: `<built-in function SwitchMissionArrows>`
|
||||
```
|
||||
SwitchMissionArrows(MainMissionFile,TargetMissionFile) : Cambia las flechas de mision primaria y secundaria (Modelos)
|
||||
```
|
||||
- *GetFarestParked*: `<built-in function GetFarestParked>`
|
||||
```
|
||||
Entity,Pos = Scrap.GetFarestParked([isparked[,fromPos]]) : Obtiene la nave aparcada mas lejana.
|
||||
```
|
||||
- *CreateSaveProfile*: `<built-in function CreateSaveProfile>`
|
||||
```
|
||||
int Scrap.CreateSaveProfile(str) : Crea un Save profile para X-Box y pone como actual.
|
||||
```
|
||||
- *ExtractPack*: `<built-in function ExtractPack>`
|
||||
```
|
||||
ExtractPack(string PackPath) : Extrae un archivo *.packed
|
||||
```
|
||||
- *SetLockAlarm*: `<built-in function SetLockAlarm>`
|
||||
```
|
||||
Scrap.SetLockAlarm(1/0) : Bloquea/desbloquea la alarma
|
||||
```
|
||||
- *SetSaveVar*: `<built-in function SetSaveVar>`
|
||||
```
|
||||
bool Scrap.SetSaveVar(Name,DefValue) : Modifica una variable (si puede)
|
||||
```
|
||||
- *ListModels*: `<built-in function ListModels>`
|
||||
```
|
||||
ListModels() : Muestra una lista de modelos y escenas
|
||||
```
|
||||
- *ConsoleOut*: `<built-in function ConsoleOut>`
|
||||
```
|
||||
ConsoleOut(string name) : Muestra un mensaje pyton por la consola
|
||||
```
|
||||
- *ScreenShot*: `<built-in function ScreenShot>`
|
||||
```
|
||||
ScreenShot(filename) : Screenshot of the current frame
|
||||
```
|
||||
- *SetMoney*: `<built-in function SetMoney>`
|
||||
```
|
||||
Scrap.SetMoney(money) : Determina el liquido disponible
|
||||
```
|
||||
- *GetFirst*: `<built-in function GetFirst>`
|
||||
```
|
||||
GetFirst() : Retorna la primera entuidad de la lista o none
|
||||
```
|
||||
- *LoadGameVars*: `<built-in function LoadGameVars>`
|
||||
```
|
||||
Scrap.LoadGameVars(str) : Carga un juego en un archivo
|
||||
```
|
||||
- *GetTime*: `<built-in function GetTime>`
|
||||
```
|
||||
GetTime() : Obtiene el tiempo del mundo en segundos
|
||||
```
|
||||
- *GetAlarmChars*: `<built-in function GetAlarmChars>`
|
||||
```
|
||||
(Actual, El_que_se_busca) Scrap.GetAlarmChars() : Obtiene los tipos de personaje que se buscan.
|
||||
```
|
||||
- *SetVideoCurrentMode*: `<built-in function SetVideoCurrentMode>`
|
||||
```
|
||||
SetVideoCurrentMode() : Set Video Current Mode Index
|
||||
```
|
||||
- *AddScheduledFunc*: `<built-in function AddScheduledFunc>`
|
||||
```
|
||||
AddScheduledFunc(time,func,params[,name]) : Ejecuta un codigo en python en un instante de tiempo.
|
||||
```
|
||||
- *ClosePack*: `<built-in function ClosePack>`
|
||||
```
|
||||
ClosePack() : Abre un archivo *.packed
|
||||
```
|
||||
- *CreateEntityList*: `<built-in function CreateEntityList>`
|
||||
```
|
||||
CreateEntityList(listName) : Crea una lista de entidades
|
||||
```
|
||||
- *ProcessDVF*: `<built-in function ProcessDVF>`
|
||||
```
|
||||
ProcessDVF(filename,command) : send a dvf command
|
||||
```
|
||||
- *SetAlarm*: `<built-in function SetAlarm>`
|
||||
```
|
||||
Scrap.SetAlarm(dialpos) : pone la alarma en una posicion (0 desactiva, 1 activa).
|
||||
```
|
||||
- *GetNearestParked*: `<built-in function GetNearestParked>`
|
||||
```
|
||||
Entity,Pos = Scrap.GetNearestParked([isparked[,fromPos]]) : Obtiene la nave aparcada mas cercana.
|
||||
```
|
||||
- *Set*: `<built-in function Set>`
|
||||
```
|
||||
Set('GlobalVar',val) : Modifica el valor de una variable global
|
||||
```
|
||||
- *PythonCompileAll*: `<built-in function PythonCompileAll>`
|
||||
```
|
||||
PythonCompileAll() : compila recursivamente los archivos .py
|
||||
```
|
||||
- *Rand*: `<built-in function Rand>`
|
||||
```
|
||||
Rand(min,max) : Obtiene un numero seudo-aleatorio entre (min,max)
|
||||
```
|
||||
- *Def*: `<built-in function Def>`
|
||||
```
|
||||
Def('GlobalVar') : Obtiene el valor por defecto de una variable global
|
||||
```
|
||||
- *Round*: `<built-in function Round>`
|
||||
```
|
||||
Round(num) : Redondea un numero real al entero más cercano.
|
||||
```
|
||||
- *ModelInfo*: `<built-in function ModelInfo>`
|
||||
```
|
||||
ModelInfo(name) : Muestra información sobre la jerarquía de nodos del modelo
|
||||
```
|
||||
- *UsrEntity*: `<built-in function UsrEntity>`
|
||||
```
|
||||
UsrEntity(ictr) : Retorna una entidad controlada por usario (personaje o nave)
|
||||
```
|
||||
- *GetAlarm*: `<built-in function GetAlarm>`
|
||||
```
|
||||
(active,dialpos,growcode) Scrap.GetAlarm() : Obtiene informacion de la alarma.
|
||||
```
|
||||
- *SetDebrisValue*: `<built-in function SetDebrisValue>`
|
||||
```
|
||||
SetDebrisValue(id,x,y,z) : id = 0:Posicion, 1:Angulos, 2:Velocidad, 3:Velocidad de rotacion
|
||||
```
|
||||
- *MusicPlayer*: `<built-in function MusicPlayer>`
|
||||
```
|
||||
MusicPlayer(filename[,command,param1]) : play music file
|
||||
```
|
||||
- *Des*: `<built-in function Des>`
|
||||
```
|
||||
Des('GlobalVar') : Obtiene el descriptor de una variable global
|
||||
```
|
||||
- *SetCallFunc*: `<built-in function SetCallFunc>`
|
||||
```
|
||||
Scrap.SetCallFunc('!funcname') : Especifica la funcion callback (c++) a llamar. (1 si existe)
|
||||
```
|
||||
- *SetDebrisSys*: `<built-in function SetDebrisSys>`
|
||||
```
|
||||
SetDebrisSys(name,initialnumber,MaxDist,Friction,Scale,Grav,bounce) crea/modifica un sistema de debris
|
||||
```
|
||||
- *Execute*: `<built-in function Execute>`
|
||||
```
|
||||
Scrap.Execute() : Ejecuta una funcion callback (c++).
|
||||
```
|
||||
- *AddParamf*: `<built-in function AddParamf>`
|
||||
```
|
||||
Scrap.AddParamf(Float) : Incluye un parametro a una funcion callback (c++).
|
||||
```
|
||||
- *PreloadLibrary*: `<built-in function PreloadLibrary>`
|
||||
```
|
||||
PreloadLibrary(LibraryName,CompiledFile) : Precarga una libreria de un archivo empaquetado
|
||||
```
|
||||
- *GetVideoCurrentMode*: `<built-in function GetVideoCurrentMode>`
|
||||
```
|
||||
GetVideoCurrentMode() : Get Video Current Mode Index
|
||||
```
|
||||
- *InitLoading*: `<built-in function InitLoading>`
|
||||
```
|
||||
InitLoading() Inicia la pantalla de carga rapida.
|
||||
```
|
||||
- *CallElements*: `<built-in function CallElements>`
|
||||
```
|
||||
CallElements('Function') : llama una funcion Function(Name,x,y,z,angx,angy)
|
||||
```
|
||||
- *GetTimeSpeed*: `<built-in function GetTimeSpeed>`
|
||||
```
|
||||
GetTimeSpeed() : Obtiene la velocidad del tiempo del mundo.
|
||||
```
|
||||
- *CreateSaveVar*: `<built-in function CreateSaveVar>`
|
||||
```
|
||||
bool Scrap.CreateSaveVar(Name,DefValue) : Crea una nueva variable (si puede)
|
||||
```
|
||||
- *EntityListGet*: `<built-in function EntityListGet>`
|
||||
```
|
||||
(value)Scrap.EntityListGet(listName, varName) : Obtiene el valor de una variable de una lista de entidades
|
||||
```
|
||||
- *DeleteProfile*: `<built-in function DeleteProfile>`
|
||||
```
|
||||
int Scrap.DeleteProfile(str) : Borra un profile de X-Box.
|
||||
```
|
||||
- *AddParami*: `<built-in function AddParami>`
|
||||
```
|
||||
Scrap.AddParami(int) : Incluye un parametro a una funcion callback (c++).
|
||||
```
|
||||
- *VideoPlayer*: `<built-in function VideoPlayer>`
|
||||
```
|
||||
VideoPlayer(filename[,command]) : play video file
|
||||
```
|
||||
- *SetLanguage*: `<built-in function SetLanguage>`
|
||||
```
|
||||
res Scrap.SetLanguage(Name[,forcetoreload]) : Modifica la lengua actual. false si ya es la lengua la selecionada
|
||||
```
|
||||
- *AddParams*: `<built-in function AddParams>`
|
||||
```
|
||||
Scrap.AddParams(string) : Incluye un parametro a una funcion callback (c++).
|
||||
```
|
||||
- *GetVideoModes*: `<built-in function GetVideoModes>`
|
||||
```
|
||||
GetVideoModes(NumMode) : Get Video Mode Info
|
||||
```
|
||||
- *GetSaveGamesList*: `<built-in function GetSaveGamesList>`
|
||||
```
|
||||
[(fecha, slot)] Scrap.GetSaveGamesList() : Devuelve una lista de tuplas donde para cada tupla el primer elemento indica el nombre (fecha) del archivo y el segundo elemento indica el slot del que se leerá la partida. La lista está ordenada por la fecha, de la más reciente a la más antigua.
|
||||
```
|
||||
- *GetLevelPath*: `<built-in function GetLevelPath>`
|
||||
```
|
||||
levelpath GetLevelPath() : Obtiene el path del nivel actual
|
||||
```
|
||||
- *BuildFont*: `<built-in function BuildFont>`
|
||||
```
|
||||
BuildFont(name,size) : Construye un archivo de fuente de letras a partir de un .TGA dado
|
||||
```
|
||||
- *DeleteScheduledFuncs*: `<built-in function DeleteScheduledFuncs>`
|
||||
```
|
||||
DeleteScheduledFuncs(name) : Borra las scheduled funcs creadas con un nombre dado.
|
||||
```
|
||||
- *GetNetFlags*: `<built-in function GetNetFlags>`
|
||||
```
|
||||
GetNetFlags() : Obtiene la tupla de flags : client, server, dedicated
|
||||
```
|
||||
- *GetSaveVar*: `<built-in function GetSaveVar>`
|
||||
```
|
||||
value Scrap.GetSaveVar(Name) : obtiene una variable o devuelve none
|
||||
```
|
||||
- *GetLangEntries*: `<built-in function GetLangEntries>`
|
||||
```
|
||||
[str] Scrap.GetLangEntries(Name) : Obtiene un subconjunto de entradas de la tabla de lenguaje que empiecen por 'Name'.
|
||||
```
|
||||
- *FileExist*: `<built-in function FileExist>`
|
||||
```
|
||||
FileExist() check for file existence.
|
||||
```
|
||||
- *GetMinCamDist*: `<built-in function GetMinCamDist>`
|
||||
```
|
||||
GetMinCamDist(x,y,z) : Obtiene la distancia de un punto a la camara
|
||||
```
|
||||
- *EntityListSet*: `<built-in function EntityListSet>`
|
||||
```
|
||||
EntityListSet(listName, varName, value) : Modifica el valor de una variable de una lista de entidades
|
||||
```
|
||||
- *SetAlarmGrow*: `<built-in function SetAlarmGrow>`
|
||||
```
|
||||
Scrap.SetAlarmGrow(growcode) : -1 baja, 0 se mantiene, 1 sube.
|
||||
```
|
||||
- *Exit*: `<built-in function Exit>`
|
||||
```
|
||||
Exit() sale del juego.
|
||||
```
|
||||
- *DelSaveVars*: `<built-in function DelSaveVars>`
|
||||
```
|
||||
Scrap.DelSaveVars(str) : Elimina las <str>* variables
|
||||
```
|
||||
- *SaveConfig*: `<built-in function SaveConfig>`
|
||||
```
|
||||
res Scrap.SaveConfig() : Guarda el fichero de configuración
|
||||
```
|
||||
- *SetSaveProfile*: `<built-in function SetSaveProfile>`
|
||||
```
|
||||
int Scrap.SetSaveProfile(path,name) : Carga un profile de jugador.
|
||||
```
|
||||
- *GetSavedProfilesList*: `<built-in function GetSavedProfilesList>`
|
||||
```
|
||||
[(Directorio, nombre)] Scrap.GetSavedProfilesList() : Devuelve una lista de tuplas donde para cada tupla el primer elemento indica el directorio del profile y el segundo elemento indica el nombre del profile. La lista está ordenada por el criterio de microsoft
|
||||
```
|
||||
- *SetGrid*: `<built-in function SetGrid>`
|
||||
```
|
||||
SetGrid(x,y,z) : Modifica las dimensiones del grid de colision
|
||||
```
|
||||
- *TestSphere*: `<built-in function TestSphere>`
|
||||
```
|
||||
Scrap.TestSphere(x,y,z,radius,´strmask´) : testea una esfera y devuelve el nombre de la entidad o '' si es el mapa o None
|
||||
```
|
||||
- *EndLoading*: `<built-in function EndLoading>`
|
||||
```
|
||||
EndLoading() Inicia la pantalla de carga rapida.
|
||||
```
|
||||
- *Ver*: `<built-in function Ver>`
|
||||
```
|
||||
Ver() Obtiene la version del juego.
|
||||
```
|
||||
- *ConsoleError*: `<built-in function ConsoleError>`
|
||||
```
|
||||
ConsoleError(string name) : Muestra un mensaje de error por la consola
|
||||
```
|
||||
- *GetFloor*: `<built-in function GetFloor>`
|
||||
```
|
||||
i GetFloor(y) : Obtiene el numero de piso en el que esta.
|
||||
```
|
||||
- *AddParkingZone*: `<built-in function AddParkingZone>`
|
||||
```
|
||||
Scrap.AddParkingZone(occupied,pos,name) : Agrega un sitio de parking.
|
||||
```
|
||||
|
||||
|
475700
Scrap.rzdb
Normal file
475700
Scrap.rzdb
Normal file
File diff suppressed because one or more lines are too long
22
ScrapHacks/.gitignore
vendored
Normal file
22
ScrapHacks/.gitignore
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/cmake
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=cmake
|
||||
|
||||
### CMake ###
|
||||
CMakeLists.txt.user
|
||||
CMakeCache.txt
|
||||
CMakeFiles
|
||||
CMakeScripts
|
||||
Testing
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
install_manifest.txt
|
||||
compile_commands.json
|
||||
CTestTestfile.cmake
|
||||
_deps
|
||||
CMakeUserPresets.json
|
||||
|
||||
### CMake Patch ###
|
||||
# External projects
|
||||
*-prefix/
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/cmake
|
|
@ -5,24 +5,30 @@ project(ScrapHacks
|
|||
DESCRIPTION "Scrapland memory hacking library"
|
||||
LANGUAGES CXX)
|
||||
|
||||
message(STATUS "SCRAPLAND_DIR=${SCRAPLAND_DIR}")
|
||||
message(STATUS "Fetching Scrapland installation folder")
|
||||
get_filename_component(SCRAPLAND_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\MercurySteam Entertainment\\Scrapland;DIRECTORY]" ABSOLUTE CACHE)
|
||||
get_filename_component(DEFAULT_SCRAPLAND_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\MercurySteam Entertainment\\Scrapland;DIRECTORY]" ABSOLUTE CACHE)
|
||||
|
||||
if(NOT IS_ABSOLUTE "${SCRAPLAND_DIR}" OR NOT EXISTS "${SCRAPLAND_DIR}")
|
||||
message(FATAL_ERROR "Scrapland installation folder not found!")
|
||||
if(NOT IS_ABSOLUTE "${DEFAULT_SCRAPLAND_DIR}" OR NOT EXISTS "${DEFAULT_SCRAPLAND_DIR}")
|
||||
set(DEFAULT_SCRAPLAND_DIR "")
|
||||
endif()
|
||||
set(SCRAPLAND_DIR "${DEFAULT_SCRAPLAND_DIR}" CACHE STRING "Scrapland installation path")
|
||||
|
||||
|
||||
message(STATUS "Scrapland found at ${SCRAPLAND_DIR}")
|
||||
|
||||
message(STATUS "Checking Scrap.exe hash")
|
||||
message(CHECK_START "Checking Scrap.exe hash")
|
||||
file(SHA1 "${SCRAPLAND_DIR}/Bin/Scrap.exe" SCRAP_EXE_HASH)
|
||||
|
||||
if(NOT ${SCRAP_EXE_HASH} STREQUAL "d2dde960e8eca69d60c2e39a439088b75f0c89fa")
|
||||
message(FATAL_ERROR "Scrap.exe hash miss match!")
|
||||
message(CHECK_FAIL "Scrap.exe hash miss match ScrapHacks will probably crash your game!")
|
||||
else()
|
||||
message(CHECK_PASS "Hash matches!")
|
||||
endif()
|
||||
|
||||
# ==============================
|
||||
# "${SCRAPLAND_DIR}/Bin/Scrap.exe"
|
||||
|
||||
add_custom_target(
|
||||
run
|
||||
COMMAND "Scrap.exe"
|
||||
|
@ -30,6 +36,7 @@ add_custom_target(
|
|||
VERBATIM
|
||||
)
|
||||
add_dependencies(run install)
|
||||
|
||||
# ==============================
|
||||
|
||||
set(COMPONENT "ScrapHacks")
|
||||
|
@ -125,6 +132,7 @@ add_custom_command(
|
|||
COMMENT "Generating D3D8_VMT.hpp from d3d8.h"
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
add_custom_target(D3D8_VMT ALL
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/D3D8_VMT.hpp")
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ This will find the Games's installation folder, verify that the version you have
|
|||
- type `import ScrapHack`
|
||||
- type `$help`
|
||||
|
||||
## Config file keys
|
||||
## Config file keys (not yet implemented?)
|
||||
|
||||
- patches.asm: map of address->list of assembly instructions
|
||||
- patches.hex: map of address->hex bytes
|
||||
|
@ -62,3 +62,4 @@ Example:
|
|||
- Zydis disassembler
|
||||
- asmJIT/asmTK assembler
|
||||
- nlohmann/json JSON-parser
|
||||
- LIEF/lief
|
|
@ -1,5 +1,6 @@
|
|||
@echo off
|
||||
setlocal
|
||||
set SCRAPLAND_DIR=%1
|
||||
if "%VSINSTALLDIR%"=="" (
|
||||
for /f "usebackq tokens=*" %%i in (`vswhere -latest -find **\vcvarsall.bat`) do (
|
||||
call "%%i" x86
|
||||
|
@ -7,13 +8,14 @@ if "%VSINSTALLDIR%"=="" (
|
|||
)
|
||||
if "%VSINSTALLDIR%"=="" (
|
||||
echo "VSINSTALLDIR" not set something is wrong!
|
||||
exit
|
||||
) else (
|
||||
if not exist build cmake -G"NMake Makefiles" -B build
|
||||
if "%1"=="--run" (
|
||||
if "%2"=="--run" (
|
||||
cmake --build build --target run
|
||||
) else (
|
||||
cmake --build build --target install
|
||||
)
|
||||
)
|
||||
|
||||
:END
|
||||
endlocal
|
24
ScrapHacks/patcher.py
Normal file
24
ScrapHacks/patcher.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
import lief
|
||||
import sys
|
||||
|
||||
exit("WIP, not really useful yet")
|
||||
|
||||
Scrap = lief.PE.parse(sys.argv[1])
|
||||
|
||||
data = []
|
||||
|
||||
section_data = lief.PE.Section(".hdata")
|
||||
section_data.content = data
|
||||
section_data.virtual_address = 0x8000
|
||||
section_data.characteristics = (
|
||||
lief.PE.SECTION_CHARACTERISTICS.CNT_INITIALIZED_DATA
|
||||
| lief.PE.SECTION_CHARACTERISTICS.MEM_READ
|
||||
)
|
||||
sh = Scrap.add_library("_ScrapHack.pyd")
|
||||
sh.add_entry("Init")
|
||||
|
||||
builder = lief.PE.Builder(Scrap)
|
||||
|
||||
builder.build_imports(True).patch_imports(True).build()
|
||||
|
||||
builder.write("Scrap_mod_sh.exe")
|
4
Scrapper_rs/.gitignore
vendored
Normal file
4
Scrapper_rs/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
target
|
||||
ext
|
||||
.history
|
||||
Cargo.lock
|
12
Scrapper_rs/Cargo.toml
Normal file
12
Scrapper_rs/Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "scrapper"
|
||||
version = "0.1.0"
|
||||
authors = ["Daniel Seiller <earthnuker@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.2"
|
||||
encoding = "0.2.33"
|
||||
structopt = {version="0.3.21",features = [ "paw" ]}
|
202
Scrapper_rs/src/main.rs
Normal file
202
Scrapper_rs/src/main.rs
Normal file
|
@ -0,0 +1,202 @@
|
|||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use encoding::{all::WINDOWS_1252, EncoderTrap};
|
||||
use encoding::{DecoderTrap, Encoding};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
convert::TryInto,
|
||||
fs::{self, File},
|
||||
io::Seek,
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use std::{fs::create_dir_all, io::SeekFrom};
|
||||
use structopt::{StructOpt, paw};
|
||||
use std::{
|
||||
io::{BufReader, BufWriter, Read, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(about = "Scrapland .packed packer and unpacker")]
|
||||
enum Args {
|
||||
/// Unpack .packed file
|
||||
Unpack {
|
||||
destination_folder: PathBuf,
|
||||
packed_files: Vec<PathBuf>,
|
||||
},
|
||||
/// Repack .packed file
|
||||
Repack {
|
||||
input_folder: PathBuf,
|
||||
destination_folder: PathBuf
|
||||
}
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct FileEntry {
|
||||
path: String,
|
||||
offset: u32,
|
||||
size: u32,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Packed {
|
||||
path: PathBuf,
|
||||
ext_path: Option<PathBuf>,
|
||||
magic: [u8; 4],
|
||||
version: [u8; 4],
|
||||
files: Vec<FileEntry>,
|
||||
}
|
||||
|
||||
impl Packed {
|
||||
fn from_file(filename: &PathBuf) -> std::io::Result<Packed> {
|
||||
let mut fh = BufReader::new(File::open(filename)?);
|
||||
let magic: [u8; 4] = [fh.read_u8()?, fh.read_u8()?, fh.read_u8()?, fh.read_u8()?];
|
||||
let version: [u8; 4] = [fh.read_u8()?, fh.read_u8()?, fh.read_u8()?, fh.read_u8()?];
|
||||
println!("Magic: {:?}", magic);
|
||||
println!("Version: {:?}", version);
|
||||
let num_files = fh.read_u32::<LittleEndian>()?;
|
||||
println!("{} files", num_files);
|
||||
let mut files: Vec<FileEntry> = Vec::new();
|
||||
for _ in 0..num_files {
|
||||
let name_len = fh.read_u32::<LittleEndian>()?;
|
||||
let mut name = vec![0; name_len as usize];
|
||||
fh.read_exact(&mut name)?;
|
||||
let size = fh.read_u32::<LittleEndian>()?;
|
||||
let offset = fh.read_u32::<LittleEndian>()?;
|
||||
let path = WINDOWS_1252.decode(&name, DecoderTrap::Strict).unwrap();
|
||||
files.push(FileEntry { path, offset, size });
|
||||
}
|
||||
Ok(Packed {
|
||||
path: filename.to_owned(),
|
||||
magic,
|
||||
version,
|
||||
files,
|
||||
ext_path: None,
|
||||
})
|
||||
}
|
||||
fn size(&self) -> (u64, u64) {
|
||||
let mut header_size: u64 = 4 * 3; // Magic+Version+Num files
|
||||
let mut data_size: u64 = 0;
|
||||
for entry in &self.files {
|
||||
header_size += 4 * 3; // Name length, Offset, Size
|
||||
let path = WINDOWS_1252
|
||||
.encode(&entry.path, EncoderTrap::Strict)
|
||||
.unwrap();
|
||||
let hs: u64 = path.len().try_into().unwrap();
|
||||
header_size += hs;
|
||||
data_size += entry.size as u64;
|
||||
}
|
||||
(header_size, data_size)
|
||||
}
|
||||
|
||||
fn from_folder(folder: PathBuf) -> std::io::Result<Self> {
|
||||
let mut queue = VecDeque::new();
|
||||
queue.push_back(folder.clone());
|
||||
let mut header = Packed {
|
||||
path: PathBuf::new(),
|
||||
magic: [0x42, 0x46, 0x50, 0x4B], // BFPK
|
||||
version: [0x00, 0x00, 0x00, 0x00],
|
||||
files: vec![],
|
||||
ext_path: Some(folder.clone()),
|
||||
};
|
||||
while let Some(dir) = queue.pop_front() {
|
||||
for entry in fs::read_dir(dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
queue.push_back(path);
|
||||
} else {
|
||||
let size = path.metadata().unwrap().len().try_into().unwrap();
|
||||
header.files.push(FileEntry {
|
||||
path: path
|
||||
.strip_prefix(folder.clone())
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.replace("\\", "/"),
|
||||
offset: 0,
|
||||
size,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut offset = header.size().0.try_into().unwrap();
|
||||
for entry in header.files.iter_mut() {
|
||||
entry.offset = offset;
|
||||
offset += entry.size;
|
||||
}
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
fn write(&self, out_path: &PathBuf) -> std::io::Result<()> {
|
||||
let base_path = self.ext_path.clone().unwrap();
|
||||
let mut outfile = BufWriter::new(File::create(out_path)?);
|
||||
outfile.write_all(&self.magic)?;
|
||||
outfile.write_all(&self.version)?;
|
||||
outfile.write_u32::<LittleEndian>(self.files.len() as u32)?;
|
||||
println!("Building header");
|
||||
for entry in &self.files {
|
||||
let path = WINDOWS_1252
|
||||
.encode(&entry.path, EncoderTrap::Strict)
|
||||
.unwrap();
|
||||
outfile.write_u32::<LittleEndian>(path.len().try_into().unwrap())?;
|
||||
outfile.write_all(&path)?;
|
||||
outfile.write_u32::<LittleEndian>(entry.size)?;
|
||||
outfile.write_u32::<LittleEndian>(entry.offset)?;
|
||||
}
|
||||
let total = self.files.len();
|
||||
for (n, entry) in self.files.iter().enumerate() {
|
||||
println!(
|
||||
"[{}/{}] Writing: {} (offset: {}, size: {})",
|
||||
n + 1,
|
||||
total,
|
||||
entry.path,
|
||||
entry.offset,
|
||||
entry.size
|
||||
);
|
||||
let mut fh = BufReader::new(File::open(base_path.join(&entry.path))?);
|
||||
std::io::copy(&mut fh, &mut outfile)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extract(&mut self, ext_folder: &PathBuf) -> std::io::Result<()> {
|
||||
let total = self.files.len();
|
||||
let ext_folder = ext_folder.join(&Path::new(&self.path).file_name().unwrap());
|
||||
println!("Extracting to {}", ext_folder.to_string_lossy());
|
||||
let mut fh = File::open(self.path.clone())?;
|
||||
for (n, entry) in self.files.iter().enumerate() {
|
||||
println!(
|
||||
"[{}/{}] Extracting: {} (offset: {}, size: {})",
|
||||
n + 1,
|
||||
total,
|
||||
entry.path,
|
||||
entry.offset,
|
||||
entry.size
|
||||
);
|
||||
fh.seek(SeekFrom::Start(entry.offset.try_into().unwrap()))?;
|
||||
let mut data = vec![0; entry.size.try_into().unwrap()];
|
||||
let path: PathBuf = PathBuf::from(&entry.path);
|
||||
let file = Path::new(&ext_folder).join(path);
|
||||
fh.read_exact(&mut data)?;
|
||||
create_dir_all(file.parent().unwrap())?;
|
||||
let mut fh = BufWriter::new(File::create(file)?);
|
||||
fh.write_all(&data)?;
|
||||
}
|
||||
self.ext_path = Some(ext_folder);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[paw::main]
|
||||
fn main(args: Args) -> std::io::Result<()> {
|
||||
match args {
|
||||
Args::Unpack { packed_files, destination_folder } => {
|
||||
for packed_file in &packed_files {
|
||||
let mut pkd = Packed::from_file(&packed_file)?;
|
||||
pkd.extract(&destination_folder)?;
|
||||
}
|
||||
}
|
||||
Args::Repack { input_folder, destination_folder } => {
|
||||
Packed::from_folder(input_folder)?.write(&destination_folder)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
51
config.yml
51
config.yml
|
@ -64,6 +64,9 @@ flags:
|
|||
0x8c6140: P_module_filename
|
||||
0x853954: P_D3DApp
|
||||
0x853091: N_Uniones
|
||||
0x7faa4c: server_state
|
||||
0x8039a8: client_data
|
||||
0x8038a0: client_ship
|
||||
|
||||
# 0x7fbe24:
|
||||
# 0x7fa778:
|
||||
|
@ -101,6 +104,48 @@ types:
|
|||
- "struct HashTable { uint32_t size; struct HashTableEntry** data; };"
|
||||
- "struct va_list { unsigned int gp_offset; unsigned int fp_offset; void *overflow_arg_area; void *reg_save_area; };"
|
||||
functions:
|
||||
0x5dadc0:
|
||||
name: read_MAT_block
|
||||
0x5daf10:
|
||||
name: read_MAP_block
|
||||
0x60d710:
|
||||
name: read_INI_block
|
||||
0x6155b0:
|
||||
name: read_DUM_block
|
||||
0x628760:
|
||||
name: read_EMI_block
|
||||
0x62c070:
|
||||
name: read_AMC_block
|
||||
0x62c580:
|
||||
name: read_SUEL_block
|
||||
0x631570:
|
||||
name: read_CMSH_block
|
||||
0x631940:
|
||||
name: read_QUAD_block
|
||||
0x64d380:
|
||||
name: read_LUZ_block
|
||||
0x650f80:
|
||||
name: read_SCN_block
|
||||
0x652480:
|
||||
name: read_LFVF_block
|
||||
0x658770:
|
||||
name: read_CAM_block
|
||||
0x6665a0:
|
||||
name: read_CM3_block
|
||||
0x666900:
|
||||
name: read_SM3_block
|
||||
0x6715e0:
|
||||
name: read_PORT_block
|
||||
0x675c90:
|
||||
name: read_MD3D_block
|
||||
0x6776d0:
|
||||
name: read_NAM_block
|
||||
0x6787a0:
|
||||
name: read_ANI_block
|
||||
0x6869b0:
|
||||
name: read_SPR3_block
|
||||
0x6875d0:
|
||||
name: read_EVA_block
|
||||
0x6283a0:
|
||||
name: load_emi
|
||||
0x4fa9f0:
|
||||
|
@ -371,15 +416,9 @@ functions:
|
|||
name: list_models
|
||||
0x63a2f0:
|
||||
name: gdi_draw_line
|
||||
0x650f80:
|
||||
name: load_sm3
|
||||
0x6597d0:
|
||||
name: read_ini_entry
|
||||
signature: bool read_ini_entry(void* dest,const char* key, const char* section);
|
||||
0x6665a0:
|
||||
name: load_m3d_1
|
||||
0x666900:
|
||||
name: load_m3d_2
|
||||
0x666c60:
|
||||
name: read_m3d
|
||||
0x6b1c70:
|
||||
|
|
76
frida/utils.js
Normal file
76
frida/utils.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
|
||||
function dasm(addr, size) {
|
||||
var size = size || 8;
|
||||
var offset = 0;
|
||||
var ret = [];
|
||||
while (ret.length != size) {
|
||||
var inst = Instruction.parse(ptr(addr).add(offset));
|
||||
ret.push(("[" + inst.address + "] " + inst.mnemonic + " " + inst.opStr).trim());
|
||||
offset += inst.size;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function r(addr, options) {
|
||||
var options = options || {}
|
||||
var max_depth = options.max_depth || 4;
|
||||
var num = options.num || 4;
|
||||
var ret = {};
|
||||
var vals = [
|
||||
"S8",
|
||||
"U8",
|
||||
"S16",
|
||||
"U16",
|
||||
"S32",
|
||||
"U32",
|
||||
"Float",
|
||||
"Double",
|
||||
"Pointer",
|
||||
"CString",
|
||||
"Utf8String",
|
||||
"Utf16String",
|
||||
"AnsiString"
|
||||
];
|
||||
vals.forEach(function (k) {
|
||||
try {
|
||||
ret[k] = ptr(addr)['read' + k]()
|
||||
} catch (e) {
|
||||
ret[k] = undefined;
|
||||
}
|
||||
})
|
||||
try {
|
||||
ret["code"] = dasm(addr, 8);
|
||||
} catch (e) {
|
||||
ret["code"] = undefined;
|
||||
}
|
||||
|
||||
if (max_depth > 1) {
|
||||
var p = {};
|
||||
var read_ptr = false;
|
||||
for (var i = 0; i < num; ++i) {
|
||||
if (ret["Pointer"] === undefined) {
|
||||
continue;
|
||||
}
|
||||
p[i * Process.pointerSize] = r(ret["Pointer"].add(i * Process.pointerSize), {
|
||||
max_depth: max_depth - 1,
|
||||
num
|
||||
});
|
||||
read_ptr = true;
|
||||
}
|
||||
if (read_ptr) {
|
||||
ret["p"] = p;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// function test() {
|
||||
// for (var p = 0; p < 4; ++p) {
|
||||
// var player = ptr(0x7FE944).readPointer().add(0x288 + p * 4).readPointer();
|
||||
// if (!player.isNull()) {
|
||||
// console.log("Player " + (p+1) + ":", player);
|
||||
// console.log(JSON.stringify(r(player),null,4));
|
||||
// }
|
||||
// }
|
||||
// }
|
2367
helplib.md
Normal file
2367
helplib.md
Normal file
File diff suppressed because one or more lines are too long
1558
helplib.txt
1558
helplib.txt
File diff suppressed because it is too large
Load diff
9
notes/.gitignore
vendored
Normal file
9
notes/.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/mdbook
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=mdbook
|
||||
|
||||
### MdBook ###
|
||||
book
|
||||
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/mdbook
|
25
notes/book.toml
Normal file
25
notes/book.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[book]
|
||||
authors = ["Daniel Seiller"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Scrapland Reverse Engineering Notes"
|
||||
|
||||
[output.html]
|
||||
preferred-dark-theme = "ayu"
|
||||
mathjax-support = true
|
||||
|
||||
# [preprocessor.graphviz]
|
||||
# command = "mdbook-graphviz"
|
||||
|
||||
[preprocessor.svgbob]
|
||||
text_width = 8.0
|
||||
text_height = 16.0
|
||||
class = "bob"
|
||||
font_family = "arial"
|
||||
font_size = 14.0
|
||||
stroke_width = 2.0
|
||||
# there's using css-variables from theme:
|
||||
stroke_color = "var(--fg)" # see default theme / variables.css
|
||||
background_color = "transparent" # also useful `var(--bg)`
|
||||
# all properties are optional.
|
140
notes/src/Chunked.md
Normal file
140
notes/src/Chunked.md
Normal file
|
@ -0,0 +1,140 @@
|
|||
# Chunked Formats
|
||||
|
||||
# General Block format
|
||||
|
||||
```cpp
|
||||
struct Block {
|
||||
unsigned char block_id[4],
|
||||
uint32_t size,
|
||||
unsigned char data[size],
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct Block {
|
||||
unsigned char block_id[4],
|
||||
uint32_t size,
|
||||
T data,
|
||||
}
|
||||
```
|
||||
|
||||
# Block IDs
|
||||
|
||||
File ID | Chunk IDs
|
||||
------- | ------------------------------------------------------------------------------
|
||||
AMC | AMC, CMSH, QUAD
|
||||
CM3 | ANI, CM3, EVA, NAE, NAM, SCN
|
||||
DUM | DUM, INI
|
||||
EMI | EMI, LFVF, MAP, MAT, TRI
|
||||
SM3 | ANI, CAM, INI, LFVF, LUZ, MAP, MAT, MD3D, NAE, NAM, PORT, SCN, SM3, SPR3, SUEL
|
||||
|
||||
Read types:
|
||||
|
||||
- `i`: 4-byte unsigned integer
|
||||
- `s`: 4-byte signed integer
|
||||
- `p`: length prefixed string
|
||||
- `f`: 4-byte float
|
||||
- `3f`: array of 3 4-byte floats
|
||||
- `3i`: array of 3 4-byte unsigned integers
|
||||
|
||||
Chunk ID | Description | Reads
|
||||
-------- | --------------------------- | ------------------------
|
||||
AMC | Collision Data |
|
||||
ANI | Animation data? |
|
||||
CAM | Camera info? |
|
||||
CMSH | Collision Mesh Data |
|
||||
DUM | Dummy (map object) data |
|
||||
INI | INI-Configuration data |
|
||||
LFVF | FVF Vertex Data |
|
||||
LUZ | Lighting information |
|
||||
MAP | UV Map? |
|
||||
MAT | Material information |
|
||||
NAE | Animation Data? |
|
||||
NAM | Animation Data? |
|
||||
PORT | Map portals? | i==1, i, i, 4, 4
|
||||
QUAD | Mesh data? |
|
||||
SCN | Scene data? |
|
||||
SUEL | Ground plane? | 0x18, 0xc, 4, 4, 4, 0x18
|
||||
TRI | Triangle strip definitions? |
|
||||
MD3D | 3D Model definition? |
|
||||
|
||||
# Format of Specific chunks
|
||||
|
||||
## INI
|
||||
|
||||
Configuration Data
|
||||
|
||||
```cpp
|
||||
struct INI {
|
||||
uint32_t num_sections;
|
||||
struct {
|
||||
uint32_t num_lines;
|
||||
struct {
|
||||
uint32_t num_chars;
|
||||
char line[num_chars]
|
||||
} lines[num_lines];
|
||||
} sections[num_sections];
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
## LFVF
|
||||
|
||||
DirectX Flexible Vertex Format Data
|
||||
|
||||
```cpp
|
||||
struct Vertex { // fields according to flags
|
||||
float position[3]; // D3DFVF_XYZ | D3DFVF_XYZRHW | D3DFVF_XYZB*
|
||||
float rhw; // D3DFVF_XYZRHW
|
||||
float weights[3]; // D3DFVF_XYZB*
|
||||
float normal[3]; // D3DFVF_NORMAL
|
||||
float point_size; // D3DFVF_PSIZE
|
||||
uint32_t diffuse; // D3DFVF_DIFFUSE, RGBA
|
||||
uint32_t specular; //D3DFVF_SPECULAR, RGBA
|
||||
float tex_coords[D3DFVF_TEXTUREFORMAT][D3DFVF_TEX]; // D3DFVF_TEX* and D3DFVF_TEXTUREFORMAT*
|
||||
};
|
||||
|
||||
struct LFVF {
|
||||
uint32_t unk;
|
||||
uint32_t num_entries;
|
||||
struct {
|
||||
uint32_t FVF; // FVF vertex data configuration
|
||||
uint32_t vert_size; //?,
|
||||
uint32_t num_verts;
|
||||
Vertex vertices[num_vers];
|
||||
} entry[num_entries];
|
||||
};
|
||||
```
|
||||
|
||||
## DUM
|
||||
|
||||
Map object data
|
||||
|
||||
```cpp
|
||||
struct DUM {
|
||||
uint32_t unk_1;
|
||||
uint32_t num_dummies;
|
||||
uint32_t unk_2;
|
||||
struct {
|
||||
uint32_t name_length;
|
||||
char name[name_length];
|
||||
float position[3];
|
||||
float rotation[3];
|
||||
uint32_t has_ini;
|
||||
if (has_ini) {
|
||||
Block<INI> ini;
|
||||
};
|
||||
uint32_t unk_1; // has_next?
|
||||
} sections[num_sections];
|
||||
};
|
||||
```
|
||||
|
||||
## MAP
|
||||
|
||||
```cpp
|
||||
struct MAP {
|
||||
uint32_t version;
|
||||
uint32_t tex_name_len;
|
||||
char tex_name[tex_name_len];
|
||||
// TODO: rest
|
||||
}
|
||||
```
|
1
notes/src/Entities.md
Normal file
1
notes/src/Entities.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Entities
|
18
notes/src/File.md
Normal file
18
notes/src/File.md
Normal file
|
@ -0,0 +1,18 @@
|
|||
# File Formats
|
||||
|
||||
|
||||
File Extension | Description | Chunked
|
||||
-------------- | ------------------------ | -------
|
||||
.packed | Game Data Archive | n
|
||||
.cm3 | Animation file | y
|
||||
.sm3 | 3d model file | y
|
||||
.dum | Dummy (map object) file | y
|
||||
.pth | AI Path | n
|
||||
.emi | Emission maps/Materials? | y
|
||||
.amc | Collision Data | y
|
||||
.ini | Configuration | n
|
||||
.txa | Texture Animation Config | n
|
||||
|
||||
- [Chunked](Chunked.md)
|
||||
- [Packed](Packed.md)
|
||||
- [AI Pathfinding Graph](Nodegraph.md)
|
1
notes/src/File_Formats.md
Normal file
1
notes/src/File_Formats.md
Normal file
|
@ -0,0 +1 @@
|
|||
# File Formats
|
41
notes/src/Netplay.md
Normal file
41
notes/src/Netplay.md
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Netplay
|
||||
|
||||
Game Info Packet
|
||||
|
||||
```
|
||||
Server 'B':FZ (0/10) Ver 1.0 at 192.168.99.1:28086
|
||||
[0-3] header/ID?
|
||||
[4-5] port (16-bit)
|
||||
[6-7] max_players (16-bit)
|
||||
[8-9] curr_player (16-bit)
|
||||
[10-x] server name (char*)
|
||||
|
||||
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
||||
0019fdc0 ba ce 00 01 b6 6d 0a 00 00 00 42 00 30 fe 19 00 .....m....B.0...
|
||||
0019fdd0 ff ff ff ff 27 2b b3 9b c7 3e bb 00 9c af 29 00 ....'+...>....).
|
||||
0019fde0 db 69 00 00 00 00 00 00 00 00 44 65 61 74 68 4d .i........DeathM
|
||||
0019fdf0 61 74 63 68 00 00 00 00 ff ff 46 5a 00 4a 91 f0 atch......FZ.J..
|
||||
0019fe00 92 8b 57 4e 7f 00 00 00 10 21 fe 38 0d ae 00 00 ..WN.....!.8....
|
||||
0019fe10 f0 ce f3 36 a0 e8 0b 77 a0 e8 ...6...w..
|
||||
```
|
||||
|
||||
|
||||
Player Join Packet
|
||||
|
||||
```
|
||||
[0-3] header/ID?
|
||||
[6-x] Player name
|
||||
|
||||
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
||||
09c9dfe8 7f 47 00 00 00 0e 55 6e 6e 61 6d 65 64 20 50 6c .G....Unnamed Pl
|
||||
09c9dff8 61 79 65 72 06 53 42 6f 73 73 31 b9 00 07 50 5f ayer.SBoss1...P_
|
||||
09c9e008 42 65 74 74 79 06 4d 42 4f 53 53 31 06 4d 42 4f Betty.MBOSS1.MBO
|
||||
09c9e018 53 53 31 00 00 10 30 2c 31 35 2c 30 2c 30 2c 31 SS1...0,15,0,0,1
|
||||
09c9e028 35 2c 31 35 2c 31 02 00 00 00 5,15,1....
|
||||
```
|
||||
|
||||
Message | Description
|
||||
---------------------------------------- | -----------------------------------------------------------------
|
||||
`5c68625c32383230395c73637261706c616e64` | "Scrapland Server" announcement broadcast (`\hb\28209\scrapland`)
|
||||
`7f01000007` | Retrieve Game info
|
||||
`48423d35323932322c3235363a323830383600` | Connection Information (`HB=52922,256:28086`)
|
26
notes/src/Nodegraph.md
Normal file
26
notes/src/Nodegraph.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# AI Nodegraph
|
||||
|
||||
Used for pathfinding using the A*-Algorithm.
|
||||
|
||||
```cpp
|
||||
// n= number of dimension (3 or 2)
|
||||
|
||||
template<size_t n>
|
||||
struct Node {
|
||||
float pos[n];
|
||||
};
|
||||
|
||||
template<size_t n>
|
||||
struct Edge {
|
||||
uint32_t num_edge_nodes;
|
||||
Node<n> nodes[];
|
||||
};
|
||||
|
||||
template<size_t n>
|
||||
struct Graph {
|
||||
uint32_t num_nodes;
|
||||
Node<n> nodes[];
|
||||
uint32_t num_edges;
|
||||
Edge<n> edges[];
|
||||
};
|
||||
```
|
1
notes/src/Overview.md
Normal file
1
notes/src/Overview.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Overview
|
15
notes/src/Packed.md
Normal file
15
notes/src/Packed.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
# Packed
|
||||
|
||||
```cpp
|
||||
struct Header {
|
||||
unsigned char magic[4]; // always BFPK
|
||||
uint32_t version;
|
||||
uint32_t number_of_files;
|
||||
struct File {
|
||||
uint32_t path_length;
|
||||
char path[]; // latin1 encoding
|
||||
uint32_t data_size;
|
||||
uint32_t data_offset; // offset includes header size so it can be used directly in a seek() call
|
||||
} files[];
|
||||
};
|
||||
```
|
2369
notes/src/Python.md
Normal file
2369
notes/src/Python.md
Normal file
File diff suppressed because one or more lines are too long
1
notes/src/Python_API.md
Normal file
1
notes/src/Python_API.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Python API
|
13
notes/src/SUMMARY.md
Normal file
13
notes/src/SUMMARY.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Summary
|
||||
|
||||
- [Overview](./Overview.md)
|
||||
- [ScrapEngine](./ScrapEngine.md)
|
||||
- [World](./World.md)
|
||||
- [Entities](./Entities.md)
|
||||
- [Netplay](./Netplay.md)
|
||||
- [Python API](./Python_API.md)
|
||||
- [File Formats](./File_Formats.md)
|
||||
- [Chunked Formats](./Chunked.md)
|
||||
- [Packed](./Packed.md)
|
||||
- [AI Nodegraph](./Nodegraph.md)
|
||||
- [ScrapHacks](./ScrapHacks.md)
|
54
notes/src/ScrapEngine.md
Normal file
54
notes/src/ScrapEngine.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
# ScrapEngine
|
||||
|
||||
- Ingame Scripting Language: Python 1.5.2
|
||||
- Interesting memory locations and functions are noted in `config.yml`
|
||||
|
||||
## Launch options
|
||||
|
||||
- `-console`: open external console window on start
|
||||
- `-wideWindow`: start game in widescreen mode
|
||||
- `-dedicated`: start in multiplayer dedicated server mode (needs to be used with `-server`)
|
||||
- `-server`: start in multiplayer server mode
|
||||
- `-build`: Rebuild `Data.packed` (needs a `filelist.2Bpack`)
|
||||
|
||||
## Ingame-Console
|
||||
|
||||
(Ctrl+\^ or right click on window title bar and select "switch console") (Handler @ `0x402190`)
|
||||
|
||||
* `<Code>`: Evaluate Python code
|
||||
* `:<Var>`: Get Game Engine Variable
|
||||
* `:<Var> <Val>`: Set Game Engine Variable
|
||||
* `?`: Show all Game Engine Variables
|
||||
* `?<String>`: Show all Game Engine Variables matching `<String>`
|
||||
* `/<command>`: Run Command defined in `QuickConsole.py`
|
||||
* `import quickconsole;quickconsole.%s()`
|
||||
* `/<command> <arg>,<arg>`: Run function in `QuickConsole.py` with argument(s)
|
||||
* `import quickconsole;quickconsole.%s(%s)`
|
||||
|
||||
## External Console
|
||||
|
||||
(Scene graph debugging?) (Handler @ `0x5f9520`)
|
||||
|
||||
* `listar luces` List lights in scene
|
||||
* `listar` list models in scene
|
||||
* `arbol <model_name>` show details for model
|
||||
* `mem` (doesn't do anything?)
|
||||
* `ver uniones`
|
||||
* Easter Eggs:
|
||||
* `imbecil`
|
||||
* `idiota`
|
||||
* `capullo`
|
||||
|
||||
|
||||
## Other interesting Memory Addresses
|
||||
|
||||
- `0x852914`: D3D8-Device pointer
|
||||
- `0x7FCC00`: number of opened `.packed` files
|
||||
- `0x84cb64`: pointer to console command handler
|
||||
- `0x7fac84`: pointer to C++ callback list structure
|
||||
- `0x80b2cc`: pointer to ActionClassList (???)
|
||||
- `0x807a20`: pointer to SScorer (ingame GUI/Menu/Text system) structure (???)
|
||||
- `0x80a398`: pointer to SoundSystem (???)
|
||||
- `0x8b18f0`: pointer to Models Data (can be dumped using scene graph debugging console)
|
||||
- `0x8b18f4`: pointer to Scenes Data (can be dumped using scene graph debugging console)
|
||||
- `0x8b18f8`: pointer to active Models Data (can be dumped using scene graph debugging console)
|
1
notes/src/ScrapHacks.md
Normal file
1
notes/src/ScrapHacks.md
Normal file
|
@ -0,0 +1 @@
|
|||
# ScrapHacks
|
80
notes/src/World.md
Normal file
80
notes/src/World.md
Normal file
|
@ -0,0 +1,80 @@
|
|||
# World
|
||||
|
||||
## Game World/State Pointer @ `0x7fe944`
|
||||
|
||||
Points to World struct
|
||||
|
||||
Offset | Type | Description
|
||||
------ | ------------------------ | --------------------------------------
|
||||
0x0000 | `void**` | Virtual Method Table
|
||||
0x0004 | `uint32_t` | Slots in Entity Hashtable
|
||||
0x0008 | `void**` | Pointer to Entity Hashtable
|
||||
0x00B0 | `??` | Pointer to Ground Object (?)
|
||||
0x0288 | `pyEntity*` | UsrEntity[0]
|
||||
0x028C | `pyEntity*` | UsrEntity[1]
|
||||
0x0290 | `pyEntity*` | UsrEntity[2]
|
||||
0x0294 | `pyEntity*` | UsrEntity[3]
|
||||
0x0298 | `uint32_t` | Slots in Model Hashtable
|
||||
0x029C | `void**` | Pointer to Model Hashtable
|
||||
0x02B8 | `uint32_t` | Slots in Entity lists Hashtable
|
||||
0x02BC | `void**` | Pointer to Entity list Hashtable
|
||||
0x0330 | `float[3]` | Time (why 3 times?)
|
||||
0x1C6C | `float` | Alarm level
|
||||
0x1C68 | `float` | Alarm Grow Level
|
||||
0x2158 | `float` | Used in `World_Init`
|
||||
0x2170 | `???` | Used in `World_Init`
|
||||
0x2180 | `float` | Used in `World_Init`
|
||||
0x2188 | `void*` | Used in `World_Init`
|
||||
0x218C | `void*` | Used in `World_Init`
|
||||
0x2190 | `float` | Used in `World_Init`
|
||||
0x2198 | `void*` | Used in `World_Init`
|
||||
0x219C | `void*` | Used in `World_Init`
|
||||
0x21A0 | `void**` | Used in `World_Init` (VTable pointer?)
|
||||
0x21B4 | `void**` | Used in `World_Init` (VTable pointer?)
|
||||
0x21C8 | `???` | Used in `World_Init`
|
||||
0x2204 | `uint32_t` or `uint16_t` | Used in `World_Init`
|
||||
0x2230 | `float` | Used in `World_Init`
|
||||
0x2238 | `???` | Used in `World_Init`
|
||||
0x2254 | `float` | Used in `World_Init`
|
||||
|
||||
## cPyEntity structure
|
||||
|
||||
Offset | Type | Description
|
||||
------ | -------- | --------------------
|
||||
0x0000 | `void**` | Virtual Method Table
|
||||
0x0004 | `char*` | Name
|
||||
0x0008 | `void*` | ???
|
||||
|
||||
|
||||
## Entity Hash Table
|
||||
|
||||
Hash-function used: [PJW](https://en.wikipedia.org/wiki/PJW_hash_function) (Same parameters as the example implementation)
|
||||
|
||||
Entry format:
|
||||
|
||||
```cpp
|
||||
struct HT_Entry {
|
||||
void* data;
|
||||
const char* key;
|
||||
HT_Entry* next;
|
||||
}
|
||||
```
|
||||
|
||||
Data format:
|
||||
|
||||
Offset | Type | Description
|
||||
------ | ------------- | ------------------------
|
||||
0x0 | `void**` | Virtual Method Table (?)
|
||||
0x4 | `const char*` | name as string
|
||||
0x14 | `void*` | pointer to self (why?)
|
||||
0x28 | `float[3]` | Position in Game World
|
||||
|
||||
## EntityList Hash Table
|
||||
|
||||
Attributes:
|
||||
- `Near`
|
||||
- `First`
|
||||
- `Num`
|
||||
- `OnDeath`
|
||||
- `OnDamage`
|
||||
- ...
|
21
parse_save.py
Normal file
21
parse_save.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import sys
|
||||
from construct import *
|
||||
from pprint import pprint
|
||||
|
||||
ScrapSaveVar = Struct(
|
||||
"name" / PascalString(Int32ul, encoding="utf-8"),
|
||||
"data" / PascalString(Int32ul, encoding="utf-8"),
|
||||
)
|
||||
ScrapSave = "ScarpSaveGame" / Struct(
|
||||
"title" / PascalString(Int32ul, encoding="utf-8"),
|
||||
"id" / PascalString(Int32ul, encoding="utf-8"),
|
||||
"data" / PrefixedArray(Int32ul, ScrapSaveVar),
|
||||
Terminated,
|
||||
)
|
||||
with open(sys.argv[1], "rb") as sav_file:
|
||||
save = ScrapSave.parse_stream(sav_file)
|
||||
print("ID:", save.id)
|
||||
print("Title:", save.title)
|
||||
for var in save.data:
|
||||
print(" {}: {}".format(var.name, var.data))
|
||||
|
|
@ -113,7 +113,6 @@ for addr, func in config.functions.items():
|
|||
r2_cmd(f"afr fcn.{name} {hex(addr)}")
|
||||
r2_cmd(f"afn fcn.{name} {hex(addr)}")
|
||||
if sig:
|
||||
sig = sig.replace(name, "fcn." + name)
|
||||
r2_cmd(f'"afs {sig}" @{hex(addr)}')
|
||||
|
||||
|
||||
|
@ -305,7 +304,7 @@ print("[+] Wrote scrap_dissect.x32dbg.txt")
|
|||
with open(r2_script_path, "w") as of:
|
||||
wcmds = []
|
||||
for cmd in r2cmds:
|
||||
if cmd == "avj":
|
||||
if cmd=="avj":
|
||||
continue
|
||||
record = True
|
||||
for start in ["p", "/", "s"]:
|
||||
|
@ -317,7 +316,6 @@ with open(r2_script_path, "w") as of:
|
|||
|
||||
print("[+] Wrote scrap_dissect.r2")
|
||||
|
||||
|
||||
def start_program(cmdl, **kwargs):
|
||||
if os.name == "nt":
|
||||
return SP.Popen(["cmd", "/c", "start"] + cmdl, **kwargs)
|
||||
|
|
343
rz_analyze.py
Normal file
343
rz_analyze.py
Normal file
|
@ -0,0 +1,343 @@
|
|||
import rzpipe
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime
|
||||
import subprocess as SP
|
||||
from tqdm import tqdm
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
tqdm_ascii = False
|
||||
|
||||
rzcmds = []
|
||||
x64_dbg_script = []
|
||||
script_path = os.path.dirname(os.path.abspath(__file__))
|
||||
scrap_exe = os.path.abspath(sys.argv[1])
|
||||
scrapland_folder = os.path.abspath(os.path.dirname(scrap_exe))
|
||||
rz_script_path = os.path.join(scrapland_folder, "scrap_dissect.rz")
|
||||
x64_dbg_script_path = os.path.join(scrapland_folder, "scrap_dissect.x32dbg.txt")
|
||||
json_path = os.path.join(scrapland_folder, "scrap_dissect.json")
|
||||
|
||||
assert os.path.isfile(scrap_exe), "File not found!"
|
||||
rz = rzpipe.open(scrap_exe)
|
||||
file_hashes = rz.cmdj("itj")
|
||||
target_hashes = {
|
||||
"sha1": "d2dde960e8eca69d60c2e39a439088b75f0c89fa",
|
||||
"md5": "a934c85dca5ab1c32f05c0977f62e186",
|
||||
"sha256": "24ef449322f28f87b702834f1a1aac003f885db6d68757ff29fad3ddba6c7b88",
|
||||
}
|
||||
|
||||
assert file_hashes == target_hashes, "Hash mismatch"
|
||||
|
||||
|
||||
def x64_dbg_label(addr, name, prefix=None):
|
||||
global x64_dbg_script
|
||||
if isinstance(addr, int):
|
||||
addr = hex(addr)
|
||||
if prefix:
|
||||
x64_dbg_script.append(f'lbl {addr},"{prefix}.{name}"')
|
||||
else:
|
||||
x64_dbg_script.append(f'lbl {addr},"{name}"')
|
||||
|
||||
|
||||
def rz_cmd(cmd):
|
||||
global rz, rzcmds
|
||||
rzcmds.append(cmd)
|
||||
return rz.cmd(cmd)
|
||||
|
||||
|
||||
def rz_cmdj(cmd):
|
||||
global rz, rzcmds
|
||||
rzcmds.append(cmd)
|
||||
return rz.cmdj(cmd)
|
||||
|
||||
|
||||
def rz_cmdJ(cmd):
|
||||
global rz, rzcmds
|
||||
rzcmds.append(cmd)
|
||||
return rz.cmdJ(cmd)
|
||||
|
||||
|
||||
t_start = datetime.today()
|
||||
|
||||
|
||||
def analysis(full=False):
|
||||
print("[*] Running analysis")
|
||||
steps = []
|
||||
if full:
|
||||
steps = [
|
||||
"e anal.dataref = true",
|
||||
# "e anal.esil = true",
|
||||
"e anal.jmp.after = true",
|
||||
"e anal.jmp.indir = true",
|
||||
"e anal.loads = true",
|
||||
"e anal.pushret = true",
|
||||
"e anal.refstr = true",
|
||||
"e anal.strings = true",
|
||||
"e anal.vinfun = true",
|
||||
"e asm.anal = true",
|
||||
]
|
||||
if full:
|
||||
steps += ["aaaa"]
|
||||
else:
|
||||
steps += ["aaa"]
|
||||
for ac in steps:
|
||||
print(f"[*] Running '{ac}'")
|
||||
rz_cmd(f"{ac} 2>NUL")
|
||||
|
||||
|
||||
with open(os.path.join(script_path, "config.yml")) as cfg:
|
||||
print("[*] Loading config")
|
||||
config = type("Config", (object,), yaml.load(cfg, Loader=yaml.SafeLoader))
|
||||
|
||||
for line in config.script.strip().splitlines():
|
||||
rz_cmd(line)
|
||||
|
||||
analysis(False)
|
||||
|
||||
for addr, comment in config.comments.items():
|
||||
rz_cmd(f"CC {comment} @ {hex(addr)}")
|
||||
|
||||
for t in config.types:
|
||||
rz_cmd(f'"td {t}"')
|
||||
|
||||
for addr, name in config.flags.items():
|
||||
x64_dbg_label(addr, name, "loc")
|
||||
rz_cmd(f"f loc.{name} 4 {hex(addr)}")
|
||||
|
||||
|
||||
for addr, func in config.functions.items():
|
||||
name, sig = func.get("name"), func.get("signature")
|
||||
if name:
|
||||
x64_dbg_label(addr, name, "fcn")
|
||||
rz_cmd(f"afr fcn.{name} {hex(addr)}")
|
||||
rz_cmd(f"afn fcn.{name} {hex(addr)}")
|
||||
if sig:
|
||||
sig = sig.replace(name, "fcn." + name)
|
||||
rz_cmd(f'"afs {sig}" @{hex(addr)}')
|
||||
|
||||
|
||||
def vtables():
|
||||
ret = {}
|
||||
print("[*] Analyzing VTables")
|
||||
vtables = rz_cmdJ("avj")
|
||||
for c in tqdm(vtables, ascii=tqdm_ascii):
|
||||
methods = []
|
||||
name = config.VMTs.get(c.offset, f"{c.offset:08x}")
|
||||
x64_dbg_label(c.offset, name, "vmt")
|
||||
rz_cmd(f"f vmt.{name} 4 {hex(c.offset)}")
|
||||
for idx, m in enumerate(tqdm(c.methods, ascii=tqdm_ascii, leave=False)):
|
||||
methods.append(hex(m.offset))
|
||||
x64_dbg_label(m.offset, f"{name}.{idx}", "fcn.vmt")
|
||||
rz_cmd(f"afr fcn.vmt.{name}.{idx} {hex(m.offset)} 2>NUL")
|
||||
ret[hex(c.offset)] = methods
|
||||
return ret
|
||||
|
||||
|
||||
def c_callbacks():
|
||||
print("[*] Parsing C Callbacks")
|
||||
funcs = {}
|
||||
res = rz_cmd("/r fcn.register_c_callback ~CALL[1]").splitlines()
|
||||
for addr in tqdm(res, ascii=tqdm_ascii):
|
||||
rz_cmd(f"s {addr}")
|
||||
rz_cmd(f"so -3")
|
||||
func, name = rz_cmdJ(f"pdj 2")
|
||||
func = func.refs[0].addr
|
||||
name = rz_cmd(f"psz @{hex(name.refs[0].addr)}").strip()
|
||||
rz_cmd(f"afr fcn.callbacks.{name} {hex(func)} 2>NUL")
|
||||
x64_dbg_label(func, f"{name}", "fcn.callbacks")
|
||||
funcs[name] = hex(func)
|
||||
return funcs
|
||||
|
||||
|
||||
def assertions():
|
||||
assertions = {}
|
||||
for (n_args, a_addr) in [
|
||||
(3, "fcn.throw_assertion_1"),
|
||||
(4, "fcn.throw_assertion_2"),
|
||||
]:
|
||||
print(f"[*] Parsing C assertions for {a_addr}")
|
||||
res = rz_cmd(f"/r {a_addr} ~CALL[1]").splitlines()
|
||||
print()
|
||||
for line in tqdm(res, ascii=tqdm_ascii):
|
||||
addr = line.strip()
|
||||
rz_cmd(f"s {addr}")
|
||||
rz_cmd(f"so -{n_args}")
|
||||
dis = rz_cmdJ(f"pij {n_args}")
|
||||
if n_args == 4:
|
||||
line, date, file, msg = dis
|
||||
elif n_args == 3:
|
||||
date = None
|
||||
line, file, msg = dis
|
||||
try:
|
||||
file = rz_cmd(f"psz @{file.refs[0].addr}").strip()
|
||||
msg = rz_cmd(f"psz @{msg.refs[0].addr}").strip()
|
||||
if date:
|
||||
date = rz_cmd(f"psz @{date.refs[0].addr}").strip()
|
||||
line = line.val
|
||||
file = file.replace("\\\\", "\\")
|
||||
assertions.setdefault(file, [])
|
||||
assertions[file].append(
|
||||
{"line": line, "date": date, "addr": addr, "msg": msg}
|
||||
)
|
||||
except:
|
||||
pass
|
||||
for path in assertions:
|
||||
assertions[path].sort(key=lambda v: v["line"])
|
||||
return assertions
|
||||
|
||||
|
||||
def bb_refs(addr):
|
||||
ret = {}
|
||||
res = rz_cmd(f"/r {addr} ~fcn[0,1]").splitlines()
|
||||
print()
|
||||
for ent in tqdm(res, ascii=tqdm_ascii):
|
||||
func, hit = ent.split()
|
||||
ret[hit] = {"asm": [], "func": func}
|
||||
for ins in rz_cmdJ(f"pdbj @{hit}"):
|
||||
ret[hit]["asm"].append(ins.disasm)
|
||||
return ret
|
||||
|
||||
|
||||
def world():
|
||||
print("[*] Parsing World offsets")
|
||||
return bb_refs("loc.P_World")
|
||||
|
||||
|
||||
def render():
|
||||
print("[*] Parsing D3D_Device offsets")
|
||||
return bb_refs("loc.P_D3D8_Dev")
|
||||
|
||||
|
||||
def py_mods():
|
||||
print("[*] Parsing Python modules")
|
||||
res = rz_cmd("/r fcn.Py_InitModule ~CALL[1]").splitlines()
|
||||
print()
|
||||
py_mods = {}
|
||||
for call_loc in tqdm(res, ascii=tqdm_ascii):
|
||||
rz_cmd(f"s {call_loc}")
|
||||
rz_cmd(f"so -3")
|
||||
args = rz_cmdJ("pdj 3")
|
||||
refs = []
|
||||
if not all(arg.type == "push" for arg in args):
|
||||
continue
|
||||
for arg in args:
|
||||
refs.append(hex(arg.val))
|
||||
doc, methods, name = refs
|
||||
doc = rz_cmd(f"psz @{doc}").strip()
|
||||
name = rz_cmd(f"psz @{name}").strip()
|
||||
rz_cmd(f"s {methods}")
|
||||
rz_cmd(f"f py.{name} 4 {methods}")
|
||||
x64_dbg_label(methods, f"{name}", "py")
|
||||
py_mods[name] = {"methods_addr": methods, "doc": doc, "methods": {}}
|
||||
while True:
|
||||
m_name, m_func, _, m_doc = [v.value for v in rz_cmdJ(f"pfj xxxx")]
|
||||
if m_name == 0:
|
||||
break
|
||||
m_name, m_func, m_doc = map(hex, (m_name, m_func, m_doc))
|
||||
m_name = rz_cmd(f"psz @{m_name}").strip()
|
||||
rz_cmd(f"f py.{name}.{m_name}.__doc__ 4 {m_doc}")
|
||||
if int(m_doc, 16) != 0:
|
||||
x64_dbg_label(m_doc, f"{name}.{m_name}.__doc__", "py")
|
||||
m_doc = rz_cmd(f"psz @{m_doc}").strip()
|
||||
else:
|
||||
m_doc = None
|
||||
py_mods[name]["methods"][m_name] = {"addr": m_func, "doc": m_doc}
|
||||
rz_cmd(f"afr py.{name}.{m_name} {m_func} 2>NUL")
|
||||
x64_dbg_label(m_func, f"{name}.{m_name}", "fcn.py")
|
||||
rz_cmd("s +16")
|
||||
return py_mods
|
||||
|
||||
|
||||
def game_vars():
|
||||
ret = {}
|
||||
print("[*] Parsing Game variables")
|
||||
res = rz_cmd("/r fcn.setup_game_vars ~CALL[1]").splitlines()
|
||||
print()
|
||||
for line in tqdm(res, ascii=tqdm_ascii):
|
||||
addr = line.strip()
|
||||
rz_cmd(f"s {addr}")
|
||||
args = rz_cmd("pdj -5") # seek and print disassembly
|
||||
if not args:
|
||||
continue
|
||||
args = json.loads(args)
|
||||
args_a = []
|
||||
push_cnt = 0
|
||||
for arg in args[::-1]:
|
||||
if arg["type"] not in ["push", "mov"]:
|
||||
continue
|
||||
if arg["type"] == "push":
|
||||
push_cnt += 1
|
||||
args_a.append(arg)
|
||||
if push_cnt == 3:
|
||||
break
|
||||
if len(args_a) != 4:
|
||||
continue
|
||||
if not all("val" in v for v in args_a):
|
||||
continue
|
||||
addr, name, _, desc = [v["val"] for v in args_a]
|
||||
name = rz_cmd(f"psz @{hex(name)}").strip()
|
||||
desc = rz_cmd(f"psz @{hex(desc)}").strip()
|
||||
addr = hex(addr)
|
||||
rz_cmd(f"f loc.gvar.{name} 4 {addr}")
|
||||
x64_dbg_label(addr, f"{name}", "loc.gvar")
|
||||
ret[addr] = {"name": name, "desc": desc}
|
||||
return ret
|
||||
|
||||
|
||||
ret = {}
|
||||
# world, render
|
||||
for func in ["game_vars", "c_callbacks", "py_mods", "assertions", "vtables"]:
|
||||
ret[func] = globals()[func]()
|
||||
|
||||
analysis(True)
|
||||
|
||||
with open(json_path, "w") as of:
|
||||
json.dump(ret, of, indent=4)
|
||||
|
||||
print("[+] Wrote scrap_dissect.json")
|
||||
|
||||
with open(x64_dbg_script_path, "w") as of:
|
||||
of.write("\n".join(x64_dbg_script))
|
||||
|
||||
print("[+] Wrote scrap_dissect.x32dbg.txt")
|
||||
|
||||
with open(rz_script_path, "w") as of:
|
||||
wcmds = []
|
||||
for cmd in rzcmds:
|
||||
if cmd == "avj":
|
||||
continue
|
||||
record = True
|
||||
for start in ["p", "/", "s"]:
|
||||
if cmd.strip('"').startswith(start):
|
||||
record = False
|
||||
if record:
|
||||
wcmds.append(cmd)
|
||||
of.write("\n".join(wcmds))
|
||||
|
||||
print("[+] Wrote scrap_dissect.rz")
|
||||
|
||||
|
||||
def start_program(cmdl, **kwargs):
|
||||
if os.name == "nt":
|
||||
return SP.Popen(["cmd", "/c", "start"] + cmdl, **kwargs)
|
||||
else:
|
||||
return SP.Popen(cmdl, **kwargs)
|
||||
|
||||
|
||||
print("[+] Analysis took:", datetime.today() - t_start)
|
||||
rz.cmd("Ps Scrap.rzdb")
|
||||
exit()
|
||||
|
||||
print("[+] Executing Cutter")
|
||||
try:
|
||||
start_program(
|
||||
["cutter", "-A", "0", "-i", rz_script_path, scrap_exe],
|
||||
cwd=scrapland_folder,
|
||||
shell=False,
|
||||
)
|
||||
except FileNotFoundError:
|
||||
print("[-] cutter not installed, falling back to rz")
|
||||
start_program(
|
||||
["rz", "-i", rz_script_path, scrap_exe], cwd=scrapland_folder, shell=False
|
||||
)
|
132
scrapper.py
Normal file
132
scrapper.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
import argparse
|
||||
from collections import OrderedDict
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
from construct import *
|
||||
from tqdm import tqdm
|
||||
|
||||
setglobalstringencoding(None)
|
||||
|
||||
ScrapFile = Struct(
|
||||
"path" / PascalString(Int32ul),
|
||||
"size" / Int32ul,
|
||||
"offset" / Int32ul,
|
||||
"data" / OnDemandPointer(this.offset, Bytes(this.size)),
|
||||
)
|
||||
DummyFile = Struct("path" / PascalString(Int32ul), "size" / Int32ul, "offset" / Int32ul)
|
||||
|
||||
PackedHeader = Struct(
|
||||
Const(b"BFPK"), Const(b"\0\0\0\0"), "files" / PrefixedArray(Int32ul, ScrapFile)
|
||||
)
|
||||
DummyHeader = Struct(
|
||||
Const(b"BFPK"), Const(b"\0\0\0\0"), "files" / PrefixedArray(Int32ul, DummyFile)
|
||||
)
|
||||
parser = argparse.ArgumentParser(description="Unpack and Repack .packed files")
|
||||
parser.add_argument(
|
||||
"-u", "--unpack", action="store_true", help="unpack file to 'extracted' directory"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r", "--repack", action="store_true", help="repack file from 'extracted' directory"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--reset", action="store_true", default=False, help="restore backup"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"scrap_dir",
|
||||
metavar="Scrapland Directory",
|
||||
type=str,
|
||||
default=".",
|
||||
help="Scrapland installation directory",
|
||||
)
|
||||
options = parser.parse_args()
|
||||
scrap_dir = os.path.abspath(options.scrap_dir)
|
||||
|
||||
if options.reset:
|
||||
print("Restoring Backups and removing extracted folder...")
|
||||
for packed_file in glob.glob(os.path.join(scrap_dir, "*.packed.bak")):
|
||||
outfile = os.path.basename(packed_file)
|
||||
orig_filename = outfile[:-4]
|
||||
if os.path.isfile(outfile):
|
||||
print("deleting", orig_filename)
|
||||
os.remove(orig_filename)
|
||||
print("moving", outfile, "->", orig_filename)
|
||||
shutil.move(outfile, orig_filename)
|
||||
target_folder = os.path.join("extracted", os.path.basename(orig_filename))
|
||||
print("deleting", target_folder)
|
||||
shutil.rmtree(target_folder)
|
||||
if os.path.isdir("extracted"):
|
||||
input("Press enter to remove rest of extracted folder")
|
||||
shutil.rmtree("extracted")
|
||||
exit("Done!")
|
||||
|
||||
if not (options.unpack or options.repack):
|
||||
parser.print_help()
|
||||
exit()
|
||||
pstatus = ""
|
||||
if options.unpack:
|
||||
if os.path.isdir("extracted"):
|
||||
print("Removing extracted folder")
|
||||
shutil.rmtree("extracted")
|
||||
for packed_file in glob.glob(os.path.join(scrap_dir, "*.packed")):
|
||||
os.chdir(scrap_dir)
|
||||
BN = os.path.basename(packed_file)
|
||||
target_folder = os.path.join("extracted", os.path.basename(packed_file))
|
||||
os.makedirs(target_folder, exist_ok=True)
|
||||
os.chdir(target_folder)
|
||||
print("Unpacking {}".format(os.path.basename(packed_file)))
|
||||
with open(packed_file, "rb") as pkfile:
|
||||
data = PackedHeader.parse_stream(pkfile)
|
||||
print("Offset:", hex(pkfile.tell()))
|
||||
for file in tqdm(data.files, ascii=True):
|
||||
folder, filename = os.path.split(file.path)
|
||||
if folder:
|
||||
os.makedirs(folder, exist_ok=True)
|
||||
with open(file.path, "wb") as outfile:
|
||||
outfile.write(file.data())
|
||||
print("\r" + " " * len(pstatus) + "\r", end="", flush=True)
|
||||
os.chdir(scrap_dir)
|
||||
|
||||
if options.unpack and options.repack:
|
||||
input(
|
||||
"Press enter to rebuild *.packed files from folders in 'extracted' dir..."
|
||||
) # noqa
|
||||
pass
|
||||
|
||||
|
||||
def file_gen(files, offset=0):
|
||||
for real_path, size, path in files:
|
||||
file = dict(path=path, offset=offset, size=size)
|
||||
yield file
|
||||
offset += file["size"]
|
||||
|
||||
|
||||
def make_header(files, offset=0):
|
||||
files_list = list(file_gen(files, offset))
|
||||
return DummyHeader.build(dict(files=files_list))
|
||||
|
||||
|
||||
if options.repack:
|
||||
for folder in glob.glob(os.path.join(scrap_dir, "extracted", "*.packed")):
|
||||
data = []
|
||||
filename = os.path.join(scrap_dir, os.path.basename(folder))
|
||||
for root, folders, files in os.walk(folder):
|
||||
for file in sorted(files):
|
||||
file = os.path.join(root, file)
|
||||
rel_path = bytes(
|
||||
file.replace(folder, "").replace("\\", "/").lstrip("/"),
|
||||
"windows-1252",
|
||||
)
|
||||
size = os.stat(file).st_size
|
||||
data.append((file, size, rel_path))
|
||||
print("Found {} files for {}".format(len(data), filename))
|
||||
offset = len(make_header(data))
|
||||
print("Writing", filename)
|
||||
header = make_header(data, offset)
|
||||
with open(filename, "wb") as outfile:
|
||||
outfile.write(header)
|
||||
for file, size, rel_path in tqdm(data, ascii=True):
|
||||
outfile.write(open(file, "rb").read())
|
||||
print("Done!")
|
|
@ -8,10 +8,13 @@ import string
|
|||
import re
|
||||
from binascii import hexlify
|
||||
|
||||
|
||||
def gen():
|
||||
with open(sys.argv[1], "rb") as fh:
|
||||
size = os.stat(sys.argv[1]).st_size
|
||||
progbar = tqdm(total=size, unit="bytes", unit_scale=True, unit_divisor=1024)
|
||||
progbar = tqdm(
|
||||
total=size, unit="bytes", unit_scale=True, unit_divisor=1024, leave=False
|
||||
)
|
||||
pos = 0
|
||||
for entry in mp.Unpacker(fh, raw=True):
|
||||
progbar.update(fh.tell() - pos)
|
||||
|
@ -27,14 +30,10 @@ def gen():
|
|||
yield entry
|
||||
|
||||
|
||||
|
||||
def strdump(data):
|
||||
printable_chars = set(bytes(string.printable, "ascii")) - set(b"\n\r\t\x0b\x0c")
|
||||
return "".join(chr(c) if c in printable_chars else "." for c in data)
|
||||
|
||||
def tohex(data):
|
||||
return str(hexlify(data), "utf8").upper()
|
||||
|
||||
|
||||
# best=sorted(tqdm(gen(),ascii=True),key=lambda v:len(v['data']),reverse=True)
|
||||
|
||||
|
@ -53,16 +52,24 @@ def tohex(data):
|
|||
# entry['infos'][fmt]=[v[0] for v in struct.iter_unpack(fmt,data)]
|
||||
# return entry
|
||||
|
||||
filters=[re.compile(s) for s in sys.argv[2:]]
|
||||
|
||||
filters = sys.argv[2:]
|
||||
with open("all.log", "w") as of:
|
||||
for entry in gen():
|
||||
fm=[(f.match(entry['filename']) is not None) for f in filters]
|
||||
if filters and not any(fm):
|
||||
fm = any(
|
||||
all(s.lower() in entry["filename"].lower() for s in f.split("|"))
|
||||
for f in filters
|
||||
)
|
||||
if filters and not fm:
|
||||
continue
|
||||
is_magic = bytes(entry["block_id"], "utf8").ljust(4, b"\0") == entry["data"]
|
||||
entry["data_len"] = len(entry["data"])
|
||||
entry["str"] = strdump(entry["data"])
|
||||
entry["data"] = tohex(entry["data"])
|
||||
entry["data"] = entry["data"].hex().upper()
|
||||
if is_magic:
|
||||
print("#" * 50, file=of)
|
||||
print(
|
||||
"{timestamp} {block_id} {filename} {data_len:08X} {data} {str}".format(**entry), file=of
|
||||
"{timestamp} {block_id} {filename} {data_len:08X} {data} {str}".format(
|
||||
**entry
|
||||
),
|
||||
file=of,
|
||||
)
|
||||
|
|
|
@ -38,12 +38,12 @@ def seek_to(fh, offset, pos=None):
|
|||
yield
|
||||
fh.seek(pos)
|
||||
|
||||
def read_array(s,fh):
|
||||
ret=[]
|
||||
|
||||
def read_array(s, fh):
|
||||
ret = []
|
||||
count = read_struct("<I", fh)[0]
|
||||
size = struct.calcsize(s)
|
||||
for _ in range(count):
|
||||
ret.append(read_struct(s,fh))
|
||||
ret.append(read_struct(s, fh))
|
||||
return ret
|
||||
|
||||
|
||||
|
@ -93,7 +93,10 @@ class Parser:
|
|||
print("{}[{}] {} bytes".format(" " * self.depth, magic, len(data)))
|
||||
self.depth += 1
|
||||
fh = BytesIO(data)
|
||||
if len(data) != 0:
|
||||
ret = getattr(self, magic, lambda fh: self._default(magic, fh))(fh)
|
||||
else:
|
||||
ret = None
|
||||
pos = fh.tell()
|
||||
leftover = len(fh.read())
|
||||
fh.seek(pos)
|
||||
|
@ -198,17 +201,27 @@ class Parser:
|
|||
dum = {}
|
||||
dum["name"] = read_str(fh)
|
||||
dum["pos"] = read_struct("<fff", fh)
|
||||
dum["rot"] = read_struct("<fff", fh)
|
||||
dum["ang"] = read_struct("<fff", fh)
|
||||
dum["has_ini"] = read_struct("<I", fh)[0]
|
||||
if dum["has_ini"]:
|
||||
dum['ini']=self.parse_block(fh)
|
||||
dum["has_next"] = read_struct("<I", fh)[0]
|
||||
dum["ini"] = self.parse_block(fh)
|
||||
dum["unk_1"] = read_struct("<I", fh)[0]
|
||||
ret["dummies"].append(dum)
|
||||
assert fh.read() == b"", "Leftover Data"
|
||||
return ret
|
||||
|
||||
# def AMC(self, fh):
|
||||
# return len(fh.read())
|
||||
def AMC(self, fh):
|
||||
ret = {}
|
||||
ret["unk_1"] = read_struct("<I", fh)[0]
|
||||
ret["unk_2"] = read_struct("<I", fh)[0]
|
||||
ret["floats"] = read_struct("<16f", fh) # ???
|
||||
ret["cmsh_1"] = [self.parse_block(fh) for _ in range(2)]
|
||||
ret["num_submeshes"] = read_struct("<I", fh)[0]
|
||||
ret["cmsh_2"] = [self.parse_block(fh) for _ in range(ret["num_submeshes"])]
|
||||
return ret
|
||||
|
||||
def CMSH(self, fh):
|
||||
return {"ret": len(fh.read())}
|
||||
|
||||
# def EMI(self, fh):
|
||||
# return len(fh.read())
|
||||
|
@ -218,30 +231,14 @@ class Parser:
|
|||
|
||||
basedir = r"D:/Games/Deep Silver/Scrapland/extracted/Data.packed"
|
||||
|
||||
files = [
|
||||
r"Models/Chars/Dtritus/Dtritus.sm3",
|
||||
r"Models/Elements/AnilloEstructuraA/AnilloEstructuraA.SM3",
|
||||
r"models/elements/antenaa/antenaa.lod1.sm3",
|
||||
# r"models/elements/abshield/anm/loop.cm3",
|
||||
# r"levels/fake/map/map3d.amc",
|
||||
# r"levels/shipedit/map/map3d.dum",
|
||||
# r"levels/menu/map/map3d.emi",
|
||||
r"Models/Skies/Menu/Sky.SM3",
|
||||
r"Levels/Menu/Map/Map3D.SM3",
|
||||
r"Models/Elements/AnilloEstructuraD/AnilloEstructuraD.LOD1.SM3",
|
||||
# r"levels/menu/map/map3d.amc",
|
||||
# r"levels/menu/map/map3d.dum",
|
||||
# r"levels/menu/map/scenecamera/anm/loop.cm3",
|
||||
r"models/chars/boss/boss.sm3",
|
||||
# r"models/chars/boss/anm/boss_walk.cm3",
|
||||
]
|
||||
|
||||
filt = [s.lower() for s in sys.argv[1:]]
|
||||
|
||||
for root, folders, files in os.walk(basedir):
|
||||
for root, _, files in os.walk(basedir):
|
||||
for file in files:
|
||||
path = os.path.join(root, file).replace("\\","/")
|
||||
if not path.lower().endswith(".dum".lower()):
|
||||
path = os.path.join(root, file).replace("\\", "/")
|
||||
if not path.lower().endswith(".amc".lower()):
|
||||
continue
|
||||
if "menu" not in path.lower():
|
||||
continue
|
||||
print("Parsing", path)
|
||||
p = Parser(debug=True)
|
||||
|
@ -252,4 +249,3 @@ for root, folders, files in os.walk(basedir):
|
|||
break
|
||||
pprint(parsed, compact=False, indent=4)
|
||||
print("#" * 50)
|
||||
|
||||
|
|
21
tools/parse_save.py
Normal file
21
tools/parse_save.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
import sys
|
||||
from construct import *
|
||||
from pprint import pprint
|
||||
|
||||
ScrapSaveVar = Struct(
|
||||
"name" / PascalString(Int32ul, encoding="utf-8"),
|
||||
"data" / PascalString(Int32ul, encoding="utf-8"),
|
||||
)
|
||||
ScrapSave = "ScarpSaveGame" / Struct(
|
||||
"title" / PascalString(Int32ul, encoding="utf-8"),
|
||||
"id" / PascalString(Int32ul, encoding="utf-8"),
|
||||
"data" / PrefixedArray(Int32ul, ScrapSaveVar),
|
||||
Terminated,
|
||||
)
|
||||
with open(sys.argv[1], "rb") as sav_file:
|
||||
save = ScrapSave.parse_stream(sav_file)
|
||||
print("ID:", save.id)
|
||||
print("Title:", save.title)
|
||||
for var in save.data:
|
||||
print(" {}: {}".format(var.name, var.data))
|
||||
|
|
@ -3,29 +3,18 @@ from collections import OrderedDict
|
|||
import glob
|
||||
import os
|
||||
import shutil
|
||||
from construct import (
|
||||
Struct,
|
||||
PascalString,
|
||||
Int32ul,
|
||||
Lazy,
|
||||
Pointer,
|
||||
Bytes,
|
||||
this,
|
||||
PrefixedArray,
|
||||
Const,
|
||||
Debugger
|
||||
)
|
||||
from construct import *
|
||||
from tqdm import tqdm
|
||||
|
||||
setglobalstringencoding(None)
|
||||
|
||||
ScrapFile = Struct(
|
||||
"path" / PascalString(Int32ul, encoding="ascii"),
|
||||
"path" / PascalString(Int32ul),
|
||||
"size" / Int32ul,
|
||||
"offset" / Int32ul,
|
||||
"data" / Lazy(Pointer(this.offset, Bytes(this.size))),
|
||||
)
|
||||
DummyFile = Struct(
|
||||
"path" / PascalString(Int32ul, encoding="u8"), "size" / Int32ul, "offset" / Int32ul
|
||||
"data" / OnDemandPointer(this.offset, Bytes(this.size)),
|
||||
)
|
||||
DummyFile = Struct("path" / PascalString(Int32ul), "size" / Int32ul, "offset" / Int32ul)
|
||||
|
||||
PackedHeader = Struct(
|
||||
Const(b"BFPK"), Const(b"\0\0\0\0"), "files" / PrefixedArray(Int32ul, ScrapFile)
|
||||
|
|
Loading…
Reference in a new issue