Refresh 10

This commit is contained in:
n64
2020-06-02 12:44:34 -04:00
parent e58669b4b3
commit d43d9b7f20
444 changed files with 30163 additions and 7628 deletions

16
tools/.gitignore vendored
View File

@@ -1,16 +1,16 @@
/aifc_decode
/aiff_extract_codebook
/armips
/extract_data_for_mio
/mio0
/n64cksum
/n64graphics
/textconv
/patch_libultra_math
/iplfontutil
/n64graphics_ci
/aifc_decode
/aiff_extract_codebook
/vadpcm_enc
/tabledesign
/extract_data_for_mio
/patch_libultra_math
/skyconv
/tabledesign
/textconv
/vadpcm_enc
!/ido5.3_compiler/lib/*.so
!/ido5.3_compiler/usr/lib/*.so
!/ido5.3_compiler/usr/lib/*.so.1

View File

@@ -1,49 +1,58 @@
CC := gcc
CFLAGS := -I . -Wall -Wextra -Wno-unused-parameter -pedantic -std=c99 -O3 -s
PROGRAMS := n64graphics n64graphics_ci mio0 n64cksum textconv patch_libultra_math iplfontutil aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv
CXX := g++
CFLAGS := -I . -Wall -Wextra -Wno-unused-parameter -pedantic -std=c99 -O2 -s
LDFLAGS := -lm
PROGRAMS := n64graphics n64graphics_ci mio0 n64cksum textconv patch_libultra_math aifc_decode aiff_extract_codebook vadpcm_enc tabledesign extract_data_for_mio skyconv
# if armips is not found on the system, build it in tools
ifeq (, $(shell which armips 2> /dev/null))
CXX_PROGRAMS += armips
endif
default: all
armips: armips.cpp
$(CXX) $(CXXFLAGS) -fno-exceptions -fno-rtti -pipe $^ -o $@ -lpthread
n64graphics_SOURCES := n64graphics.c utils.c
n64graphics_CFLAGS := -DN64GRAPHICS_STANDALONE
n64graphics_ci_SOURCES := n64graphics_ci_dir/n64graphics_ci.c n64graphics_ci_dir/exoquant/exoquant.c n64graphics_ci_dir/utils.c
n64graphics_ci_CFLAGS := -O2 # 3s faster compile time
mio0_SOURCES := libmio0.c
mio0_CFLAGS := -DMIO0_STANDALONE
n64cksum_SOURCES := n64cksum.c libmio0.c libsm64.c utils.c
n64cksum_SOURCES := n64cksum.c utils.c
n64cksum_CFLAGS := -DN64CKSUM_STANDALONE
textconv_SOURCES := textconv.c utf8.c hashtable.c
patch_libultra_math_SOURCES := patch_libultra_math.c
iplfontutil_SOURCES := iplfontutil.c
iplfontutil_CFLAGS := -O2 # faster compile time
aifc_decode_SOURCES := aifc_decode.c
aifc_decode_CFLAGS := -O2 # both runs and compiles faster than -O3
aiff_extract_codebook_SOURCES := aiff_extract_codebook.c
tabledesign_SOURCES := sdk-tools/tabledesign/codebook.c sdk-tools/tabledesign/estimate.c sdk-tools/tabledesign/print.c sdk-tools/tabledesign/tabledesign.c
tabledesign_CFLAGS := -Wno-uninitialized -laudiofile
tabledesign_CFLAGS := -Wno-uninitialized
tabledesign_LDFLAGS := -laudiofile
vadpcm_enc_SOURCES := sdk-tools/adpcm/vadpcm_enc.c sdk-tools/adpcm/vpredictor.c sdk-tools/adpcm/quant.c sdk-tools/adpcm/util.c sdk-tools/adpcm/vencode.c
vadpcm_enc_CFLAGS := -Wno-unused-result -Wno-uninitialized -Wno-sign-compare -Wno-absolute-value
extract_data_for_mio_SOURCES := extract_data_for_mio.c
extract_data_for_mio_CFLAGS := -O2
skyconv_SOURCES := skyconv.c n64graphics.c utils.c
skyconv_CFLAGS := -O2 -lm
all: $(PROGRAMS)
all: $(PROGRAMS) $(CXX_PROGRAMS)
clean:
$(RM) $(PROGRAMS)
$(RM) $(PROGRAMS) $(CXX_PROGRAMS)
define COMPILE
$(1): $($1_SOURCES)
$(CC) $(CFLAGS) $$^ -lm -o $$@ $($1_CFLAGS)
$(CC) $(CFLAGS) $($1_CFLAGS) $$^ -o $$@ $(LDFLAGS) $($1_LDFLAGS)
endef
$(foreach p,$(PROGRAMS),$(eval $(call COMPILE,$(p))))
.PHONY: all clean default

19906
tools/armips.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -481,11 +481,16 @@ def write_aifc(entry, out):
def write_aiff(entry, filename):
with tempfile.NamedTemporaryFile(suffix=".aifc") as temp:
temp = tempfile.NamedTemporaryFile(suffix=".aifc", delete=False)
try:
write_aifc(entry, temp)
temp.flush()
temp.close()
aifc_decode = os.path.join(os.path.dirname(__file__), "aifc_decode")
subprocess.run([aifc_decode, temp.name, filename], check=True)
finally:
temp.close()
os.remove(temp.name)
# Modified from https://stackoverflow.com/a/25935321/1359139, cc by-sa 3.0

View File

@@ -1,179 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define STBI_NO_LINEAR
#define STBI_NO_PSD
#define STBI_NO_TGA
#define STBI_NO_HDR
#define STBI_NO_PIC
#define STBI_NO_PNM
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb/stb_image_write.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#define GETBIT(buf, idx) ((buf[(idx)/8] >> (7-((idx)%8))) & 1)
#define SETBIT(buf, idx) buf[(idx)/8] |= (1 << (7-((idx)%8)))
#define IPL3_FONT_NCHARS 50
#define IPL3_FONT_CHAR_W 13
#define IPL3_FONT_CHAR_H 14
#define IPL3_FONT_CHAR_NPIXELS (IPL3_FONT_CHAR_W * IPL3_FONT_CHAR_H)
#define IPL3_FONT_CHAR_NBITS (IPL3_FONT_CHAR_NPIXELS + 2)
#define IPL3_FONT_CHAR_NBYTES (IPL3_FONT_CHAR_NBITS / 8)
#define IPL3_FONT_FILE_SIZE ((IPL3_FONT_NCHARS * IPL3_FONT_CHAR_NBYTES) + 0x12)
int ipl3font_decode(const char *binPath, const char *imgPath)
{
FILE *binfp = fopen(binPath, "rb");
if(binfp == NULL)
{
printf("error: could not open %s for input\n", binPath);
return EXIT_FAILURE;
}
fseek(binfp, 0, SEEK_END);
size_t binSize = ftell(binfp);
if(binSize != IPL3_FONT_FILE_SIZE)
{
printf("error: font bin size invalid (must be 0x%X bytes)\n", IPL3_FONT_FILE_SIZE);
fclose(binfp);
return EXIT_FAILURE;
}
rewind(binfp);
char *binBuf = (char *) malloc(binSize);
if(fread(binBuf, 1, binSize, binfp) != binSize)
{
printf("error: failed to read from %s\n", binPath);
fclose(binfp);
return EXIT_FAILURE;
}
fclose(binfp);
uint32_t outSize = IPL3_FONT_NCHARS * IPL3_FONT_CHAR_NPIXELS * sizeof(uint32_t);
uint32_t *outRgba32 = (uint32_t *) malloc(outSize);
int outIdx = 0;
for(int nChar = 0; nChar < IPL3_FONT_NCHARS; nChar++)
{
for(int nRow = 0; nRow < IPL3_FONT_CHAR_H; nRow++)
{
for(int nCol = 0; nCol < IPL3_FONT_CHAR_W; nCol++)
{
int idx = (nChar * IPL3_FONT_CHAR_NBITS) + (nRow * IPL3_FONT_CHAR_W) + nCol;
int bit = GETBIT(binBuf, idx);
outRgba32[outIdx++] = (bit == 1) ? 0xFFFFFFFF : 0xFF000000;
}
}
}
int stbres = stbi_write_png(imgPath,
IPL3_FONT_CHAR_W,
IPL3_FONT_NCHARS * IPL3_FONT_CHAR_H,
4,
outRgba32,
IPL3_FONT_CHAR_W * sizeof(uint32_t));
if(stbres == 0)
{
printf("error: failed to write %s\n", imgPath);
free(outRgba32);
free(binBuf);
return EXIT_FAILURE;
}
free(outRgba32);
free(binBuf);
return EXIT_SUCCESS;
}
int ipl3font_encode(const char *imgPath, const char *binPath)
{
int x, y, channels_in_file;
uint32_t *inRgba32 = (uint32_t *) stbi_load(imgPath, &x, &y, &channels_in_file, 4);
if(inRgba32 == NULL)
{
printf("error: failed to load %s\n", imgPath);
return EXIT_FAILURE;
}
if(x != IPL3_FONT_CHAR_W || y != IPL3_FONT_NCHARS * IPL3_FONT_CHAR_H)
{
printf("error: invalid ipl3 font image dimensions (must be %dx%d)\n",
IPL3_FONT_CHAR_W, IPL3_FONT_NCHARS * IPL3_FONT_CHAR_H);
stbi_image_free(inRgba32);
return EXIT_FAILURE;
}
char *out = calloc(IPL3_FONT_FILE_SIZE, 1);
int inIdx = 0;
for(int nChar = 0; nChar < IPL3_FONT_NCHARS; nChar++)
{
for(int nRow = 0; nRow < IPL3_FONT_CHAR_H; nRow++)
{
for(int nCol = 0; nCol < IPL3_FONT_CHAR_W; nCol++)
{
// source pixels that are not 0xFFFFFFFF are ignored
if(inRgba32[inIdx++] == 0xFFFFFFFF)
{
int idx = (nChar * IPL3_FONT_CHAR_NBITS) + (nRow * IPL3_FONT_CHAR_W) + nCol;
SETBIT(out, idx);
}
}
}
}
FILE * outfp = fopen(binPath, "wb");
if(outfp == NULL)
{
printf("error: failed to write to %s\n", binPath);
stbi_image_free(inRgba32);
free(out);
return EXIT_FAILURE;
}
fwrite(out, 1, IPL3_FONT_FILE_SIZE, outfp);
fclose(outfp);
stbi_image_free(inRgba32);
free(out);
return EXIT_SUCCESS;
}
int main(int argc, const char *argv[])
{
if(argc < 4)
{
printf("error: no paths\n");
printf("iplfontutil e <input_img> <output_bin>\n");
printf("iplfontutil d <input_bin> <output_img>\n");
return EXIT_FAILURE;
}
const char *mode = argv[1];
if(strcmp(mode, "e") == 0)
{
return ipl3font_encode(argv[2], argv[3]);
}
else if(strcmp(mode, "d") == 0)
{
return ipl3font_decode(argv[2], argv[3]);
}
else
{
printf("error: unknown mode\n");
return EXIT_FAILURE;
}
}

View File

@@ -1,439 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libmio0.h"
#include "libsm64.h"
#include "utils.h"
// TODO: make these configurable
#define IN_START_ADDR 0x000D0000
#define OUT_START_ADDR 0x00800000
// MIPS instruction decoding
#define OPCODE(IBUF_) ((IBUF_)[0] & 0xFC)
#define RS(IBUF_) ( (((IBUF_)[0] & 0x3) < 3) | (((IBUF_)[1] & 0xE0) > 5) )
#define RT(IBUF_) ((IBUF_)[1] & 0x1F)
typedef struct
{
unsigned int old; // MIO0 address in original ROM
unsigned int old_end; // ending MIO0 address in original ROM
unsigned int new; // starting MIO0 address in extended ROM
unsigned int new_end; // ending MIO0 address in extended ROM
unsigned int addr; // ASM address for referenced pointer
unsigned int a1_addiu; // ASM offset for ADDIU for A1
unsigned char command; // command type: 0x1A or 0x18 (or 0xFF for ASM)
} ptr_t;
// find a pointer in the list and return index
// ptr: address to find in table old values
// table: list of addresses to MIO0 data
// count: number of addresses in table
// returns index in table if found, -1 otherwise
static int find_ptr(unsigned int ptr, ptr_t table[], int count)
{
int i;
for (i = 0; i < count; i++) {
if (ptr == table[i].old) {
return i;
}
}
return -1;
}
// find locations of existing MIO0 data
// buf: buffer containing SM64 data
// length: length of buf
// table: table to store MIO0 addresses in
// returns number of MIO0 files stored in table old values
static int find_mio0(unsigned char *buf, unsigned int length, ptr_t table[])
{
unsigned int addr;
int count = 0;
// MIO0 data is on 16-byte boundaries
for (addr = IN_START_ADDR; addr < length; addr += 16) {
if (!memcmp(&buf[addr], "MIO0", 4)) {
table[count].old = addr;
count++;
}
}
return count;
}
// find pointers to MIO0 files and stores command type
// buf: buffer containing SM64 data
// length: length of buf
// table: list of addresses to MIO0 data
// count: number of addresses in table
static void find_pointers(unsigned char *buf, unsigned int length, ptr_t table[], int count)
{
unsigned int addr;
unsigned int ptr;
int idx;
for (addr = IN_START_ADDR; addr < length; addr += 4) {
if ((buf[addr] == 0x18 || buf[addr] == 0x1A) && buf[addr+1] == 0x0C && buf[addr+2] == 0x00) {
ptr = read_u32_be(&buf[addr+4]);
idx = find_ptr(ptr, table, count);
if (idx >= 0) {
table[idx].command = buf[addr];
table[idx].old_end = read_u32_be(&buf[addr+8]);
}
}
}
}
static unsigned int la2int(unsigned char *buf, unsigned int lui, unsigned int addiu)
{
unsigned short addr_low, addr_high;
addr_high = read_u16_be(&buf[lui + 0x2]);
addr_low = read_u16_be(&buf[addiu + 0x2]);
// ADDIU sign extends which causes the encoded high val to be +1 if low MSb is set
if (addr_low & 0x8000) {
addr_high--;
}
return (addr_high << 16) | addr_low;
}
// find references to the MIO0 blocks in ASM and store type
// buf: buffer containing SM64 data
// length: length of buf
// table: list of addresses to MIO0 data
// count: number of addresses in table
static void find_asm_pointers(unsigned char *buf, ptr_t table[], int count)
{
// find the ASM references
// looking for some code that follows one of the below patterns:
// lui a1, start_upper lui a1, start_upper
// lui a2, end_upper lui a2, end_upper
// addiu a2, a2, end_lower addiu a2, a2, end_lower
// addiu a1, a1, start_lower jal function
// jal function addiu a1, a1, start_lower
unsigned int addr;
unsigned int ptr;
unsigned int end;
int idx;
for (addr = 0; addr < IN_START_ADDR; addr += 4) {
if (OPCODE(&buf[addr]) == 0x3C && OPCODE(&buf[addr+4]) == 0x3C && OPCODE(&buf[addr+8]) == 0x24) {
unsigned int a1_addiu = 0;
if (OPCODE(&buf[addr+0xc]) == 0x24) {
a1_addiu = 0xc;
} else if (OPCODE(&buf[addr+0x10]) == 0x24) {
a1_addiu = 0x10;
}
if (a1_addiu) {
if ( (RT(&buf[addr]) == RT(&buf[addr+a1_addiu]))
&& (RT(&buf[addr+4]) == RT(&buf[addr+8])) ) {
ptr = la2int(buf, addr, addr + a1_addiu);
end = la2int(buf, addr + 4, addr + 0x8);
idx = find_ptr(ptr, table, count);
if (idx >= 0) {
INFO("Found ASM reference to %X at %X\n", ptr, addr);
table[idx].command = 0xFF;
table[idx].addr = addr;
table[idx].new_end = end;
table[idx].a1_addiu = a1_addiu;
}
}
}
}
}
}
// adjust pointers to from old to new locations
// buf: buffer containing SM64 data
// length: length of buf
// table: list of addresses to MIO0 data
// count: number of addresses in table
static void sm64_adjust_pointers(unsigned char *buf, unsigned int length, ptr_t table[], int count)
{
unsigned int addr;
unsigned int old_ptr;
int idx;
for (addr = IN_START_ADDR; addr < length; addr += 4) {
if ((buf[addr] == 0x17 || buf[addr] == 0x18 || buf[addr] == 0x1A) && buf[addr+1] == 0x0C && buf[addr+2] < 0x02) {
old_ptr = read_u32_be(&buf[addr+4]);
idx = find_ptr(old_ptr, table, count);
if (idx >= 0) {
INFO("Old pointer at %X = ", addr);
INFO_HEX(&buf[addr], 12);
INFO("\n");
write_u32_be(&buf[addr+4], table[idx].new);
write_u32_be(&buf[addr+8], table[idx].new_end);
if (buf[addr] != table[idx].command) {
buf[addr] = table[idx].command;
}
INFO("NEW pointer at %X = ", addr);
INFO_HEX(&buf[addr], 12);
INFO("\n");
}
}
}
}
// adjust 'pointer' encoded in ASM LUI and ADDIU instructions
static void sm64_adjust_asm(unsigned char *buf, ptr_t table[], int count)
{
unsigned int addr;
int i;
unsigned short addr_low, addr_high;
for (i = 0; i < count; i++) {
if (table[i].command == 0xFF) {
addr = table[i].addr;
INFO("Old ASM reference at %X = ", addr);
INFO_HEX(&buf[addr], 0x14);
INFO("\n");
addr_low = table[i].new & 0xFFFF;
addr_high = (table[i].new >> 16) & 0xFFFF;
// ADDIU sign extends which causes the summed high to be 1 less if low MSb is set
if (addr_low & 0x8000) {
addr_high++;
}
write_u16_be(&buf[addr + 0x2], addr_high);
write_u16_be(&buf[addr + table[i].a1_addiu+2], addr_low);
addr_low = table[i].new_end & 0xFFFF;
addr_high = (table[i].new_end >> 16) & 0xFFFF;
if (addr_low & 0x8000) {
addr_high++;
}
write_u16_be(&buf[addr + 0x6], addr_high);
write_u16_be(&buf[addr + 0xa], addr_low);
INFO("NEW ASM reference at %X = ", addr);
INFO_HEX(&buf[addr], 0x14);
INFO(" [%06X - %06X]\n", table[i].new, table[i].new_end);
}
}
}
// compute N64 ROM checksums
// buf: buffer with extended SM64 data
// cksum: two element array to write CRC1 and CRC2 to
// TODO: this could be hand optimized
static void sm64_calc_checksums(unsigned char *buf, unsigned int cksum[]) {
unsigned int t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
unsigned int s0, s6;
unsigned int a0, a1, a2, a3, at;
unsigned int lo;
unsigned int v0, v1;
unsigned int ra;
// derived from the SM64 boot code
s6 = 0x3f;
a0 = 0x1000; // 59c: 8d640008 lw a0,8(t3)
a1 = s6; // 5a0: 02c02825 move a1,s6
at = 0x5d588b65; // 5a4: 3c015d58 lui at,0x5d58
// 5a8: 34218b65 ori at,at,0x8b65
lo = a1 * at; // 5ac: 00a10019 multu a1,at 16 F8CA 4DDB
ra = 0x100000; // 5bc: 3c1f0010 lui ra,0x10
v1 = 0; // 5c0: 00001825 move v1,zero
t0 = 0; // 5c4: 00004025 move t0,zero
t1 = a0; // 5c8: 00804825 move t1,a0
t5 = 32; // 5cc: 240d0020 li t5,32
v0 = lo; // 5d0: 00001012 mflo v0
v0++; // 5d4: 24420001 addiu v0,v0,1
a3 = v0; // 5d8: 00403825 move a3,v0
t2 = v0; // 5dc: 00405025 move t2,v0
t3 = v0; // 5e0: 00405825 move t3,v0
s0 = v0; // 5e4: 00408025 move s0,v0
a2 = v0; // 5e8: 00403025 move a2,v0
t4 = v0; // 5ec: 00406025 move t4,v0
do {
v0 = read_u32_be(&buf[t1]); // 5f0: 8d220000 lw v0,0(t1)
v1 = a3 + v0; // 5f4: 00e21821 addu v1,a3,v0
at = (v1 < a3); // 5f8: 0067082b sltu at,v1,a3
a1 = v1; // 600: 00602825 move a1,v1 branch delay slot
if (at) { // 5fc: 10200002 beqz at,0x608
t2++; // 604: 254a0001 addiu t2,t2,1
}
v1 = v0 & 0x1F; // 608: 3043001f andi v1,v0,0x1f
t7 = t5 - v1; // 60c: 01a37823 subu t7,t5,v1
t8 = v0 >> t7; // 610: 01e2c006 srlv t8,v0,t7
t6 = v0 << v1; // 614: 00627004 sllv t6,v0,v1
a0 = t6 | t8; // 618: 01d82025 or a0,t6,t8
at = (a2 < v0); // 61c: 00c2082b sltu at,a2,v0
a3 = a1; // 620: 00a03825 move a3,a1
t3 ^= v0; // 624: 01625826 xor t3,t3,v0
s0 += a0; // 62c: 02048021 addu s0,s0,a0 branch delay slot
if (at) { // 628: 10200004 beqz at,0x63c
t9 = a3 ^ v0; // 630: 00e2c826 xor t9,a3,v0
// 634: 10000002 b 0x640
a2 ^= t9; // 638: 03263026 xor a2,t9,a2 branch delay
} else {
a2 ^= a0; // 63c: 00c43026 xor a2,a2,a0
}
t0 += 4; // 640: 25080004 addiu t0,t0,4
t7 = v0 ^ s0; // 644: 00507826 xor t7,v0,s0
t1 += 4; // 648: 25290004 addiu t1,t1,4
t4 += t7; // 650: 01ec6021 addu t4,t7,t4 branch delay
} while (t0 != ra); // 64c: 151fffe8 bne t0,ra,0x5f0
t6 = a3 ^ t2; // 654: 00ea7026 xor t6,a3,t2
a3 = t6 ^ t3; // 658: 01cb3826 xor a3,t6,t3
t8 = s0 ^ a2; // 65c: 0206c026 xor t8,s0,a2
s0 = t8 ^ t4; // 660: 030c8026 xor s0,t8,t4
cksum[0] = a3;
cksum[1] = s0;
}
rom_type sm64_rom_type(unsigned char *buf, unsigned int length)
{
const unsigned char bs[] = {0x37, 0x80, 0x40, 0x12};
const unsigned char be[] = {0x80, 0x37, 0x12, 0x40};
const unsigned char le[] = {0x40, 0x12, 0x37, 0x80};
if (!memcmp(buf, bs, sizeof(bs)) && length == (8*MB)) {
return ROM_SM64_BS;
}
if (!memcmp(buf, bs, sizeof(le)) && length == (8*MB)) {
return ROM_SM64_LE;
}
if (!memcmp(buf, be, sizeof(be))) {
if (length == 8*MB) {
return ROM_SM64_BE;
} else if (length > 8*MB) {
return ROM_SM64_BE_EXT;
}
}
return ROM_INVALID;
}
rom_version sm64_rom_version(unsigned char *buf)
{
typedef struct {const unsigned char cksum1[4]; const rom_version version;} version_entry;
const version_entry version_table[] =
{
{ {0x63, 0x5a, 0x2b, 0xff}, VERSION_SM64_U},
{ {0xa0, 0x3c, 0xf0, 0x36}, VERSION_SM64_E},
{ {0x4e, 0xaa, 0x3d, 0x0e}, VERSION_SM64_J},
{ {0xd6, 0xfb, 0xa4, 0xa8}, VERSION_SM64_SHINDOU},
};
for (unsigned int i = 0; i < DIM(version_table); i++) {
if (!memcmp(&buf[0x10], version_table[i].cksum1, 4)) {
return version_table[i].version;
}
}
return VERSION_UNKNOWN;
}
void sm64_decompress_mio0(const sm64_config *config,
unsigned char *in_buf,
unsigned int in_length,
unsigned char *out_buf)
{
#define MAX_PTRS 128
#define COMPRESSED_LENGTH 2
mio0_header_t head;
int bit_length;
int move_offset;
unsigned int in_addr;
unsigned int out_addr = OUT_START_ADDR;
unsigned int align_add = config->alignment - 1;
unsigned int align_mask = ~align_add;
ptr_t ptr_table[MAX_PTRS];
int ptr_count;
int i;
// find MIO0 locations and pointers
ptr_count = find_mio0(in_buf, in_length, ptr_table);
find_pointers(in_buf, in_length, ptr_table, ptr_count);
find_asm_pointers(in_buf, ptr_table, ptr_count);
// extract each MIO0 block and prepend fake MIO0 header for 0x1A command and ASM references
for (i = 0; i < ptr_count; i++) {
in_addr = ptr_table[i].old;
if (!memcmp(&in_buf[in_addr], "MIO0", 4)) {
unsigned int end;
int length;
int is_mio0 = 0;
// align output address
out_addr = (out_addr + align_add) & align_mask;
length = mio0_decode(&in_buf[in_addr], &out_buf[out_addr], &end);
if (length > 0) {
// dump MIO0 data and decompressed data to file
if (config->dump) {
char filename[FILENAME_MAX];
sprintf(filename, MIO0_DIR "/%08X.mio", in_addr);
write_file(filename, &in_buf[in_addr], end);
sprintf(filename, MIO0_DIR "/%08X", in_addr);
write_file(filename, &out_buf[out_addr], length);
}
// 0x1A commands and ASM references need fake MIO0 header
// relocate data and add MIO0 header with all uncompressed data
if (ptr_table[i].command == 0x1A || ptr_table[i].command == 0xFF) {
bit_length = (length + 7) / 8 + 2;
move_offset = MIO0_HEADER_LENGTH + bit_length + COMPRESSED_LENGTH;
memmove(&out_buf[out_addr + move_offset], &out_buf[out_addr], length);
head.dest_size = length;
head.comp_offset = move_offset - COMPRESSED_LENGTH;
head.uncomp_offset = move_offset;
mio0_encode_header(&out_buf[out_addr], &head);
memset(&out_buf[out_addr + MIO0_HEADER_LENGTH], 0xFF, head.comp_offset - MIO0_HEADER_LENGTH);
memset(&out_buf[out_addr + head.comp_offset], 0x0, 2);
length += head.uncomp_offset;
is_mio0 = 1;
} else if (ptr_table[i].command == 0x18) {
// 0x18 commands become 0x17
ptr_table[i].command = 0x17;
}
// use output from decoder to find end of ASM referenced MIO0 blocks
if (ptr_table[i].old_end == 0x00) {
ptr_table[i].old_end = in_addr + end;
}
INFO("MIO0 file %08X-%08X decompressed to %08X-%08X as raw data%s\n",
in_addr, ptr_table[i].old_end, out_addr, out_addr + length,
is_mio0 ? " with a MIO0 header" : "");
if (config->fill) {
INFO("Filling old MIO0 with 0x01 from %X length %X\n", in_addr, end);
memset(&out_buf[in_addr], 0x01, end);
}
// keep track of new pointers
ptr_table[i].new = out_addr;
ptr_table[i].new_end = out_addr + length;
out_addr += length + config->padding;
} else {
ERROR("Error decoding MIO0 block at %X\n", in_addr);
}
}
}
INFO("Ending offset: %X\n", out_addr);
// adjust pointers and ASM pointers to new values
sm64_adjust_pointers(out_buf, in_length, ptr_table, ptr_count);
sm64_adjust_asm(out_buf, ptr_table, ptr_count);
}
void sm64_update_checksums(unsigned char *buf)
{
unsigned int cksum_offsets[] = {0x10, 0x14};
unsigned int read_cksum[2];
unsigned int calc_cksum[2];
int i;
// assume CIC-NUS-6102
INFO("BootChip: CIC-NUS-6102\n");
// calculate new N64 header checksum
sm64_calc_checksums(buf, calc_cksum);
// mimic the n64sums output
for (i = 0; i < 2; i++) {
read_cksum[i] = read_u32_be(&buf[cksum_offsets[i]]);
INFO("CRC%d: 0x%08X ", i+1, read_cksum[i]);
INFO("Calculated: 0x%08X ", calc_cksum[i]);
if (calc_cksum[i] == read_cksum[i]) {
INFO("(Good)\n");
} else {
INFO("(Bad)\n");
}
}
// write checksums into header
INFO("Writing back calculated Checksum\n");
write_u32_be(&buf[cksum_offsets[0]], calc_cksum[0]);
write_u32_be(&buf[cksum_offsets[1]], calc_cksum[1]);
}

View File

@@ -1,62 +0,0 @@
#ifndef LIBSM64_H_
#define LIBSM64_H_
#define MIO0_DIR "mio0files"
// typedefs
typedef enum
{
ROM_INVALID, // not valid SM64 ROM
ROM_SM64_BS, // SM64 byte-swapped (BADC)
ROM_SM64_BE, // SM64 big-endian (ABCD)
ROM_SM64_LE, // SM64 little-endian
ROM_SM64_BE_EXT, // SM64 big-endian, extended
} rom_type;
typedef enum
{
VERSION_UNKNOWN,
VERSION_SM64_U,
VERSION_SM64_E,
VERSION_SM64_J,
VERSION_SM64_SHINDOU,
} rom_version;
typedef struct
{
char *in_filename;
char *ext_filename;
unsigned int ext_size;
unsigned int padding;
unsigned int alignment;
char fill;
char dump;
} sm64_config;
// determine ROM type based on data
// buf: buffer containing raw SM64 ROM file data
// length: length of 'buf'
// returns SM64 ROM type or invalid
rom_type sm64_rom_type(unsigned char *buf, unsigned int length);
// determine SM64 ROM type based on cksum data
// buf: buffer containing raw SM64 ROM file data
// returns SM64 ROM version or unknown
rom_version sm64_rom_version(unsigned char *buf);
// find and decompress all MIO0 blocks
// config: configuration to determine alignment, padding and size
// in_buf: buffer containing entire contents of SM64 data in big endian
// length: length of in_buf
// out_buf: buffer containing extended SM64
void sm64_decompress_mio0(const sm64_config *config,
unsigned char *in_buf,
unsigned int in_length,
unsigned char *out_buf);
// update N64 header checksums
// buf: buffer containing ROM data
// checksums are written into the buffer
void sm64_update_checksums(unsigned char *buf);
#endif // LIBSM64_H_

View File

@@ -1,11 +1,95 @@
#include <stdio.h>
#include <stdlib.h>
#include "libsm64.h"
#include "n64cksum.h"
#include "utils.h"
#define N64CKSUM_VERSION "0.1"
// compute N64 ROM checksums
// buf: buffer with extended SM64 data
// cksum: two element array to write CRC1 and CRC2 to
void n64cksum_calc_6102(unsigned char *buf, unsigned int cksum[]) {
uint32_t t2, t3, t4, t6, t7, t8, s0;
uint32_t a0, a1, a2, a3;
uint32_t v0, v1;
uint32_t seed, end_offset, cur_offset, buf_offset;
// derived from the SM64 boot code
seed = 0xF8CA4DDB; // 0x3f * 0x5d588b65;
end_offset = 0x100000;
cur_offset = 0;
buf_offset = 0x1000;
seed++;
a3 = seed;
t2 = seed;
t3 = seed;
s0 = seed;
a2 = seed;
t4 = seed;
do {
v0 = read_u32_be(&buf[buf_offset]);
v1 = a3 + v0;
a1 = v1;
if (v1 < a3) {
t2++;
}
v1 = v0 & 0x1F;
t7 = 32 - v1;
t8 = v0 >> t7;
t6 = v0 << v1;
a0 = t6 | t8;
a3 = a1;
t3 ^= v0;
s0 += a0;
if (a2 < v0) {
a2 ^= a3 ^ v0;
} else {
a2 ^= a0;
}
cur_offset += 4;
t7 = v0 ^ s0;
buf_offset += 4;
t4 += t7;
} while (cur_offset != end_offset);
cksum[0] = (a3 ^ t2) ^ t3;
cksum[1] = (s0 ^ a2) ^ t4;
}
void n64cksum_update_checksums(uint8_t *buf)
{
unsigned int cksum_offsets[] = {0x10, 0x14};
uint32_t read_cksum[2];
uint32_t calc_cksum[2];
int i;
// assume CIC-NUS-6102
INFO("BootChip: CIC-NUS-6102\n");
// calculate new N64 header checksum
n64cksum_calc_6102(buf, calc_cksum);
// mimic the n64sums output
for (i = 0; i < 2; i++) {
read_cksum[i] = read_u32_be(&buf[cksum_offsets[i]]);
INFO("CRC%d: 0x%08X ", i+1, read_cksum[i]);
INFO("Calculated: 0x%08X ", calc_cksum[i]);
if (calc_cksum[i] == read_cksum[i]) {
INFO("(Good)\n");
} else {
INFO("(Bad)\n");
}
}
// write checksums into header
INFO("Writing back calculated Checksum\n");
write_u32_be(&buf[cksum_offsets[0]], calc_cksum[0]);
write_u32_be(&buf[cksum_offsets[1]], calc_cksum[1]);
}
#ifdef N64CKSUM_STANDALONE
static void print_usage(void)
{
ERROR("Usage: n64cksum ROM [ROM_OUT]\n"
@@ -42,7 +126,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
sm64_update_checksums(rom_data);
n64cksum_update_checksums(rom_data);
write_length = write_file(file_out, rom_data, length);
@@ -55,3 +139,4 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
#endif // N64CKSUM_STANDALONE

16
tools/n64cksum.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef N64CKSUM_H_
#define N64CKSUM_H_
#include <stdint.h>
// compute N64 ROM checksums
// buf: buffer with extended SM64 data
// cksum: two element array to write CRC1 and CRC2 to
void n64cksum_calc_6102(unsigned char *buf, unsigned int cksum[]);
// update N64 header checksums
// buf: buffer containing ROM data
// checksums are written into the buffer
void n64cksum_update_checksums(uint8_t *buf);
#endif // N64CKSUM_H_

File diff suppressed because it is too large Load Diff

View File

@@ -18,6 +18,14 @@ typedef struct _ia
uint8_t alpha;
} ia;
// CI palette
typedef struct
{
uint16_t data[256];
int max; // max number of entries
int used; // number of entries used
} palette_t;
//---------------------------------------------------------
// N64 RGBA/IA/I/CI -> intermediate RGBA/IA
//---------------------------------------------------------
@@ -31,10 +39,6 @@ ia *raw2ia(const uint8_t *raw, int width, int height, int depth);
// N64 raw I4/I8 -> intermediate IA
ia *raw2i(const uint8_t *raw, int width, int height, int depth);
// N64 raw CI + palette -> intermediate RGBA
rgba *rawci2rgba(const uint8_t *rawci, const uint8_t *palette, int width, int height, int depth);
//---------------------------------------------------------
// intermediate RGBA/IA -> N64 RGBA/IA/I/CI
// returns length written to 'raw' used or -1 on error
@@ -49,9 +53,16 @@ int ia2raw(uint8_t *raw, const ia *img, int width, int height, int depth);
// intermediate IA -> N64 raw I4/I8
int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth);
// intermediate RGBA -> N64 raw CI + palette
// TODO
// int rgba2rawci(uint8_t *raw, uint8_t *out_palette, int *pal_len, const rgba *img, int width, int height, int depth);
//---------------------------------------------------------
// N64 CI <-> N64 RGBA16/IA16
//---------------------------------------------------------
// N64 CI raw data and palette to raw data (either RGBA16 or IA16)
uint8_t *ci2raw(const uint8_t *rawci, const uint8_t *palette, int width, int height, int ci_depth);
// convert from raw (RGBA16 or IA16) format to CI + palette
int raw2ci(uint8_t *rawci, palette_t *pal, const uint8_t *raw, int raw_len, int ci_depth);
//---------------------------------------------------------

76
tools/rasm2armips.py Executable file
View File

@@ -0,0 +1,76 @@
#!/usr/bin/env python
import argparse
import re
import sys
def read_file(filepath):
with open(filepath) as f:
lines = f.readlines()
split_lines = [re.split(r'[ ,]+', l.strip().replace('$', '')) for l in lines]
return split_lines
# jumps and branches with named targets
jumps = ['jal', 'j']
branches = ['beq', 'bgez', 'bgtz', 'blez', 'bltz', 'bne']
jump_branches = jumps + branches
# jumps and branches with delay slots
has_delay_slot = jump_branches + ['jr']
def decode_references(instructions):
refs = []
for ins in instructions:
if ins[3] in jump_branches:
target = int(ins[-1], 0)
if target not in refs:
refs.append(target)
return refs
def reassemble(args, instructions, refs):
print('.rsp')
print('\n.create DATA_FILE, 0x%04X' % 0x0000)
print('\n.close // DATA_FILE\n')
print('.create CODE_FILE, 0x%08X\n' % args.base)
delay_slot = False
for ins in instructions:
addr = int(ins[0], 0)
if (addr & 0xFFFF) in refs:
print('%s_%08x:' % (args.name, addr))
sys.stdout.write(' ' * args.indent)
if delay_slot:
sys.stdout.write(' ')
delay_slot = False
if ins[3] in jumps:
target = int(ins[-1], 0) | (args.base & 0xFFFF0000)
ins[-1] = '%s_%08x' % (args.name, target)
elif ins[3] in branches:
if ins[3][-1] =='z' and ins[5] == 'zero':
del ins[5] # remove 'zero' operand from branch
target = (int(ins[-1], 0) & 0x1FFF) + (args.base & 0xFFFF0000)
ins[-1] = '%s_%08x' % (args.name, target)
elif ins[3] == 'vsar': # fixup last operand of vsar
reg_map = {'ACC_H': 0, 'ACC_M': 1, 'ACC_L': 2}
reg = ins[4].split(r'[')[0]
num = reg_map[ins[-1]]
ins[-1] = '%s[%d]' % (reg, num)
if ins[3] in has_delay_slot:
delay_slot = True
if len(ins) > 4: # with args
print('%-5s %s' % (ins[3], ', '.join(ins[4:])))
else:
print('%s' % ins[3])
print('\n.close // CODE_FILE')
def main():
parser = argparse.ArgumentParser()
parser.add_argument('input_file', help="input assembly file generated from `rasm2 -D -e -a rsp -B -o 0x04001000 -f`")
parser.add_argument('-b', type=int, help="base address of file", dest='base', default=0x04001000)
parser.add_argument('-i', type=int, help="amount of indentation", dest='indent', default=4)
parser.add_argument('-n', help="name to prefex labels with", dest='name', default='f3d')
args = parser.parse_args()
lines = read_file(args.input_file)
refs = decode_references(lines)
reassemble(args, lines, refs)
main()

View File

@@ -212,6 +212,11 @@ static void assign_tile_positions() {
}
}
// Provide a replacement for realpath on Windows
#ifdef _WIN32
#define realpath(path, resolved_path) _fullpath(resolved_path, path, PATH_MAX)
#endif
/* write pngs to disc */
void write_tiles() {
const ImageProps props = IMAGE_PROPERTIES[type][true];
@@ -479,6 +484,7 @@ static void usage() {
// Modified from n64split
static int parse_arguments(int argc, char *argv[]) {
programName = argv[0];
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--combine") == 0) {
if (++i >= argc || mode != InvalidMode) {

View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
import os
import re
import sys
file_list = [
'stdafx.h',
'ext/tinyformat/tinyformat.h',
'Commands/CAssemblerCommand.h',
'Core/Expression.h',
'Core/ExpressionFunctions.h',
'Core/SymbolData.h',
'Util/Util.h',
'Util/FileClasses.h',
'Util/ByteArray.h',
'Core/FileManager.h',
'Core/ELF/ElfTypes.h',
'Core/ELF/ElfFile.h',
'Core/ELF/ElfRelocator.h',
'Archs/Architecture.h',
'Archs/MIPS/Mips.h',
'Archs/MIPS/MipsOpcodes.h',
'Archs/MIPS/CMipsInstruction.h',
'Util/EncodingTable.h',
'Core/Misc.h',
'Core/Assembler.h',
'Core/SymbolTable.h',
'Core/Common.h',
'Parser/DirectivesParser.h',
'Parser/Tokenizer.h',
'Archs/MIPS/MipsMacros.h',
'Archs/MIPS/MipsParser.h',
'Archs/MIPS/CMipsInstruction.cpp',
'Archs/MIPS/MipsExpressionFunctions.h',
'Archs/MIPS/MipsElfRelocator.h',
'Archs/MIPS/Mips.cpp',
'Archs/MIPS/MipsElfFile.h',
'Util/CRC.h',
'Archs/MIPS/MipsElfFile.cpp',
'Commands/CommandSequence.h',
'Parser/Parser.h',
'Archs/MIPS/MipsElfRelocator.cpp',
'Archs/MIPS/MipsExpressionFunctions.cpp',
'Archs/MIPS/MipsMacros.cpp',
'Archs/MIPS/MipsOpcodes.cpp',
'Parser/ExpressionParser.h',
'Archs/MIPS/PsxRelocator.h',
'Commands/CDirectiveFile.h',
'Archs/MIPS/MipsParser.cpp',
'Archs/MIPS/PsxRelocator.cpp',
'Archs/Architecture.cpp',
'Commands/CAssemblerCommand.cpp',
'Commands/CAssemblerLabel.h',
'Commands/CAssemblerLabel.cpp',
'Commands/CDirectiveArea.h',
'Commands/CDirectiveArea.cpp',
'Commands/CDirectiveConditional.h',
'Commands/CDirectiveConditional.cpp',
'Commands/CDirectiveData.h',
'Commands/CDirectiveData.cpp',
'Commands/CDirectiveFile.cpp',
'Commands/CDirectiveMessage.h',
'Commands/CDirectiveMessage.cpp',
'Commands/CommandSequence.cpp',
'Core/ELF/ElfFile.cpp',
'Core/ELF/ElfRelocator.cpp',
'Core/Assembler.cpp',
'Core/Common.cpp',
'Core/Expression.cpp',
'Core/ExpressionFunctions.cpp',
'Core/FileManager.cpp',
'Core/Misc.cpp',
'Core/SymbolData.cpp',
'Core/SymbolTable.cpp',
'Parser/DirectivesParser.cpp',
'Parser/ExpressionParser.cpp',
'Parser/Parser.cpp',
'Parser/Tokenizer.cpp',
'Util/ByteArray.cpp',
'Util/CRC.cpp',
'Util/EncodingTable.cpp',
'Util/FileClasses.cpp',
'Util/Util.cpp',
'Main/CommandLineInterface.h',
'Main/CommandLineInterface.cpp',
'Main/main.cpp',
]
file_header = \
"""// armips assembler v0.11
// https://github.com/Kingcom/armips/
// To simplify compilation, all files have been concatenated into one.
// MIPS only, ARM is not included.\n\n"""
def banned(line):
return '#pragma once' in line or '#include "' in line
def cat_file(fout, fin_name):
with open(fin_name) as fin:
lines = fin.readlines()
lines = [l.rstrip() for l in lines if not banned(l)]
for l in lines:
if re.search(r'\bArm\b', l):
fout.write("#ifdef ARMIPS_ARM\n") # must manually insert #endif
fout.write(l + '\n')
fout.write('\n')
def combine_armips(fout_name, armips_path):
with open(fout_name, 'w') as fout:
fout.write(file_header)
fout.write("/*\n")
cat_file(fout, os.path.join(armips_path, 'LICENSE.txt'))
fout.write("*/\n\n")
for f in file_list:
fout.write(f"// file: {f}\n")
cat_file(fout, os.path.join(armips_path, f))
def main():
if len(sys.argv) > 1 and sys.argv[1] in ['-h', '--help']:
print('Usage: generate_armips_cpp.py [output_filename] [armips_src_dir]')
print('Defaults: [output_filename = "armips.cpp"] [armips_src_dir = "./armips"]')
return
fout_name = sys.argv[1] if len(sys.argv) > 1 else 'armips.cpp'
armips_path = sys.argv[2] if len(sys.argv) > 2 else './armips'
combine_armips(fout_name, os.path.expanduser(armips_path))
main()