summaryrefslogtreecommitdiff
path: root/tools/n64chksum.c
blob: 64cfd3aad5c63dbda783753107fcb2159dcb448a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>

#include "n64chksum.h"
#include "util.h"

//Based on uCON64's N64 checksum algorithm by Andreas Sterbenz

#define ROL(i, b) (((i) << (b)) | ((i) >> (32 - (b))))

bool n64chksum_calculate(const uint8_t *romData, int cicType, uint32_t *chksum)
{
    unsigned int seed;
    unsigned int t1, t2, t3, t4, t5, t6;
    size_t pos;

    const size_t START = 0x1000;
    const size_t END = START + 0x100000;

    // determine initial seed
    switch (cicType)
    {
    case 6101:
    case 6102:
        seed = 0xF8CA4DDC;
        break;
    case 6103:
        seed = 0xA3886759;
        break;
    case 6105:
        seed = 0xDF26F436;
        break;
    case 6106:
        seed = 0x1FEA617A;
        break;
    default:
        return false;  // unknown CIC type
    }

    t1 = t2 = t3 = t4 = t5 = t6 = seed;

    for (pos = START; pos < END; pos += 4)
    {
        unsigned int d = util_read_uint32_be(romData + pos);
        unsigned int r = ROL(d, (d & 0x1F));

        // increment t4 if t6 overflows
        if ((t6 + d) < t6)
            t4++;

        t6 += d;
        t3 ^= d;
        t5 += r;

        if (t2 > d)
            t2 ^= r;
        else
            t2 ^= t6 ^ d;

        if (cicType == 6105)
            t1 += util_read_uint32_be(&romData[0x0750 + (pos & 0xFF)]) ^ d;
        else
            t1 += t5 ^ d;
    }

    if (cicType == 6103)
    {
        chksum[0] = (t6 ^ t4) + t3;
        chksum[1] = (t5 ^ t2) + t1;
    }
    else if (cicType == 6106)
    {
        chksum[0] = (t6 * t4) + t3;
        chksum[1] = (t5 * t2) + t1;
    }
    else
    {
        chksum[0] = t6 ^ t4 ^ t3;
        chksum[1] = t5 ^ t2 ^ t1;
    }

    return true;
}