Git Product home page Git Product logo

romjudge's Introduction

romjudge

A utility to judge an N64 ROM for correctness, for Windows and Linux.

Windows users should download the pre-built EXE: romjudge.exe

Usage:

usage: romjudge [-c ipl] [-r region] -f romfile
  -c will force an IPL and rewrite checksums.
  -r will overwrite the region letter.

romjudge will indicate if an N64 ROM has:

  • the correct byte order,
  • typical PI timings,
  • a sane file size,
  • the correct CRCs, and
  • a recognized IPL3/bootcode.

Written in C. X11 license. sha1.c and sha1.h are public domain.

romjudge's People

Contributors

jkbenaim avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

romjudge's Issues

[Feature request] Add the ability to run romjudge on directories and output to a text file

Hello! First of all, thanks so much for romjudge! I use it every day. 😄

I'd like the ability to run romjudge on a directory for verification and be able to output text files with the resulting data.

Outputting everything to a text file would also be nice for comparisons and mass verification of entire libraries. If you run it in a directory with many different ROMs, the buffer in the command prompt or PowerShell (I'm on Windows primarily) will eventually run out and you can't retrieve the discarded data. So outputting to a text file gets around this, and is also useful for other reasons. 😄

I think there is a way to pipe outputted data to a file in PowerShell, but in my experience it's flaky and doesn't work the best.

An example output using a current project verifying Mario Golf dumps:

Mario Golf (U) [!].z64
Product code:           NMFE0
Entry point:            [GOOD] 80025c00
Byte order grade:       [GOOD] 1234
PI timings grade:       [GOOD] 80371240
File size grade:        [GOOD] 0x1800000 bytes
CRC grade:              [GOOD] 664ba3d4 678a80b7
IPL3 grade:             [GOOD] 6102 or 7101

Mario Golf (E) [!].z64
Product code:           NMFP0
Entry point:            [GOOD] 80025c00
Byte order grade:       [GOOD] 1234
PI timings grade:       [GOOD] 80371240
File size grade:        [GOOD] 0x1800000 bytes
CRC grade:              [GOOD] 62e957d0 7fc15a5d
IPL3 grade:             [GOOD] 6102 or 7101

Mario Golf 64 (J) (V1.0) [!].z64
Product code:           NMFJ0
Entry point:            [GOOD] 80025c00
Byte order grade:       [GOOD] 1234
PI timings grade:       [GOOD] 80371240
File size grade:        [GOOD] 0x1800000 bytes
CRC grade:              [GOOD] d48944d1 b0d93a0e
IPL3 grade:             [GOOD] 6102 or 7101

Mario Golf 64 (J) (V1.1).z64
Product code:           NMFJ1
Entry point:            [GOOD] 80025c00
Byte order grade:       [GOOD] 1234
PI timings grade:       [GOOD] 80371240
File size grade:        [GOOD] 0x2000000 bytes
CRC grade:              [GOOD] 734f816b c6a6eb67
IPL3 grade:             [GOOD] 6102 or 7101

It could look something like that in the resulting text file.

Thanks for reading. 😄

GCC10's analyzer reports leaks and a NULL deref

[jason@moocow romjudge]$ make
cc -Wall -Og -ggdb -std=c99 -fanalyzer -c -o romjudge.o romjudge.c
cc -Wall -Og -ggdb -std=c99 -fanalyzer -c -o grader.o grader.c
grader.c: In function ‘perm_iterator_init’:
grader.c:128:5: warning: leak of ‘’ [CWE-401] [-Wanalyzer-malloc-leak]
128 | if (!p->order || !p->direction)
| ^
‘grade’: events 1-2
|
| 472 | void grade(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~
| | |
| | (1) entry to ‘grade’
| 473 | {
| 474 | grade_size(rg, rom, len);
| | ~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (2) calling ‘grade_size’ from ‘grade’
|
+--> ‘grade_size’: events 3-5
|
| 269 | void grade_size(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~~~~~~
| | |
| | (3) entry to ‘grade_size’
|......
| 272 | if (rg->fileSize < 0x101000) {
| | ~
| | |
| | (4) following ‘false’ branch...
|......
| 276 | switch (rg->fileSize) {
| | ~~~~~~
| | |
| | (5) ...to here
|
<------+
|
‘grade’: events 6-9
|
| 474 | grade_size(rg, rom, len);
| | ^~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (6) returning to ‘grade’ from ‘grade_size’
| 475 | if (rg->fileSizeGrade == GRADE_ERROR)
| | ~
| | |
| | (7) following ‘false’ branch...
|......
| 478 | grade_byte_order(rg, rom, len); // also grades ipl3
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (8) ...to here
| | (9) calling ‘grade_byte_order’ from ‘grade’
|
+--> ‘grade_byte_order’: events 10-11
|
| 296 | void grade_byte_order(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~~~~~~~~~~~~
| | |
| | (10) entry to ‘grade_byte_order’
|......
| 302 | perm_iterator_init(&p, 4);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (11) calling ‘perm_iterator_init’ from ‘grade_byte_order’
|
+--> ‘perm_iterator_init’: events 12-16
|
| 119 | int perm_iterator_init(struct perm_iterator *p, int n)
| | ^~~~~~~~~~~~~~~~~~
| | |
| | (12) entry to ‘perm_iterator_init’
| 120 | {
| 121 | if (n < 1)
| | ~
| | |
| | (13) following ‘false’ branch (when ‘n > 0’)...
|......
| 124 | p->num_elements = n;
| | ~
| | |
| | (14) ...to here
| 125 | p->order = calloc(n, sizeof(int));
| 126 | p->direction = calloc(n, sizeof(char));
| | ~
| | |
| | (15) allocated here
| 127 |
| 128 | if (!p->order || !p->direction)
| | ~
| | |
| | (16) following ‘true’ branch...
|
‘perm_iterator_init’: event 17
|
|cc1:
| (17): ...to here
|
‘perm_iterator_init’: event 18
|
| 128 | if (!p->order || !p->direction)
| | ^
| | |
| | (18) ‘’ leaks here; was allocated at (15)
|
grader.c: In function ‘grade_byte_order’:
grader.c:307:26: warning: dereference of NULL ‘p.order’ [CWE-690] [-Wanalyzer-null-dereference]
307 | b[0] = rom[i + p.order[0]];
| ~~~~~~~^~~
‘grade’: events 1-2
|
| 472 | void grade(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~
| | |
| | (1) entry to ‘grade’
| 473 | {
| 474 | grade_size(rg, rom, len);
| | ~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (2) calling ‘grade_size’ from ‘grade’
|
+--> ‘grade_size’: events 3-5
|
| 269 | void grade_size(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~~~~~~
| | |
| | (3) entry to ‘grade_size’
|......
| 272 | if (rg->fileSize < 0x101000) {
| | ~
| | |
| | (4) following ‘false’ branch...
|......
| 276 | switch (rg->fileSize) {
| | ~~~~~~
| | |
| | (5) ...to here
|
<------+
|
‘grade’: events 6-9
|
| 474 | grade_size(rg, rom, len);
| | ^~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (6) returning to ‘grade’ from ‘grade_size’
| 475 | if (rg->fileSizeGrade == GRADE_ERROR)
| | ~
| | |
| | (7) following ‘false’ branch...
|......
| 478 | grade_byte_order(rg, rom, len); // also grades ipl3
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (8) ...to here
| | (9) calling ‘grade_byte_order’ from ‘grade’
|
+--> ‘grade_byte_order’: events 10-11
|
| 296 | void grade_byte_order(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~~~~~~~~~~~~
| | |
| | (10) entry to ‘grade_byte_order’
|......
| 302 | perm_iterator_init(&p, 4);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (11) calling ‘perm_iterator_init’ from ‘grade_byte_order’
|
+--> ‘perm_iterator_init’: events 12-20
|
| 119 | int perm_iterator_init(struct perm_iterator *p, int n)
| | ^~~~~~~~~~~~~~~~~~
| | |
| | (12) entry to ‘perm_iterator_init’
| 120 | {
| 121 | if (n < 1)
| | ~
| | |
| | (13) following ‘false’ branch (when ‘n > 0’)...
|......
| 124 | p->num_elements = n;
| | ~
| | |
| | (14) ...to here
| 125 | p->order = calloc(n, sizeof(int));
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (15) allocated here
| 126 | p->direction = calloc(n, sizeof(char));
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | | |
| | | (17) allocated here
| | (16) allocated here
| 127 |
| 128 | if (!p->order || !p->direction)
| | ~~ ~
| | | |
| | | (19) ‘p.order’ is NULL
| | | (20) following ‘true’ branch...
| | (18) allocated here
|
‘perm_iterator_init’: event 21
|
|cc1:
| (21): ...to here
|
‘perm_iterator_init’: event 22
|
|cc1:
| (22): ‘p.order’ is NULL
|
‘perm_iterator_init’: event 23
|
|cc1:
| (23): ‘p.order’ is NULL
|
<------+
|
‘grade_byte_order’: events 24-27
|
| 302 | perm_iterator_init(&p, 4);
| | ^~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (24) returning to ‘grade_byte_order’ from ‘perm_iterator_init’
|......
| 306 | for (i = 0x40; i < 0x1000; i += 4) {
| | ~~~
| | |
| | (25) following ‘true’ branch (when ‘i <= 4095’)...
| 307 | b[0] = rom[i + p.order[0]];
| | ~ ~~~~~~~~~~
| | | |
| | | (27) dereference of NULL ‘p.order’
| | (26) ...to here
|
grader.c:342:1: warning: leak of ‘p.direction’ [CWE-401] [-Wanalyzer-malloc-leak]
342 | }
| ^
‘grade’: events 1-2
|
| 472 | void grade(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~
| | |
| | (1) entry to ‘grade’
| 473 | {
| 474 | grade_size(rg, rom, len);
| | ~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (2) calling ‘grade_size’ from ‘grade’
|
+--> ‘grade_size’: events 3-5
|
| 269 | void grade_size(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~~~~~~
| | |
| | (3) entry to ‘grade_size’
|......
| 272 | if (rg->fileSize < 0x101000) {
| | ~
| | |
| | (4) following ‘false’ branch...
|......
| 276 | switch (rg->fileSize) {
| | ~~~~~~
| | |
| | (5) ...to here
|
<------+
|
‘grade’: events 6-9
|
| 474 | grade_size(rg, rom, len);
| | ^~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (6) returning to ‘grade’ from ‘grade_size’
| 475 | if (rg->fileSizeGrade == GRADE_ERROR)
| | ~
| | |
| | (7) following ‘false’ branch...
|......
| 478 | grade_byte_order(rg, rom, len); // also grades ipl3
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (8) ...to here
| | (9) calling ‘grade_byte_order’ from ‘grade’
|
+--> ‘grade_byte_order’: events 10-11
|
| 296 | void grade_byte_order(struct romGrade *rg, uint8_t * rom, size_t len)
| | ^~~~~~~~~~~~~~~~
| | |
| | (10) entry to ‘grade_byte_order’
|......
| 302 | perm_iterator_init(&p, 4);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (11) calling ‘perm_iterator_init’ from ‘grade_byte_order’
|
+--> ‘perm_iterator_init’: events 12-18
|
| 119 | int perm_iterator_init(struct perm_iterator *p, int n)
| | ^~~~~~~~~~~~~~~~~~
| | |
| | (12) entry to ‘perm_iterator_init’
| 120 | {
| 121 | if (n < 1)
| | ~
| | |
| | (13) following ‘false’ branch (when ‘n > 0’)...
|......
| 124 | p->num_elements = n;
| | ~
| | |
| | (14) ...to here
| 125 | p->order = calloc(n, sizeof(int));
| 126 | p->direction = calloc(n, sizeof(char));
| | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (15) allocated here
| 127 |
| 128 | if (!p->order || !p->direction)
| | ~~ ~
| | | |
| | | (17) allocated here
| | | (18) following ‘true’ branch...
| | (16) allocated here
|
‘perm_iterator_init’: event 19
|
|cc1:
| (19): ...to here
|
‘perm_iterator_init’: event 20
|
|cc1:
| (20): allocated here
|
‘perm_iterator_init’: event 21
|
|cc1:
| (21): allocated here
|
<------+
|
‘grade_byte_order’: events 22-27
|
| 302 | perm_iterator_init(&p, 4);
| | ^~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (22) returning to ‘grade_byte_order’ from ‘perm_iterator_init’
|......
| 306 | for (i = 0x40; i < 0x1000; i += 4) {
| | ~~~
| | |
| | (23) following ‘true’ branch (when ‘i <= 4095’)...
| | (25) following ‘false’ branch (when ‘i > 4095’)...
| 307 | b[0] = rom[i + p.order[0]];
| | ~
| | |
| | (24) ...to here
|......
| 316 | rg->ipl3 = identify_ipl3(a + 0x40);
| | ~~ ~~~~~~~~~~~~~~~~~~~~~~~
| | | |
| | | (27) calling ‘identify_ipl3’ from ‘grade_byte_order’
| | (26) ...to here
|
+--> ‘identify_ipl3’: events 28-30
|
| 197 | int identify_ipl3(uint8_t * ipl3)
| | ^~~~~~~~~~~~~
| | |
| | (28) entry to ‘identify_ipl3’
| 198 | {
| 199 | uint8_t sha1sum[SHA1_DIGEST_SIZE];
| | ~~~~~~~
| | |
| | (29) allocated here
|......
| 205 | while (cics[cicIndex].type != 0) {
| | ~~~~~
| | |
| | (30) allocated here
|
‘identify_ipl3’: event 31
|
|cc1:
| (31): allocated here
|
‘identify_ipl3’: events 32-39
|
| 205 | while (cics[cicIndex].type != 0) {
| | ^
| | |
| | (32) allocated here
| | (33) allocated here
| | (34) following ‘true’ branch...
| 206 | if (!memcmp(cics[cicIndex].signature, sha1sum,
| | ~~ ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| | | | |
| | | | (37) allocated here
| | | (38) allocated here
| | | (39) following ‘true’ branch...
| | (35) ...to here
| | (36) allocated here
| 207 | SHA1_DIGEST_SIZE)) {
| | ~~~~~~~~~~~~~~~~~
|
‘identify_ipl3’: event 40
|
|cc1:
| (40): ...to here
|
‘identify_ipl3’: event 41
|
|cc1:
| (41): allocated here
|
‘identify_ipl3’: event 42
|
|cc1:
| (42): allocated here
|
<------+
|
‘grade_byte_order’: events 43-44
|
| 316 | rg->ipl3 = identify_ipl3(a + 0x40);
| | ^~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (43) returning to ‘grade_byte_order’ from ‘identify_ipl3’
| 317 | if (rg->ipl3 != -1)
| | ~
| | |
| | (44) following ‘true’ branch...
|
‘grade_byte_order’: event 45
|
|cc1:
| (45): ...to here
|
‘grade_byte_order’: events 46-52
|
| 321 | if (rg->ipl3 != -1) {
| | ^
| | |
| | (46) following ‘true’ branch...
| 322 | rg->ipl3Grade = GRADE_OK;
| | ~~
| | |
| | (47) ...to here
|......
| 330 | switch (rg->byteOrder) {
| | ~~~~~~
| | |
| | (48) following ‘false’ branch...
|......
| 334 | default:
| | ~~~~~~~
| | |
| | (49) ...to here
| 335 | if (rg->fix)
| | ~
| | |
| | (50) following ‘true’ branch...
| 336 | rg->byteOrderGrade = GRADE_FIXED;
| | ~~
| | |
| | (51) ...to here
|......
| 341 | perm_iterator_destroy(&p);
| | ~~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (52) calling ‘perm_iterator_destroy’ from ‘grade_byte_order’
|
+--> ‘perm_iterator_destroy’: events 53-58
|
| 141 | void perm_iterator_destroy(struct perm_iterator *p)
| | ^~~~~~~~~~~~~~~~~~~~~
| | |
| | (53) entry to ‘perm_iterator_destroy’
| 142 | {
| 143 | free(p->order);
| | ~~~~
| | |
| | (54) allocated here
| 144 | free(p->direction);
| | ~~~~
| | |
| | (55) state of ‘p.direction’: ‘start’ -> ‘freed’ (origin: NULL)
| | (56) allocated here
| 145 | }
| | ~
| | |
| | (57) allocated here
| | (58) allocated here
|
<------+
|
‘grade_byte_order’: events 59-60
|
| 341 | perm_iterator_destroy(&p);
| | ^~~~~~~~~~~~~~~~~~~~~~~~~
| | |
| | (59) returning to ‘grade_byte_order’ from ‘perm_iterator_destroy’
| 342 | }
| | ~
| | |
| | (60) ‘p.direction’ leaks here; was allocated at (58)
|
cc -Wall -Og -ggdb -std=c99 -fanalyzer -c -o sha1.o sha1.c
cc -Wall -Og -ggdb -std=c99 -fanalyzer -c -o mapfile-posix.o mapfile-posix.c
cc -Wall -Og -ggdb -std=c99 -fanalyzer -c -o mapfile-windows.o mapfile-windows.c
cc romjudge.o grader.o sha1.o mapfile-posix.o mapfile-windows.o -o romjudge

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.