├── .gitignore ├── rrhispec └── .gitignore ├── LICENSE ├── README.md ├── map.rim.md ├── ridge.exe.md └── map.rim.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /rrhispec/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RR HISPEC 60FPS 2 | 3 | ## Table of contents 4 | 5 | 1. [MAP.RIM](map.rim.md) 6 | 2. [RIDGE.EXE](ridge.exe.md) 7 | 8 | ## Usage 9 | 10 | 1. Download an image of `Ridge Racer Hi-Spec Demo (Europe)` from somewhere 11 | 2. Run `bchunk -w Ridge Racer Hi-Spec Demo (Europe).{bin,cue} rrhispec` to extract the .ISO in case your image is in .BIN .CUE 12 | 3. Open the .ISO in any file archiver and extract the contents of `RR30` in `rrhispec` folder of this repo, then do the same with `RR60` BUT replace every file! 13 | 14 | Now you can run any Python 3 script from this repo! 15 | 16 | ## Available Python scripts 17 | 18 | 1. [map.rim.py](map.rim.py) - MAP.RIM viewer 19 | -------------------------------------------------------------------------------- /map.rim.md: -------------------------------------------------------------------------------- 1 | # MAP.RIM 2 | 3 | ## Description 4 | 5 | This file contains the map and probably some other things. 6 | 7 | ## Structure 8 | 9 | Please note that even though MIPS I is *bi-endian*, PSX is *little endian*. 10 | 11 | |Offset|Size (bytes)|Name|Description| 12 | |------|----|----|-----------| 13 | |`0x0`|`0x800`|`padding`|Padding. Initially it's *mostly* filled with `0xFF`.| 14 | |`0x800`|`4`|`itemCount`|Amount of items| 15 | |`0x804`|`itemCount * 4`|`itemOffsets`|Offsets relative to `0x800`. In function `0x80017540` each item's relative offset to memory is turned into absolute by adding the pointer to the `MAP.RIM` file + `0x800`.| 16 | |`+ items * 4`|`items * 56`|`items`|Items themselves!| 17 | 18 | 19 | ### Item types 20 | 21 | #### Shared (unless said otherwise) 22 | 23 | |Offset|Size (bytes)|Name|Description| 24 | |------|------------|----|-----------| 25 | |`0x0`|`4`|id|Index of a function from the function table @ `0x8007ED24`. Only first two bytes are actually used.| 26 | 27 | Sizes do not include the shared fields. 28 | 29 | #### Quad 30 | 31 | **ID:** `0x00010000` (`00 00 01 00`) 32 | 33 | **Size:** 52 bytes 34 | 35 | **Note:** VXY\* registers contain two signed 16-bit values, while VZ\* registers takes a signed 16-bit value sign extended to 32 bits. 36 | 37 | |Offset|Size (bytes)|Name|Description| 38 | |------|------------|----|-----------| 39 | |`0x4`|`4`|XY0|Loaded into VXY0 data register of GTE.| 40 | |`0x8`|`4`|Z0|Loaded into VZ0 data register of GTE.| 41 | |`0xC`|`4`|XY1|Loaded into VXY1 data register of GTE.| 42 | |`0x10`|`4`|Z1|Loaded into VZ1 data register of GTE.| 43 | |`0x14`|`4`|XY2|Loaded into VXY2 data register of GTE.| 44 | |`0x18`|`4`|Z2|Loaded into VZ2 data register of GTE.| 45 | |`0x1C`|`4`|XY3|Loaded into VXY0 data register of GTE.| 46 | |`0x20`|`4`|Z3|Loaded into VZ0 data register of GTE.| 47 | |`0x24`|-|-|Unknown.| 48 | |`0x34`|`4`|unknown1|Possibly indicates that there is a next block.| 49 | -------------------------------------------------------------------------------- /ridge.exe.md: -------------------------------------------------------------------------------- 1 | # RIDGE.EXE 2 | 3 | ## Description 4 | 5 | The game executable itself. 6 | 7 | ## Memory addresses 8 | 9 | |Address (hex)|Data type|Description|Values|Notes| 10 | |-------------|---------|-----------|------|-----| 11 | |`8007b5cc`|pointers[54]|Array of states (see [Game states](#game-states))|Valid function pointers|| 12 | |`8007be08`|char*[10]|Names of music tracks|RANDOM PLAY, RIDGE RACER etc|| 13 | |`80085d68`|short|Race track selection|1 up to 4|| 14 | |`80085d84`|ushort|Timer|Time in frames until the game is over|Timer in seconds = Timer / Framerate (50Hz for PAL, 60Hz for NTSC)| 15 | |`80085dd4`|uint|Music Timer|Time until the next music track. Used for drawing the current music name|The track name is displayed for 600 frames(?)| 16 | |`80085e0c`|short|Music selection|0 - Random, 1 - Ridge Racer, 2 - Grip|Any value above 3 crashes the console immediately!| 17 | |`80085e0e`|short|Game state|See [Game states](#game-states)|Checked each frame, used as index for `8007b5cc`| 18 | |`80085e49`|short|Total racers|1 or more|The position of player is not drawn if only one racer is present| 19 | |`80085ec8`|short|Throttle|`[0; 256]`|Can be increased with X and decreased with Square, also decays with time| 20 | |`80085ef4`|short|Unknown (behaves like throttle)|0 on start, `[15; 249]`|TODO:| 21 | |`80085f00`|ushort|RPM|`[500; 10000]`|| 22 | |`80086200`|short|Transmission|0 - Automatic, 1 - Manual|Can be changed while racing| 23 | |`80086202`|short|Current Gear|1 to 6|| 24 | |`8008621c`|short|Speed|Any. Speed in KM/H = Speed * 160 / 2336|Negative speed works too!| 25 | |`80086248`|short|Place|1 or 2 since there is only one opponent in this version|| 26 | 27 | ### Game states 28 | 29 | |# (hex)|Description| 30 | |-------|-----------| 31 | |`00`|Transition to the Gameplay| 32 | |`01`|Gameplay or transition to Demo step 1 (used by Music player step 2)| 33 | |`02`|Transition to Main menu| 34 | |`03`|Main menu, waving flag| 35 | |`04`|Transition to Demo| 36 | |`05`|Demo| 37 | |`06`|Transition to Select screen| 38 | |`07`|Select screen| 39 | |`08`|Transition to Other demo or Replay| 40 | |`09`|Other demo or Replay| 41 | |`0a`|Undefined (the game stops)| 42 | |`0b`|Undefined (blue screen)| 43 | |`0c`|The same waving flag but eventually jumps to `1e`| 44 | |`0d`|Transition to Name entry| 45 | |`0e`|Name entry| 46 | |`0f`|Transition to Game Over| 47 | |`10`|Game Over| 48 | |`11`|Transition to Options| 49 | |`12`|Options| 50 | |`13`|Transition to Button config| 51 | |`14`|Options -> Button config| 52 | |`15`|Transition to Records| 53 | |`16`|Options -> Records| 54 | |`17`|Transition to Adjust Screen| 55 | |`18`|Options -> Adjust Screen| 56 | |`19`|Load demo assets (Demo step 2) (used by Music player step 1)| 57 | |`1a`|Demo step 3 (or Options -> Music player)| 58 | |`1b`|Transition to Game Option| 59 | |`1c`|Options -> Game Option| 60 | |`1d`|??? (jumps to Best scores)| 61 | |`1e`|Best scores| 62 | |`1f`|Transition to Result| 63 | |`20`|Result| 64 | |`21`|Transition to Memory card| 65 | |`22`|Options -> Memory card| 66 | |`23`|Transition to Memory Card -> Load| 67 | |`24`|Options -> Memory card -> Load| 68 | |`25`|Transition to Memory Card -> Save| 69 | |`26`|Options -> Memory card -> Save| 70 | |`27`|??? (jumps to `28`)| 71 | |`28` - `2a`|??? (jumps to Options)| 72 | |`2b`|??? (jumps to `2c`)| 73 | |`2c` - `2f`|??? (jumps to Options)| 74 | |`30`|Transition to Credits| 75 | |`31`|Credits| 76 | |`32`|Transition to Information| 77 | |`33`|Information| 78 | |`34`|Transition to other executable (NAMCO Catalogue or Original Mode)| 79 | |`35`|Now loading! (the last safe game state)| 80 | |`36`|??? (jumps to `08`)| 81 | |`37` and onwards|Crashes the system because the index is out of bounds| 82 | -------------------------------------------------------------------------------- /map.rim.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import argparse 4 | import os 5 | import struct 6 | 7 | def getMapRim(): 8 | f = open('rrhispec/MAP.RIM', 'rb') 9 | f.read(0x800) # TODO: need to figure out what are those for... 10 | return f 11 | 12 | def read_int(map): 13 | b = map.read(4) 14 | return int.from_bytes(b, byteorder='little') 15 | 16 | def read_file(map, position): 17 | map.seek(position) 18 | id = read_int(map) 19 | match id: 20 | case 0x00010000: 21 | print('DEBUG: Quad') 22 | print(read_quad(map)) 23 | case _: 24 | padding = 10 25 | print(f'Unknown ID {id:#0{padding}x}, ignoring') 26 | 27 | def read_quad(map): 28 | return struct.unpack('