ScrapHacks/NOTES.md
Daniel Seiller 7afdfb5869 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
2021-01-20 23:53:14 +01:00

11 KiB

Infos

  • Engine: ScrapEngine/Mercury Engine
  • 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 mutliplayer dedicated server mode (needs to be used with -server)
  • -server: start in multiplayer server mode

Ingame-Console (Ctrl+^ or right click on titlebar and select "switch console") (Handler@0x402190):

  • <Command>: Try to evaluate Command as Python expression
  • :<Var>: Get Game Engine Global Variable
  • :<Var> <Val>: Set Game Engine Global Variable
  • ?: Show all Global Variable
  • ?<String>: Show all Global Variable 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 (Scenegraph 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 scenegraph debugging console)
  • 0x8b18f4: pointer to Scenes Data (can be dumped using scenegraph debugging console)
  • 0x8b18f8: pointer to active Models Data (can be dumped using scenegraph debugging console)

Hash-function used in Hash-Tables

unsigned long hash(const unsigned char *s)
{
    unsigned long h = 0, high;
    while ( *s )
    {
        h = ( h << 4 ) + *s++;
        if ( high = h & 0xF0000000 )
            h ^= high >> 24;
        h &= ~high;
    }
    return h;
}

Other Functions:

Check config.yml for full list

File Index struct @ 0x7fcbec

struct FileEntry {
  uint32_t offset;
  uint32_t size;
  uint32_t unk; // seems to always be 0xBADFOO1
  unsigned char* path;
  FileEntry* next; // next entry in hashtable chain
}

struct FileIDX {
  uint32_t size;
  FileEntry** entries;
};

Packed Index struct (array of 0x80 entries @ 0x7fc1b0)

struct PackedIDX {
  void** VMT;
  unsigned char* filename;
  uint32_t locked; // not sure
  void* data;
  uint32_t seek;
}

C(++)-Callbacks @ 0x7fac84

Structure:

struct CPP_Callback {
  const char* name;
  void* func;
  CPP_Callback* left;
  CPP_Callback* right;
}

Game engine Variables Hashtable @ 0x7fbe50

Game engine Variables @ 0x7fbe4c

Structure:

struct GameVar {
  GameVar* next;
  const char* name;
  const char* desc;
  uint8_t subtype;
  uint8_t type;
  uint16_t unk;
  void* value;
  void* def_value;
}

Types

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
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* ???

Entity Hash Table

Hash-function used: PJW (Same parameters as the example implementation)

Entry format:

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

Netplay protocol (WIP)

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)

Notes

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

Virtual Method Tables:

check r2_analyze.py for full list

Loading Custom Content (not really working)

  1. Create a folder mods
  2. Drop a *.packed file into it
  3. Change Scrap.cfg as follows
    1. Add ModPathName = mods
    2. Add ModFileName = <filename>

Interesting file inside Data.packed

  • m3d.ini: Rendering Engine Configuration
  • scripts/: Game Engine Scripts

How to enable External Console:

  1. Right click on the title bar (in windowed mode) and click "Switch Console"

How to enable Scenegraph debugging console

  1. extract Data.packed
  2. in m3d.ini uncomment (remove ;) ConsolaWnd (GUI Console) and/or ConsolaTxt (Text Console) and set the value to SI
  3. repack Data.packed

Misc. Interesting things

  • sys.path contains "./lib" so you can import your own Python Modules
  • Games crashes when starting a multiplayer server and feeding it random UDP data

Code Snippets

Kaitai Struct Parser for .packed files

meta:
  id: packed
  application: Scrapland
  file-extension: packed
  endian: le
  xref: http://wiki.xentax.com/index.php/Scrapland_PACKED
  license: MIT
  encoding: UTF-8
seq:
  - id: magic
    contents: BFPK
    doc: File Magic
  - id: version
    contents: [0,0,0,0]
    doc: File Version
  - id: num_files
    type: u4
    doc: Number of files
  - id: files
    type: file_entry
    repeat: expr
    repeat-expr: num_files
    doc: Entry for each file
types:
  file_entry:
    seq:
      - id: path_len
        type: u4
        doc: Length of file path
      - id: path
        type: str
        size: path_len
        doc: File path
      - id: size
        type: u4
        doc: File size
      - id: offset
        type: u4
        doc: Absoulte File offset
    instances:
      data:
        pos: offset
        size: size

TODO:

  • Figure out how C++ Callbacks work
  • Figure out SM3 (Models), CM3 (Animations) file formats
  • Figure out rest of World structure
  • Figure out rest of Entity structure