node-rcheevos 0.5.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,77 +1,77 @@
1
1
  # node-rcheevos
2
2
 
3
- > **⚠️ Work in Progress** - This works and I'm using it in ROMie, but it's early. Only macOS ARM64 binaries are pre-built right now. Other platforms will compile from source (need build tools) until I set up CI for cross-platform builds.
3
+ [![npm version](https://badge.fury.io/js/node-rcheevos.svg)](https://www.npmjs.com/package/node-rcheevos)
4
4
 
5
- Generate RetroAchievements hashes for ROMs in Node.js. Uses the official [rcheevos](https://github.com/RetroAchievements/rcheevos) C library, so you get the same hashes as RetroArch and other emulators.
5
+ Generate RetroAchievements hashes for ROMs in Node.js. Uses the same [rcheevos](https://github.com/RetroAchievements/rcheevos) C library that RetroArch uses, so your hashes will match exactly.
6
6
 
7
- Built this for better RetroAchievements support in [ROMie](https://github.com/JZimz/romie), but it works anywhere you need to hash ROMs in Node.
7
+ ## Why not just reimplement the hashing in JavaScript?
8
8
 
9
- ## Why this exists
9
+ I tried that first. Each console has its own special hashing rules - NES strips the iNES header, SNES might have a 512-byte header to skip, PlayStation has to parse `SYSTEM.CNF` to find which executable to hash, N64 needs byte-order conversion depending on the ROM format. The [RetroAchievements docs](https://docs.retroachievements.org/developer-docs/game-identification.html) explain all this, but keeping JavaScript implementations updated for 40+ systems when RA changes their logic is a pain. Easier to just wrap their C library directly.
10
10
 
11
- If you've tried adding RetroAchievements to an Electron app, you've probably hit this: WASM libraries don't work in the main process. And reimplementing the hashing logic in JavaScript is a recipe for subtle bugs when the algorithm changes.
12
-
13
- This wraps the official C library as a native Node addon, so it just works. Same hashing logic as the source of truth, supports all the systems RetroAchievements does (Game Boy, NES, SNES, PlayStation, PSP, you name it).
14
-
15
- ## Install
11
+ Built this for [ROMie](https://github.com/JZimz/romie) but figured it's useful standalone.
16
12
 
13
+ ## Installation
17
14
  ```bash
18
15
  npm install node-rcheevos
19
16
  ```
20
17
 
21
- Comes with pre-built binaries for macOS, Windows, and Linux (both x64 and ARM64). If you're on something else, it'll build from source—just need the usual build tools (node-gyp stuff).
22
-
23
- ## How to use it
18
+ Includes pre-built binaries for macOS, Windows, and Linux (both x64 and ARM64). If you're on something else, it'll build from source automatically.
24
19
 
20
+ ## Quick Start
25
21
  ```javascript
26
- const { rhash } = require('node-rcheevos');
22
+ const { rhash, ConsoleId } = require('node-rcheevos');
27
23
 
28
- const md5 = rhash(4, '/path/to/game.gb'); // Game Boy
29
- console.log(md5); // "a1b2c3d4e5f6..."
24
+ const md5 = rhash(ConsoleId.GAMEBOY, '/path/to/pokemon-red.gb');
25
+ console.log(md5); // "bb7df04e1b0a2570657527a7e108ae23"
30
26
  ```
31
27
 
32
- TypeScript works too:
28
+ ## API
33
29
 
34
- ```typescript
35
- import { rhash } from 'node-rcheevos';
30
+ ### `rhash(consoleId, path, buffer?)`
36
31
 
37
- const md5 = rhash(41, '/path/to/game.iso'); // PSP
38
- ```
32
+ **Parameters:**
33
+ - `consoleId` (number): RetroAchievements console ID (use `ConsoleId` constants or numeric values)
34
+ - `path` (string): Path to your ROM file
35
+ - `buffer` (Buffer, optional): ROM data if you already have it in memory
39
36
 
40
- CLI if you want to test it quick:
37
+ **Returns:** MD5 hash as a lowercase hex string
41
38
 
42
- ```bash
43
- npx rhash -c 4 /path/to/game.gb
44
- ```
39
+ **Throws:** Error if the file doesn't exist, can't be read, or the console ID is invalid
45
40
 
46
- ## API
41
+ ### `ConsoleId`
47
42
 
48
- Just one function: `rhash(consoleId, path, buffer?)`
43
+ Exported constants for all console IDs if you prefer named constants over numbers.
49
44
 
50
- Pass it a RetroAchievements console ID (see table below) and the path to your ROM. Returns the MD5 hash as a string. Throws an error if the file doesn't exist or can't be hashed.
45
+ ```javascript
46
+ const { ConsoleId } = require('node-rcheevos');
51
47
 
52
- ## Console IDs
48
+ console.log(ConsoleId.GAMEBOY); // 4
49
+ console.log(ConsoleId.PLAYSTATION); // 12
50
+ console.log(ConsoleId.PSP); // 41
51
+ ```
53
52
 
54
- Here are the common ones (full list at [docs.retroachievements.org](https://docs.retroachievements.org/Console-IDs/)):
53
+ ### Buffer limitations
55
54
 
56
- | ID | System |
57
- |----|--------|
58
- | 1 | Genesis/Mega Drive |
59
- | 2 | Nintendo 64 |
60
- | 3 | Super Nintendo |
61
- | 4 | Game Boy |
62
- | 5 | Game Boy Advance |
63
- | 6 | Game Boy Color |
64
- | 7 | NES/Famicom |
65
- | 11 | Game Gear |
66
- | 12 | PlayStation |
67
- | 27 | PlayStation Portable |
68
- | 38 | Nintendo 3DS |
69
- | 39 | Dreamcast |
55
+ **Works with buffers** (cartridge-based systems like GB, GBA, NES, SNES):
56
+ ```javascript
57
+ const buffer = fs.readFileSync('/path/to/game.gb');
58
+ const md5 = rhash(ConsoleId.GAMEBOY, '/path/to/game.gb', buffer);
59
+ ```
70
60
 
71
- ## Building from source
61
+ **Doesn't work with buffers** (disc-based like PlayStation, PSP, and arcade systems):
62
+ ```javascript
63
+ // Passing a buffer will throw an error - must use file path
64
+ const md5 = rhash(ConsoleId.PLAYSTATION, '/path/to/game.bin');
65
+ ```
72
66
 
73
- If you want to hack on it:
67
+ Disc-based systems need to read specific sectors from the image file, and arcade systems hash the filename, so they can't work with in-memory buffers.
68
+
69
+ ## CLI Usage
70
+ ```bash
71
+ npx rhash -c 4 /path/to/game.gb
72
+ ```
74
73
 
74
+ ## Building from Source
75
75
  ```bash
76
76
  git clone --recursive https://github.com/jzimz/node-rcheevos.git
77
77
  cd node-rcheevos
@@ -79,8 +79,8 @@ npm install
79
79
  npm run build
80
80
  ```
81
81
 
82
- The `--recursive` flag pulls in the rcheevos submodule. Without it, you won't have the C library to build against.
82
+ The `--recursive` flag is important - it pulls in the rcheevos library. Without it, you won't have anything to build against.
83
83
 
84
84
  ## License
85
85
 
86
- MIT. The rcheevos library this wraps is also MIT.
86
+ MIT. The rcheevos library is also MIT.
package/lib/index.d.ts CHANGED
@@ -1,7 +1,112 @@
1
+ /**
2
+ * RetroAchievements console identifiers.
3
+ * Use these constants instead of magic numbers for better readability.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { rhash, ConsoleId } from 'node-rcheevos';
8
+ *
9
+ * const md5 = rhash(ConsoleId.GAMEBOY, '/path/to/game.gb');
10
+ * ```
11
+ */
12
+ export const ConsoleId: {
13
+ readonly UNKNOWN: 0;
14
+ readonly MEGA_DRIVE: 1;
15
+ readonly NINTENDO_64: 2;
16
+ readonly SUPER_NINTENDO: 3;
17
+ readonly GAMEBOY: 4;
18
+ readonly GAMEBOY_ADVANCE: 5;
19
+ readonly GAMEBOY_COLOR: 6;
20
+ readonly NINTENDO: 7;
21
+ readonly PC_ENGINE: 8;
22
+ readonly SEGA_CD: 9;
23
+ readonly SEGA_32X: 10;
24
+ readonly MASTER_SYSTEM: 11;
25
+ readonly PLAYSTATION: 12;
26
+ readonly ATARI_LYNX: 13;
27
+ readonly NEOGEO_POCKET: 14;
28
+ readonly GAME_GEAR: 15;
29
+ readonly GAMECUBE: 16;
30
+ readonly ATARI_JAGUAR: 17;
31
+ readonly NINTENDO_DS: 18;
32
+ readonly WII: 19;
33
+ readonly WII_U: 20;
34
+ readonly PLAYSTATION_2: 21;
35
+ readonly XBOX: 22;
36
+ readonly MAGNAVOX_ODYSSEY2: 23;
37
+ readonly POKEMON_MINI: 24;
38
+ readonly ATARI_2600: 25;
39
+ readonly MS_DOS: 26;
40
+ readonly ARCADE: 27;
41
+ readonly VIRTUAL_BOY: 28;
42
+ readonly MSX: 29;
43
+ readonly COMMODORE_64: 30;
44
+ readonly ZX81: 31;
45
+ readonly ORIC: 32;
46
+ readonly SG1000: 33;
47
+ readonly VIC20: 34;
48
+ readonly AMIGA: 35;
49
+ readonly ATARI_ST: 36;
50
+ readonly AMSTRAD_PC: 37;
51
+ readonly APPLE_II: 38;
52
+ readonly SATURN: 39;
53
+ readonly DREAMCAST: 40;
54
+ readonly PSP: 41;
55
+ readonly CDI: 42;
56
+ readonly THREEDO: 43;
57
+ readonly COLECOVISION: 44;
58
+ readonly INTELLIVISION: 45;
59
+ readonly VECTREX: 46;
60
+ readonly PC8800: 47;
61
+ readonly PC9800: 48;
62
+ readonly PCFX: 49;
63
+ readonly ATARI_5200: 50;
64
+ readonly ATARI_7800: 51;
65
+ readonly X68K: 52;
66
+ readonly WONDERSWAN: 53;
67
+ readonly CASSETTEVISION: 54;
68
+ readonly SUPER_CASSETTEVISION: 55;
69
+ readonly NEO_GEO_CD: 56;
70
+ readonly FAIRCHILD_CHANNEL_F: 57;
71
+ readonly FM_TOWNS: 58;
72
+ readonly ZX_SPECTRUM: 59;
73
+ readonly GAME_AND_WATCH: 60;
74
+ readonly NOKIA_NGAGE: 61;
75
+ readonly NINTENDO_3DS: 62;
76
+ readonly SUPERVISION: 63;
77
+ readonly SHARPX1: 64;
78
+ readonly TIC80: 65;
79
+ readonly THOMSONTO8: 66;
80
+ readonly PC6000: 67;
81
+ readonly PICO: 68;
82
+ readonly MEGADUCK: 69;
83
+ readonly ZEEBO: 70;
84
+ readonly ARDUBOY: 71;
85
+ readonly WASM4: 72;
86
+ readonly ARCADIA_2001: 73;
87
+ readonly INTERTON_VC_4000: 74;
88
+ readonly ELEKTOR_TV_GAMES_COMPUTER: 75;
89
+ readonly PC_ENGINE_CD: 76;
90
+ readonly ATARI_JAGUAR_CD: 77;
91
+ readonly NINTENDO_DSI: 78;
92
+ readonly TI83: 79;
93
+ readonly UZEBOX: 80;
94
+ readonly FAMICOM_DISK_SYSTEM: 81;
95
+ readonly HUBS: 100;
96
+ readonly EVENTS: 101;
97
+ readonly STANDALONE: 102;
98
+ };
99
+
100
+ /**
101
+ * Valid console ID values for the rhash function.
102
+ * This is the union of all values in the ConsoleId object.
103
+ */
104
+ export type ConsoleIdValue = typeof ConsoleId[keyof typeof ConsoleId];
105
+
1
106
  /**
2
107
  * Generate a hash for a ROM file using the RetroAchievements algorithm.
3
108
  *
4
- * @param consoleId - The RetroAchievements console ID (e.g., 41 for PSP)
109
+ * @param consoleId - The RetroAchievements console ID
5
110
  * @param path - Path to the ROM file (required even when using buffer, for file extension detection)
6
111
  * @param buffer - Optional buffer containing ROM data. If provided, hashes from memory instead of reading file
7
112
  * @returns MD5 hash as a 32-character hex string
@@ -9,15 +114,15 @@
9
114
  *
10
115
  * @example
11
116
  * ```typescript
12
- * import { rhash } from 'node-rcheevos';
117
+ * import { rhash, ConsoleId } from 'node-rcheevos';
13
118
  * import { readFileSync } from 'fs';
14
119
  *
15
- * // Hash from file
16
- * const md5 = rhash(41, '/path/to/game.iso');
120
+ * // Hash from file using ConsoleId constant or use literal numbers
121
+ * const md5 = rhash(ConsoleId.PSP, '/path/to/game.iso');
17
122
  *
18
123
  * // Hash from buffer (useful if already in memory)
19
124
  * const buffer = readFileSync('/path/to/game.iso');
20
- * const md5 = rhash(41, '/path/to/game.iso', buffer);
125
+ * const md5 = rhash(ConsoleId.PSP, '/path/to/game.iso', buffer);
21
126
  * ```
22
127
  */
23
- export function rhash(consoleId: number, path: string, buffer?: Buffer): string;
128
+ export function rhash(consoleId: ConsoleIdValue, path: string, buffer?: Buffer): string;
package/lib/index.js CHANGED
@@ -1,5 +1,94 @@
1
1
  const addon = require('node-gyp-build')(__dirname + '/..');
2
2
 
3
+ const ConsoleId = {
4
+ UNKNOWN: 0,
5
+ MEGA_DRIVE: 1,
6
+ NINTENDO_64: 2,
7
+ SUPER_NINTENDO: 3,
8
+ GAMEBOY: 4,
9
+ GAMEBOY_ADVANCE: 5,
10
+ GAMEBOY_COLOR: 6,
11
+ NINTENDO: 7,
12
+ PC_ENGINE: 8,
13
+ SEGA_CD: 9,
14
+ SEGA_32X: 10,
15
+ MASTER_SYSTEM: 11,
16
+ PLAYSTATION: 12,
17
+ ATARI_LYNX: 13,
18
+ NEOGEO_POCKET: 14,
19
+ GAME_GEAR: 15,
20
+ GAMECUBE: 16,
21
+ ATARI_JAGUAR: 17,
22
+ NINTENDO_DS: 18,
23
+ WII: 19,
24
+ WII_U: 20,
25
+ PLAYSTATION_2: 21,
26
+ XBOX: 22,
27
+ MAGNAVOX_ODYSSEY2: 23,
28
+ POKEMON_MINI: 24,
29
+ ATARI_2600: 25,
30
+ MS_DOS: 26,
31
+ ARCADE: 27,
32
+ VIRTUAL_BOY: 28,
33
+ MSX: 29,
34
+ COMMODORE_64: 30,
35
+ ZX81: 31,
36
+ ORIC: 32,
37
+ SG1000: 33,
38
+ VIC20: 34,
39
+ AMIGA: 35,
40
+ ATARI_ST: 36,
41
+ AMSTRAD_PC: 37,
42
+ APPLE_II: 38,
43
+ SATURN: 39,
44
+ DREAMCAST: 40,
45
+ PSP: 41,
46
+ CDI: 42,
47
+ THREEDO: 43,
48
+ COLECOVISION: 44,
49
+ INTELLIVISION: 45,
50
+ VECTREX: 46,
51
+ PC8800: 47,
52
+ PC9800: 48,
53
+ PCFX: 49,
54
+ ATARI_5200: 50,
55
+ ATARI_7800: 51,
56
+ X68K: 52,
57
+ WONDERSWAN: 53,
58
+ CASSETTEVISION: 54,
59
+ SUPER_CASSETTEVISION: 55,
60
+ NEO_GEO_CD: 56,
61
+ FAIRCHILD_CHANNEL_F: 57,
62
+ FM_TOWNS: 58,
63
+ ZX_SPECTRUM: 59,
64
+ GAME_AND_WATCH: 60,
65
+ NOKIA_NGAGE: 61,
66
+ NINTENDO_3DS: 62,
67
+ SUPERVISION: 63,
68
+ SHARPX1: 64,
69
+ TIC80: 65,
70
+ THOMSONTO8: 66,
71
+ PC6000: 67,
72
+ PICO: 68,
73
+ MEGADUCK: 69,
74
+ ZEEBO: 70,
75
+ ARDUBOY: 71,
76
+ WASM4: 72,
77
+ ARCADIA_2001: 73,
78
+ INTERTON_VC_4000: 74,
79
+ ELEKTOR_TV_GAMES_COMPUTER: 75,
80
+ PC_ENGINE_CD: 76,
81
+ ATARI_JAGUAR_CD: 77,
82
+ NINTENDO_DSI: 78,
83
+ TI83: 79,
84
+ UZEBOX: 80,
85
+ FAMICOM_DISK_SYSTEM: 81,
86
+ HUBS: 100,
87
+ EVENTS: 101,
88
+ STANDALONE: 102
89
+ };
90
+
3
91
  module.exports = {
4
- rhash: addon.rhash
92
+ rhash: addon.rhash,
93
+ ConsoleId
5
94
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-rcheevos",
3
- "version": "0.5.0",
3
+ "version": "1.0.0",
4
4
  "description": "Node.js native bindings for RetroAchievements rcheevos library (hash generation)",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",