summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
committerXavi Del Campo <xavi.dcr@tutanota.com>2020-01-31 10:32:23 +0100
commit7c24e9a9b02b04dcaf9507acb94091ea70a2c02d (patch)
treec28d0748652ad4b4222309e46e6cfc82c0906220 /tools
parenta2b7b6bb1cc2f4a3258b7b2dbc92399d151f864d (diff)
Imported pristine psxsdk-20190410 from official repo
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile77
-rw-r--r--tools/adpcm.c259
-rw-r--r--tools/adpcm.h14
-rw-r--r--tools/bin2c.c30
-rwxr-xr-xtools/bmp2tim.c895
-rw-r--r--tools/cdcat.c373
-rwxr-xr-xtools/elf2exe.c231
-rw-r--r--tools/endian.c52
-rw-r--r--tools/exefixup.c204
-rw-r--r--tools/getpsxiso.c55
-rw-r--r--tools/huff.c555
-rw-r--r--tools/lictool.c100
-rw-r--r--tools/mkpsxiso.c250
-rw-r--r--tools/mod4psx.c164
-rw-r--r--tools/psfex.c113
-rw-r--r--tools/spasm/Makefile17
-rw-r--r--tools/spasm/codegen.c144
-rw-r--r--tools/spasm/codegen.h33
-rw-r--r--tools/spasm/error.c58
-rw-r--r--tools/spasm/error.h10
-rw-r--r--tools/spasm/eval.c130
-rw-r--r--tools/spasm/eval.h6
-rwxr-xr-xtools/spasm/fcaseopen.c137
-rwxr-xr-xtools/spasm/fcaseopen.h40
-rw-r--r--tools/spasm/opcode.c2214
-rw-r--r--tools/spasm/opcode.h108
-rw-r--r--tools/spasm/parser.c478
-rw-r--r--tools/spasm/parser.h29
-rw-r--r--tools/spasm/readme.txt120
-rw-r--r--tools/spasm/spasm.c167
-rw-r--r--tools/spasm/spasm.h20
-rw-r--r--tools/systemcnf.c50
-rw-r--r--tools/tim2bmp.c390
-rwxr-xr-xtools/vag2wav.c202
-rw-r--r--tools/wav2vag.c431
35 files changed, 8156 insertions, 0 deletions
diff --git a/tools/Makefile b/tools/Makefile
new file mode 100644
index 0000000..15294ff
--- /dev/null
+++ b/tools/Makefile
@@ -0,0 +1,77 @@
+# Makefile for the PSXSDK tools
+
+include ../Makefile.cfg
+
+TOOL_LIST = bmp2tim$(EXE_SUFFIX) \
+ cdcat$(EXE_SUFFIX) \
+ elf2exe$(EXE_SUFFIX) \
+ getpsxiso$(EXE_SUFFIX) \
+ mkpsxiso$(EXE_SUFFIX) \
+ vag2wav$(EXE_SUFFIX) \
+ wav2vag$(EXE_SUFFIX) \
+ exefixup$(EXE_SUFFIX) \
+ systemcnf$(EXE_SUFFIX) \
+ bin2c$(EXE_SUFFIX) \
+ huff$(EXE_SUFFIX) \
+ mod4psx$(EXE_SUFFIX) \
+ tim2bmp$(EXE_SUFFIX) \
+ lictool$(EXE_SUFFIX) \
+ psfex$(EXE_SUFFIX)
+
+all: $(TOOL_LIST)
+ $(MAKE_COMMAND) -C spasm
+
+bmp2tim$(EXE_SUFFIX): bmp2tim.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ bmp2tim.c $(HOST_LDFLAGS)
+
+cdcat$(EXE_SUFFIX): cdcat.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ cdcat.c $(HOST_LDFLAGS)
+
+elf2exe$(EXE_SUFFIX): elf2exe.c
+ $(HOST_CC) $(HOST_CFLAGS) -DOBJCOPY_PATH=\"$(OBJCOPY)\" -o $@ elf2exe.c $(HOST_LDFLAGS)
+
+getpsxiso$(EXE_SUFFIX): getpsxiso.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ getpsxiso.c $(HOST_LDFLAGS)
+
+mkpsxiso$(EXE_SUFFIX): mkpsxiso.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ mkpsxiso.c $(HOST_LDFLAGS)
+
+vag2wav$(EXE_SUFFIX): vag2wav.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ vag2wav.c $(HOST_LDFLAGS)
+
+wav2vag$(EXE_SUFFIX): wav2vag.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ wav2vag.c $(HOST_LDFLAGS)
+
+exefixup$(EXE_SUFFIX): exefixup.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ exefixup.c $(HOST_LDFLAGS)
+
+systemcnf$(EXE_SUFFIX): systemcnf.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ systemcnf.c $(HOST_LDFLAGS)
+
+bin2c$(EXE_SUFFIX): bin2c.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ bin2c.c $(HOST_LDFLAGS)
+
+huff$(EXE_SUFFIX): huff.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ huff.c $(HOST_LDFLAGS)
+
+mod4psx$(EXE_SUFFIX): mod4psx.c adpcm.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ mod4psx.c adpcm.c ../libmodplay/libmodplay_nopsx.a -lm -DNO_PSX_LIB
+
+tim2bmp$(EXE_SUFFIX): tim2bmp.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ tim2bmp.c -lz $(HOST_LDFLAGS)
+
+lictool$(EXE_SUFFIX): lictool.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ lictool.c $(HOST_LDFLAGS)
+
+psfex$(EXE_SUFFIX): psfex.c
+ $(HOST_CC) $(HOST_CFLAGS) -o $@ psfex.c -lz $(HOST_LDFLAGS)
+
+clean:
+ rm -f $(TOOL_LIST)
+ $(MAKE_COMMAND) -C spasm clean
+
+distclean: clean
+
+install:
+ cp -rv $(TOOL_LIST) $(TOOLCHAIN_PREFIX)/bin
+ $(MAKE_COMMAND) -C spasm install
diff --git a/tools/adpcm.c b/tools/adpcm.c
new file mode 100644
index 0000000..1ace061
--- /dev/null
+++ b/tools/adpcm.c
@@ -0,0 +1,259 @@
+/*
+ * PCM -> ADPCM routines
+ *
+ * based on work by Bitmaster and extended
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include "adpcm.h"
+
+#define PCM_BUFFER_SIZE 128*28
+
+short pcm_buffer[PCM_BUFFER_SIZE];
+
+void SsAdpcm_find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor);
+void SsAdpcm_pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor);
+
+int SsAdpcmPack(void *pcm_data, void *adpcm_data, int sample_len,
+ int sample_fmt, int adpcm_len, int enable_looping,
+ int loop_start)
+{
+ short *ptr;
+ unsigned char *pcm_data_c = pcm_data;
+ short *pcm_data_s = pcm_data;
+ unsigned char *adpcm_data_c = adpcm_data;
+ // short *adpcm_data_s = adpcm_data;
+ double d_samples[28];
+ short four_bit[28];
+ int predict_nr;
+ int shift_factor;
+ int flags;
+ int size;
+ int i, j, k;
+ unsigned char d;
+ // char s[4];
+ // int chunk_data;
+ // short e;
+ // short sample_size;
+ //unsigned char c;
+ int ap = 0;
+ int sp = 0;
+
+/*printf("pcm_data = %x, adpcm_data = %x, len = %x, fmt = %x, alen = %x,"
+ "loop = %x\n", pcm_data, adpcm_data, sample_len, sample_fmt,
+ adpcm_len, enable_looping);
+ for(i=0;i<8;i++)
+ printf("I[%d] = %x\n", i, pcm_data_c[i]);*/
+
+ //if(enable_looping)
+// flags = 6;
+// else
+ flags = 0;
+
+while( sample_len > 0 ) {
+ size = ( sample_len >= PCM_BUFFER_SIZE ) ? PCM_BUFFER_SIZE : sample_len;
+
+ if(sample_fmt == FMT_U8)
+ {
+ for(i = 0; i < size; i++)
+ {
+ //c = fgetc(fp);
+ pcm_buffer[i] = *(pcm_data_c++);
+ pcm_buffer[i] ^= 0x80;
+ pcm_buffer[i] <<= 8;
+ }
+ }
+ else if(sample_fmt == FMT_S16)
+ {
+ //fread( wave, sizeof( short ), size, fp );
+ memcpy(pcm_buffer, pcm_data_s, size * sizeof(short));
+ pcm_data_s += size;
+ }
+ else
+ {
+ printf("%s, line %d: Unknown source sample format!, id=%d\n",__FUNCTION__,__LINE__,sample_fmt);
+ return 0;
+ }
+
+ i = size / 28;
+ if ( size % 28 ) {
+ for ( j = size % 28; j < 28; j++ )
+ pcm_buffer[28*i+j] = 0;
+ i++;
+ }
+
+ for ( j = 0; j < i; j++ ) { // pack 28 samples
+ if ( sample_len < 28 && enable_looping == 0)
+ flags = 1;
+
+ if(enable_looping)
+ {
+ if(((loop_start/28)*28) == sp)
+ flags = 6;
+ else
+ flags = 2;
+ }
+
+
+ ptr = pcm_buffer + j * 28;
+ SsAdpcm_find_predict( ptr, d_samples, &predict_nr, &shift_factor );
+ SsAdpcm_pack( d_samples, four_bit, predict_nr, shift_factor );
+ d = ( predict_nr << 4 ) | shift_factor;
+ // fputc( d, vag );
+ adpcm_data_c[ap++] = d;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+ // fputc( flags, vag );
+ adpcm_data_c[ap++] = flags;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+ for ( k = 0; k < 28; k += 2 ) {
+ d = ( ( four_bit[k+1] >> 8 ) & 0xf0 ) | ( ( four_bit[k] >> 12 ) & 0xf );
+ // fputc( d, vag );
+ adpcm_data_c[ap++] = d;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+ }
+ sample_len -= 28;
+ sp += 28;
+ }
+ }
+
+ // fputc( ( predict_nr << 4 ) | shift_factor, vag );
+ adpcm_data_c[ap++] = ( predict_nr << 4 ) | shift_factor;
+ if(ap>=adpcm_len) goto adpcm_too_big;
+
+ if(enable_looping)
+ // fputc(3, vag);
+ adpcm_data_c[ap++] = 3;
+ else
+// fputc( 7, vag ); // end flag
+ adpcm_data_c[ap++] = 7;
+
+ if(ap>=adpcm_len) goto adpcm_too_big;
+
+ for ( i = 0; i < 14; i++ )
+ // fputc( 0, vag );
+ adpcm_data_c[ap++] = 0;
+
+ if(ap>=adpcm_len) goto adpcm_too_big;
+
+ return ap;
+
+adpcm_too_big:
+ printf("%s: Resulting ADPCM data would have been larger than the output array length! Exiting %s.\n", __FUNCTION__, __FUNCTION__);
+ return 0;
+}
+
+
+
+
+
+
+void SsAdpcm_find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor )
+{
+ int i, j;
+ double buffer[28][5];
+ double min = 1e10;
+ double max[5];
+ double ds;
+ int min2;
+ int shift_mask;
+ static double _s_1 = 0.0; // s[t-1]
+ static double _s_2 = 0.0; // s[t-2]
+ double s_0, s_1, s_2;
+ double f[5][2] = { { 0.0, 0.0 },
+ { -60.0 / 64.0, 0.0 },
+ { -115.0 / 64.0, 52.0 / 64.0 },
+ { -98.0 / 64.0, 55.0 / 64.0 },
+ { -122.0 / 64.0, 60.0 / 64.0 } };
+
+ for ( i = 0; i < 5; i++ ) {
+ max[i] = 0.0;
+ s_1 = _s_1;
+ s_2 = _s_2;
+ for ( j = 0; j < 28; j ++ ) {
+ s_0 = (double) samples[j]; // s[t-0]
+ if ( s_0 > 30719.0 )
+ s_0 = 30719.0;
+ if ( s_0 < - 30720.0 )
+ s_0 = -30720.0;
+ ds = s_0 + s_1 * f[i][0] + s_2 * f[i][1];
+ buffer[j][i] = ds;
+ if ( fabs( ds ) > max[i] )
+ max[i] = fabs( ds );
+// printf( "%+5.2f\n", s2 );
+ s_2 = s_1; // new s[t-2]
+ s_1 = s_0; // new s[t-1]
+ }
+
+ if ( max[i] < min ) {
+ min = max[i];
+ *predict_nr = i;
+ }
+ if ( min <= 7 ) {
+ *predict_nr = 0;
+ break;
+ }
+
+ }
+
+// store s[t-2] and s[t-1] in a static variable
+// these than used in the next function call
+
+ _s_1 = s_1;
+ _s_2 = s_2;
+
+ for ( i = 0; i < 28; i++ )
+ d_samples[i] = buffer[i][*predict_nr];
+
+// if ( min > 32767.0 )
+// min = 32767.0;
+
+ min2 = ( int ) min;
+ shift_mask = 0x4000;
+ *shift_factor = 0;
+
+ while( *shift_factor < 12 ) {
+ if ( shift_mask & ( min2 + ( shift_mask >> 3 ) ) )
+ break;
+ (*shift_factor)++;
+ shift_mask = shift_mask >> 1;
+ }
+
+}
+
+void SsAdpcm_pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor )
+{
+ static double f[5][2] = { { 0.0, 0.0 },
+ { -60.0 / 64.0, 0.0 },
+ { -115.0 / 64.0, 52.0 / 64.0 },
+ { -98.0 / 64.0, 55.0 / 64.0 },
+ { -122.0 / 64.0, 60.0 / 64.0 } };
+ double ds;
+ int di;
+ double s_0;
+ static double s_1 = 0.0;
+ static double s_2 = 0.0;
+ int i;
+
+ for ( i = 0; i < 28; i++ ) {
+ s_0 = d_samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
+ ds = s_0 * (double) ( 1 << shift_factor );
+
+ di = ( (int) ds + 0x800 ) & 0xfffff000;
+
+ if ( di > 32767 )
+ di = 32767;
+ if ( di < -32768 )
+ di = -32768;
+
+ four_bit[i] = (short) di;
+
+ di = di >> shift_factor;
+ s_2 = s_1;
+ s_1 = (double) di - s_0;
+
+ }
+}
diff --git a/tools/adpcm.h b/tools/adpcm.h
new file mode 100644
index 0000000..7dcee2e
--- /dev/null
+++ b/tools/adpcm.h
@@ -0,0 +1,14 @@
+#ifndef _PSX_ADPCM_H
+#define _PSX_ADPCM_H
+
+enum
+{
+ FMT_U8, // unsigned 8-bit
+ FMT_S16, // signed 16-bit
+};
+
+int SsAdpcmPack(void *pcm_data, void *adpcm_data, int sample_len,
+ int sample_fmt, int adpcm_len, int enable_looping,
+ int loop_start);
+
+#endif
diff --git a/tools/bin2c.c b/tools/bin2c.c
new file mode 100644
index 0000000..a40e431
--- /dev/null
+++ b/tools/bin2c.c
@@ -0,0 +1,30 @@
+/*
+ * bin2c: converts a file to a C array of bytes
+ */
+
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ char *name = "data";
+ int n = 0;
+ int c;
+
+ if(argc >= 2)
+ name = argv[1];
+
+ printf("unsigned char %s_array[] =\n{\n", name);
+
+ while((c=getchar()) != EOF)
+ {
+ printf("%d, ", c);
+ n++;
+
+ if(!(n & 15))
+ printf("\n");
+ }
+
+ printf("};\n");
+
+ return 0;
+}
diff --git a/tools/bmp2tim.c b/tools/bmp2tim.c
new file mode 100755
index 0000000..12558b7
--- /dev/null
+++ b/tools/bmp2tim.c
@@ -0,0 +1,895 @@
+/*
+ * bmp2tim
+ *
+ * Converts a bitmap to a TIM image
+ *
+ * TEST output in various color depths... and check for issues on big-endian machines
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BMP2TIM_VERSION "0.5"
+
+enum
+{
+ BITMAPINFOHEADER,
+ BITMAPV2INFOHEADER,
+ BITMAPV3INFOHEADER,
+ BITMAPV4HEADER,
+ BITMAPV5HEADER,
+};
+
+typedef struct
+{
+ unsigned char r, g, b;
+}PS_RGB;
+
+typedef struct
+{
+ int w, h;
+ int depth;
+ // 0 = r, 1 = g, 2 = b, 3 = alpha
+ unsigned int mask[4];
+ unsigned int shift[4];
+ unsigned int bits[4];
+ unsigned char hdr_type;
+ void *data;
+}PS_BITMAP;
+
+int do_clut = 0;
+unsigned int clut_x, clut_y;
+
+unsigned int org_x = 0;
+unsigned int org_y = 0;
+
+int tim_depth;
+
+unsigned int tim_flag;
+
+int set_stp_bit = 0;
+
+int transparent_black = 0;
+int magic_pink = 0;
+int raw_flag = 0;
+
+PS_RGB *ps_default_palette;
+
+unsigned short read_le_word(FILE *f)
+{
+ unsigned char c;
+ unsigned short i;
+
+ fread(&c, sizeof(char), 1, f);
+ i = c;
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<8);
+
+ return i;
+}
+
+unsigned int read_le_dword(FILE *f)
+{
+ unsigned char c;
+ unsigned int i;
+
+ fread(&c, sizeof(char), 1, f);
+ i = c;
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<8);
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<16);
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<24);
+
+ return i;
+}
+
+
+void write_le_word(FILE *f, unsigned short leword)
+{
+ unsigned char c;
+
+ c = leword & 0xff;
+ fwrite(&c, sizeof(char), 1, f);
+ c = leword >> 8;
+ fwrite(&c, sizeof(char), 1, f);
+}
+
+void write_le_dword(FILE *f, unsigned int ledword)
+{
+ unsigned char c;
+ int x;
+
+ for(x = 0; x < 4; x++)
+ {
+ c = (ledword >> (x<<3)) & 0xff;
+ fwrite(&c, sizeof(char), 1, f);
+ }
+}
+
+PS_BITMAP *ps_create_bitmap(int w, int h, int depth)
+{
+ PS_BITMAP *bm;
+
+ bm = malloc(sizeof(PS_BITMAP));
+
+ bm->w = w;
+ bm->h = h;
+ bm->depth = depth;
+
+ switch(depth)
+ {
+ case 1:
+ bm->data = malloc((w*h)/8);
+ break;
+ case 4:
+ bm->data = malloc((w*h)/2);
+ break;
+ case 8:
+ bm->data = malloc(w*h);
+ break;
+ case 15:
+ case 16:
+ bm->data = malloc((w*h)*2);
+ break;
+ case 24:
+ bm->data = malloc((w*h)*3);
+ break;
+ case 32:
+ bm->data = malloc((w*h)*4);
+ break;
+ }
+
+ return bm;
+}
+
+void ps_destroy_bitmap(PS_BITMAP *bm)
+{
+ free(bm->data);
+ free(bm);
+}
+
+
+PS_BITMAP *ps_load_bitmap(char *filename, PS_RGB *palette)
+{
+ FILE *bf;
+ //unsigned int bsize;
+ unsigned int bisize;
+ unsigned int bwidth, bheight, bbpp, boff, blw;
+// unsigned int bcompr;
+ unsigned char *bdata;
+ PS_BITMAP *bm;
+ int x, y, z, i, l;
+
+ bf = fopen(filename, "rb");
+
+ if(bf == NULL)
+ return NULL;
+
+ if(read_le_word(bf) != 19778) // 'BM'
+ {
+ fclose(bf);
+ return NULL;
+ }
+
+ /* bsize = */ read_le_dword(bf);
+
+// Read bitmap data offset
+ fseek(bf, 10, SEEK_SET);
+ boff = read_le_dword(bf);
+
+// printf("BOFF = %d\n", boff);
+
+// Read information header size, width and height
+
+ bisize = read_le_dword(bf);
+
+
+
+ bwidth = read_le_dword(bf);
+ bheight = read_le_dword(bf);
+
+// printf("bwidth = %d, bheight = %d\n", bwidth, bheight);
+
+// Read BPP
+
+ fseek(bf, 28, SEEK_SET);
+
+ bbpp = read_le_word(bf);
+
+// Check if there is compression, if there is, abort
+
+ /* bcompr = */ read_le_dword(bf);
+// printf("BCOMPR = %d\n", bcompr);
+
+ bm = ps_create_bitmap(bwidth, bheight, bbpp);
+
+ if(palette != NULL && bm->depth <= 8)
+ {
+ fseek(bf, 14 + bisize, SEEK_SET);
+
+ if(bm->depth == 4) l = 16;
+ else if(bm->depth == 8) l = 256;
+ else if(bm->depth == 1) l = 2;
+
+ for(x=0;x<l;x++)
+ {
+ palette[x].b = fgetc(bf);
+ palette[x].g = fgetc(bf);
+ palette[x].r = fgetc(bf);
+ fgetc(bf);
+ }
+ }
+
+// nextvolume FIX 2011-07-08: Now blw (line width with padding) and bwidth
+// (line width without padding) are calculated in a much cleaner and correct manner.
+
+// printf("BPP = %d\n", bbpp);
+
+ bwidth = (bwidth * bbpp) >> 3;
+ blw = bwidth;
+ if(blw & 3) blw = (blw & ~3) + 4;
+
+ bdata = (unsigned char*)bm->data;
+
+// Bit mask and colour stuff... Added 2011-07-09
+
+ switch(bisize)
+ {
+ case 40: bm->hdr_type = BITMAPINFOHEADER; break;
+ case 52: bm->hdr_type = BITMAPV2INFOHEADER; break;
+ case 56: bm->hdr_type = BITMAPV3INFOHEADER; break;
+ case 108: bm->hdr_type = BITMAPV4HEADER; break;
+ case 124: bm->hdr_type = BITMAPV5HEADER; break;
+ }
+
+// For now clear Alpha, it will be filled only if it will be found
+
+ bm->mask[3] = 0;
+ bm->shift[3] = 0;
+ bm->bits[3] = 0;
+
+ if(bm->hdr_type == BITMAPINFOHEADER && bbpp == 16)
+ {
+ // Old header type and no bitmasks specified - force defaults
+ // X1 R5 G5 B5
+ bm->mask[2] = 0x1f;
+ bm->mask[1] = 0x1f << 5;
+ bm->mask[0] = 0x1f << 10;
+ bm->shift[2] = 0;
+ bm->shift[1] = 5;
+ bm->shift[0] = 10;
+ bm->bits[2] = 5;
+ bm->bits[1] = 5;
+ bm->bits[0] = 5;
+ }
+ else if(bm->hdr_type >= BITMAPV2INFOHEADER)
+ {
+ fseek(bf, 54, SEEK_SET);
+
+// Calculate rshift and rbits
+
+ if(bm->hdr_type >= BITMAPV3INFOHEADER)
+ l = 4;
+ else
+ l = 3;
+
+ for(i = 0; i < l; i++)
+ {
+ bm->mask[i] = read_le_dword(bf);
+
+ y = 0; // rshift
+ z = 0; // rbits
+
+ for(x = 31; x >= 0; x--)
+ {
+ if(bm->mask[i] & (1<<x))
+ {
+ y = x;
+ z++;
+ }
+ }
+
+ bm->shift[i] = y;
+ bm->bits[i] = z;
+
+ //printf("shift[%d] = %d, bits[%d] = %d\n", i, bm->shift[i],
+ // i, bm->bits[i]);
+ }
+ }
+
+// Copy data in allocated memory
+
+ for(y = 0; y < bm->h; y++)
+ {
+ fseek(bf, boff + (blw * (bm->h - (1+y))), SEEK_SET);
+
+ for(x = 0; x < bwidth; x++)
+ fread(&bdata[(y*bwidth)+x], sizeof(char), 1, bf);
+ }
+
+ fclose(bf);
+
+ return bm;
+}
+
+unsigned int ps_makecol(int r, int g, int b, int a)
+{
+ return (a<<24)|(r<<16)|(g<<8)|b;
+}
+
+unsigned int ps_getr(int c)
+{
+ return (c>>16)&0xff;
+}
+
+unsigned int ps_getg(int c)
+{
+ return (c>>8)&0xff;
+}
+
+unsigned int ps_getb(int c)
+{
+ return c&0xff;
+}
+
+unsigned int ps_geta(int c)
+{
+ return (c>>24)&0xff;
+}
+
+unsigned int ps_getpixel(PS_BITMAP *bm, int x, int y)
+{
+ unsigned short shortbuf;
+ unsigned int intbuf;
+ unsigned char r, g, b, a;
+ unsigned char *dataptrb = (unsigned char*)bm->data;
+ int off;
+
+ if(bm->depth == 16)
+ {
+ off = ((y*bm->w)+x)*2;
+
+ // Little endian, guys...
+
+ shortbuf = dataptrb[off];
+ shortbuf|= dataptrb[off+1]<<8;
+
+ b = ((shortbuf & bm->mask[2]) >> bm->shift[2]) << (8-bm->bits[2]);
+ g = ((shortbuf & bm->mask[1]) >> bm->shift[1]) << (8-bm->bits[1]);
+ r = ((shortbuf & bm->mask[0]) >> bm->shift[0]) << (8-bm->bits[0]);
+ a = ((shortbuf & bm->mask[3]) >> bm->shift[3]) << (8-bm->bits[3]);
+
+ return ps_makecol(r, g, b, a);
+ }
+ else if(bm->depth == 24)
+ {
+ // 24-bit bitmaps never have bitmasks.
+
+ off = ((y*bm->w)+x)*3;
+ r = dataptrb[off+2];
+ g = dataptrb[off+1];
+ b = dataptrb[off];
+ a = 255;
+
+ return ps_makecol(r, g, b, 255);
+ }
+ else if(bm->depth == 32)
+ {
+ off = ((y*bm->w)+x)*4;
+ /* r = dataptrb[off+3];
+ g = dataptrb[off+2];
+ b = dataptrb[off+1];*/
+ intbuf = dataptrb[off];
+ intbuf|= dataptrb[off+1]<<8;
+ intbuf|= dataptrb[off+2]<<16;
+ intbuf|= dataptrb[off+3]<<24;
+
+ r = ((intbuf & bm->mask[0]) >> bm->shift[0]) << (8-bm->bits[0]);
+ g = ((intbuf & bm->mask[1]) >> bm->shift[1]) << (8-bm->bits[1]);
+ b = ((intbuf & bm->mask[2]) >> bm->shift[2]) << (8-bm->bits[2]);
+ a = ((intbuf & bm->mask[3]) >> bm->shift[3]) << (8-bm->bits[3]);
+
+ return ps_makecol(r, g, b, a);
+ }
+ else if(bm->depth == 8)
+ {
+ r = dataptrb[(y*bm->w)+x];
+
+ return ps_makecol(ps_default_palette[r].r,
+ ps_default_palette[r].g, ps_default_palette[r].b, 255);
+ }
+ else if(bm->depth == 4)
+ {
+ off = (y*bm->w)+x;
+ off/= 2;
+
+ if(x & 1)
+ r = dataptrb[off] & 0xf;
+ else
+ r = dataptrb[off] >> 4;
+
+ return ps_makecol(ps_default_palette[r].r,
+ ps_default_palette[r].g, ps_default_palette[r].b, 255);
+ }
+ else if(bm->depth == 1)
+ {
+ off = (y*bm->w)+x;
+ off/= 8;
+
+ r = (dataptrb[off] & (1<<(7-(x&7)))) ? 1 : 0;
+
+ return ps_makecol(ps_default_palette[r].r,
+ ps_default_palette[r].g, ps_default_palette[r].b, 255);
+ }
+
+ return 0;
+}
+
+unsigned int ps_getpixel_pal(PS_BITMAP *bm, int x, int y)
+{
+ unsigned char *dataptrb = (unsigned char*)bm->data;
+ int off;
+
+ if(bm->depth == 8)
+ {
+ return dataptrb[(y*bm->w)+x];
+ }
+ else if(bm->depth == 4)
+ {
+ off = (y*bm->w)+x;
+ off/= 2;
+
+ if(x & 1)
+ return dataptrb[off] & 0xf;
+ else
+ return dataptrb[off] >> 4;
+ }
+ else if(bm->depth == 1)
+ {
+ off = (y*bm->w)+x;
+ off/= 8;
+
+ return (dataptrb[off] & (1<<(7-(x&7)))) ? 1 : 0;
+ }
+
+ return 0;
+}
+
+void parse_options(int argc, char *argv[])
+{
+ int x;
+
+ for(x=4;x<argc;x++)
+ {
+ if(strncmp("-clut=", argv[x], 6) == 0)
+ {
+ sscanf(argv[x], "-clut=%d,%d", &clut_x, &clut_y);
+ do_clut = 1;
+ }else if(strncmp("-org=", argv[x], 5) == 0)
+ sscanf(argv[x], "-org=%d,%d", &org_x, &org_y);
+ else if(strcmp("-stp", argv[x]) == 0)
+ set_stp_bit = 1;
+ else if(strcmp("-noblack", argv[x]) == 0)
+ transparent_black = 1;
+ else if(strcmp("-mpink", argv[x]) == 0)
+ magic_pink = 1;
+ else if(strcmp("-raw", argv[x]) == 0)
+ raw_flag = 1;
+ }
+}
+
+unsigned short rgb24_to_rgbpsx(unsigned char r, unsigned char g, unsigned char b)
+{
+ unsigned short c;
+
+ c = r>>3;
+ c|= (g>>3)<<5;
+ c|= (b>>3)<<10;
+
+ /*if(set_stp_bit) c|=0x8000;*/
+// this code is a bit messy, tidy it up.
+
+ if(c == 0 && !transparent_black)
+ c|=0x8000;
+
+ if(c == ((31)|(31<<10)) && magic_pink)
+ c=0;
+
+ if(set_stp_bit)
+ {
+ if(transparent_black && c == 0)
+ return c;
+
+ if(magic_pink && c == ((31)|(31<<10)))
+ return c;
+
+ c|=0x8000;
+ }
+
+ return c;
+}
+
+int main(int argc, char *argv[])
+{
+ PS_BITMAP *in_bitmap;
+ FILE *out_tim;
+ PS_RGB in_palette[256];
+ int x, y, z, c, c2;
+ unsigned short shortbuf;
+ int cx_out = 0;
+
+ ps_default_palette = in_palette;
+
+ for(x=1;x<argc;x++)
+ {
+ if(strcmp("-version", argv[x]) == 0)
+ {
+ printf("bmp2tim version "BMP2TIM_VERSION"\n");
+ return EXIT_SUCCESS;
+ }
+ }
+
+ if(argc < 4)
+ {
+ printf("bmp2tim "BMP2TIM_VERSION" - converts a bitmap to a TIM image\n");
+ printf("usage: bmp2tim <inbmp> <outtim> <depth> [options]\n\n");
+ printf("Options:\n");
+ printf(" -clut=x,y - Generate a Color Look Up Table (default: OFF)\n");
+ printf(" -org=x,y - Set image origin in framebuffer (default: 0, 0)\n");
+ printf(" -stp - Set semi transparency bit (default: BLACK ONLY)\n");
+ printf(" -noblack - Make black transparent (default: OFF)\n");
+ printf(" -mpink - Magic pink, 255,0,255 transparent (default: OFF)\n");
+ printf(" -raw - Do not save header and CLUT (default: OFF)\n");
+ printf(" -version - Print program version on screen\n\n");
+ printf("Valid TIM depths are 4 (16-color), 8 (256-color), 16 (RGB555) and 24 (RGB888)\n");
+ return EXIT_SUCCESS;
+ }
+
+ tim_depth = atoi(argv[3]);
+
+ parse_options(argc, argv);
+
+ if(do_clut && tim_depth >= 16)
+ {
+ printf("Images with depths higher than 8-bit can't have a color look up table.\n");
+ return EXIT_FAILURE;
+ }
+
+ if(clut_x & 0xf)
+ {
+ printf("The X position of the CLUT in the framebuffer must be a multiplier of 16.\n");
+ return EXIT_FAILURE;
+ }
+
+ switch(tim_depth)
+ {
+ case 4:
+ if(clut_x > (1024-16))
+ cx_out = 1;
+ break;
+ case 8:
+ if(clut_x > (1024-256))
+ cx_out = 1;
+ break;
+ }
+
+ if(cx_out)
+ {
+ printf("X position specified for CLUT out of bounds.\n");
+ return EXIT_FAILURE;
+ }
+
+ if(clut_y >= 512)
+ {
+ printf("Y position specified for CLUT out of bounds.\n");
+ return EXIT_FAILURE;
+ }
+
+ if(do_clut)
+ printf("Generating a Color Look Up Table (CLUT)\n");
+
+ if(tim_depth != 4 && tim_depth != 8 && tim_depth != 16 && tim_depth != 24)
+ {
+ printf("Invalid color depth specified!\n");
+ return EXIT_FAILURE;
+ }
+
+ in_bitmap = ps_load_bitmap(argv[1], in_palette);
+
+ if(in_bitmap == NULL)
+ {
+ printf("Unable to load bitmap. Unsupported format or file is unreadable or does not exist.\n");
+ return EXIT_FAILURE;
+ }
+
+ if(tim_depth == 4 && in_bitmap->depth > 4)
+ {
+ printf("Error: Only a 4-bit bitmap or a bitmap of lower depth can be used to obtain a 4-bit TIM!\n");
+ return EXIT_FAILURE;
+ }
+
+ if(tim_depth == 8 && in_bitmap->depth > 8)
+ {
+ printf("Error: Only a 8-bit or a bitmap of lower depth can be used to obtain a 8-bit TIM!\n");
+ return EXIT_FAILURE;
+ }
+
+/* allegro_init();
+ set_color_depth(32);
+ install_keyboard();
+ set_gfx_mode(GFX_AUTODETECT_WINDOWED, in_bitmap->w, in_bitmap->h, 0, 0);
+
+ for(y=0;y<in_bitmap->h;y++)
+ {
+ for(x=0;x<in_bitmap->w;x++)
+ {
+ c = ps_getpixel_pal(in_bitmap, x, y);
+
+ //putpixel(screen, x, y, makecol(ps_getr(c), ps_getg(c), ps_getb(c)));
+ putpixel(screen, x, y, makecol(in_palette[c].r, in_palette[c].g,
+ in_palette[c].b));
+ }
+ }
+
+ while(!key[KEY_ESC]);*/
+
+ if(in_bitmap == NULL)
+ {
+ printf("Could not open bitmap. Aborting.\n");
+ return EXIT_FAILURE;
+ }
+
+ switch(tim_depth)
+ {
+ case 4:
+ if(in_bitmap->w & 3)
+ {
+ printf("Error: A 4-bit bitmap must have a width divisible by four.\n");
+ return EXIT_FAILURE;
+ }
+
+ z = in_bitmap->w/4;
+ break;
+ case 8:
+ if(in_bitmap->w & 1)
+ {
+ printf("Error: A 8-bit bitmap must have a width divisible by two.\n");
+ return EXIT_FAILURE;
+ }
+
+ z = in_bitmap->w/2;
+ break;
+ case 16:
+ z = in_bitmap->w;
+ break;
+ }
+
+ if((org_x+z) > 1024)
+ {
+ printf("X position specified for image data out of bounds.\n");
+ return EXIT_FAILURE;
+ }
+
+ switch(tim_depth)
+ {
+ case 4:
+ z = in_bitmap->h/4;
+ break;
+ case 8:
+ z = in_bitmap->h/2;
+ break;
+ case 16:
+ z = in_bitmap->h;
+ break;
+ }
+
+ if((org_y+z) > 512)
+ {
+ printf("Y position specified for image data out of bounds.\n");
+ return EXIT_FAILURE;
+ }
+
+ out_tim = fopen(argv[2], "wb");
+
+ if(out_tim == NULL)
+ {
+ printf("Couldn't open file at path %s for writing. Aborting.\n", argv[2]);
+ return EXIT_FAILURE;
+ }
+
+ if(!raw_flag)
+ {
+
+ write_le_dword(out_tim, 0x10); /* ID = 0x10, Version = 0x00 */
+
+ /*
+ * Now let's fill the TIM flag double word
+ */
+
+ /*
+ * Pixel mode (PMODE)
+ */
+
+ switch(tim_depth)
+ {
+ case 4:
+ tim_flag = 0;
+ break;
+ case 8:
+ tim_flag = 1;
+ break;
+ case 16:
+ tim_flag = 2;
+ break;
+ case 24:
+ tim_flag = 3;
+ break;
+ }
+
+ /*
+ * Clut flag (CF)
+ */
+ //tim_flag|=8;
+ if(do_clut)tim_flag|=8;
+
+ write_le_dword(out_tim, tim_flag);
+
+ /*
+ * If we have to write a CLUT now, we have to write its data block
+ */
+
+ if(do_clut)
+ {
+ /*
+ * Let's write the information for the block - we already know
+ * everything about it.
+ */
+
+ switch(tim_depth)
+ {
+ case 4:
+ write_le_dword(out_tim, 44); // Number of bytes contained by the block
+ write_le_dword(out_tim, (clut_y<<16)|clut_x); // X, Y position
+ write_le_dword(out_tim, (1<<16)|16); // Width = 16, Height = 1
+ break;
+ case 8:
+ write_le_dword(out_tim, 524);
+ write_le_dword(out_tim, (clut_y<<16)|clut_x);
+ write_le_dword(out_tim, (1<<16)|256); // Width = 256, Height = 1
+ break;
+ }
+
+ /*
+ * Let's write the CLUT data
+ */
+
+ switch(tim_depth)
+ {
+ case 4:
+ for(x = 0; x < 16; x++)
+ {
+ shortbuf = rgb24_to_rgbpsx(in_palette[x].r, in_palette[x].g,
+ in_palette[x].b);
+
+ write_le_word(out_tim, shortbuf);
+ }
+ break;
+ case 8:
+ for(x = 0; x < 256; x++)
+ {
+ shortbuf = rgb24_to_rgbpsx(in_palette[x].r, in_palette[x].g,
+ in_palette[x].b);
+
+ write_le_word(out_tim, shortbuf);
+ }
+ break;
+ }
+ }
+
+ /*
+ * Write image data block
+ */
+
+ switch(tim_depth)
+ {
+ case 4:
+ x = 12 + ((in_bitmap->w * in_bitmap->h) / 2);
+ break;
+ case 8:
+ x = 12 + (in_bitmap->w * in_bitmap->h);
+ break;
+ case 16:
+ x = 12 + ((in_bitmap->w * in_bitmap->h) * 2);
+ break;
+ case 24:
+ x = 12 + ((in_bitmap->w * in_bitmap->h) * 3);
+ break;
+ }
+
+ write_le_dword(out_tim, x);
+ write_le_dword(out_tim, (org_y<<16)|org_x);
+
+ switch(tim_depth)
+ {
+ case 4:
+ write_le_dword(out_tim, (in_bitmap->h<<16)|(in_bitmap->w/4));
+ break;
+ case 8:
+ write_le_dword(out_tim, (in_bitmap->h<<16)|(in_bitmap->w/2));
+ break;
+ case 16:
+ write_le_dword(out_tim, (in_bitmap->h<<16)|in_bitmap->w);
+ break;
+ case 24:
+ write_le_dword(out_tim, (in_bitmap->h<<16)|(in_bitmap->w+
+ (in_bitmap->w/2)));
+ break;
+ }
+
+ }
+
+// Write image pixel data...
+
+ switch(tim_depth)
+ {
+ case 24:
+ for(y = 0; y < in_bitmap->h; y++)
+ {
+ for(x = 0; x < in_bitmap->w; x+=2)
+ {
+ c = ps_getpixel(in_bitmap, x, y);
+ c2 = ps_getpixel(in_bitmap, x+1, y);
+
+ write_le_word(out_tim, (ps_getg(c)<<8)|ps_getr(c));
+ write_le_word(out_tim, (ps_getr(c2)<<8)|ps_getb(c));
+ write_le_word(out_tim, (ps_getb(c2)<<8)|ps_getg(c2));
+ }
+ }
+ break;
+ case 16:
+ for(y = 0; y < in_bitmap->h; y++)
+ {
+ for(x = 0; x < in_bitmap->w; x++)
+ {
+ c = ps_getpixel(in_bitmap, x, y);
+ shortbuf = rgb24_to_rgbpsx(ps_getr(c), ps_getg(c), ps_getb(c));
+ write_le_word(out_tim, shortbuf);
+ }
+ }
+ break;
+ case 4:
+ for(y = 0; y < in_bitmap->h; y++)
+ {
+ for(x = 0; x < in_bitmap->w; x+=4)
+ {
+ shortbuf = 0;
+ for(c = 0; c < 4; c++)
+ shortbuf |= (ps_getpixel_pal(in_bitmap, x+c, y)&0xf) << (c<<2);
+
+ write_le_word(out_tim, shortbuf);
+ }
+ }
+ break;
+ case 8:
+ for(y = 0; y < in_bitmap->h; y++)
+ {
+ for(x = 0; x < in_bitmap->w; x+=2)
+ {
+ shortbuf = 0;
+ for(c = 0; c < 2; c++)
+ shortbuf |= (ps_getpixel_pal(in_bitmap, x+c, y)&0xff) << (c<<3);
+
+ write_le_word(out_tim, shortbuf);
+ }
+ }
+ break;
+ }
+
+ fclose(out_tim);
+ //printf("Bitmap converted to TIM file successfully!\n");
+ return EXIT_SUCCESS;
+}
diff --git a/tools/cdcat.c b/tools/cdcat.c
new file mode 100644
index 0000000..329fd17
--- /dev/null
+++ b/tools/cdcat.c
@@ -0,0 +1,373 @@
+/*
+ * cdcat: get and replace files inside an ISO-9660 CDROM image
+ * Based on cdcat by Robert Nordier
+ *
+ * Copyright (c) 2011 Giuseppe Gatta
+ *
+ * Copyright (c) 2007 Robert Nordier. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted subject to the following conditions:
+ *
+ *1. Existing copyright notices in source and other files must be
+ * retained.
+ *
+ *2. Redistributions in whatever form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define CDCAT_VERSION "0.5"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+enum
+{
+ cdrom_mode_1,
+ cdrom_mode_1_raw,
+ cdrom_mode_2,
+};
+
+enum
+{
+ cdcat_oper_read,
+ cdcat_oper_write,
+ cdcat_oper_showoffset
+};
+
+int cdcat_cdrom_mode = cdrom_mode_1;
+int cdcat_oper = cdcat_oper_read;
+
+#define SECSIZ 2048
+#define NAMLEN 255
+
+#define sw(x,y) ((x)<<8|(y))
+#define cv4(x) ((*(unsigned char*)(x)) | ((*(unsigned char*)(x+1))<<8) | \
+ ((*(unsigned char*)(x+2))<<16) | ((*(unsigned char*)(x+3))<<24))
+
+/* ISO 9660 Primary Volume Descriptor */
+static char cdmagic[] = {1, 'C', 'D', '0', '0', '1', 1, 0};
+
+struct cddir {
+ unsigned char len_dr; /* length of directory record */
+ unsigned char len_ear; /* extended attribute record length */
+ unsigned char ext[8]; /* location of extent */
+ unsigned char size[8]; /* data length */
+ unsigned char time[7]; /* recording date and time */
+ unsigned char flags; /* file flags */
+ unsigned char fus; /* file unit size */
+ unsigned char gap; /* interleave gap size */
+ unsigned char vsn[4]; /* volume sequence number */
+ unsigned char len_fi; /* length of file identifier */
+ unsigned char fi[1]; /* file identifier ... */
+};
+
+struct dir {
+ unsigned int ext; /* starting block number */
+ unsigned int size; /* file size */
+ int type; /* file type ('d' or '-') */
+ char name[NAMLEN + 1]; /* file name */
+};
+
+static char *fn; /* special file name */
+static int fd; /* special file descriptor */
+
+int cdcat(char *);
+void loaddir(struct cddir *, struct dir *);
+void susp(unsigned char *, int, struct dir *);
+int readblk(void *, unsigned int);
+void writeblk(void *, unsigned int);
+void error(char *);
+
+void cdcat_print_usage()
+{
+ fprintf(stderr, "usage: cdcat <options> iso-image [path]\n");
+ fprintf(stderr, "\n"
+ "cdcat can be used to explore an ISO9660 filesystem image\n"
+ "If path is not specified the root directory is listed, if it's a directory "
+ "the directory is listed or if it's a file the file is printed to standard output\n"
+ "The Rock Ridge extensions to the ISO9660 filesystem standard are supported\n"
+ "Original program (c) 2007 Robert Nordier.\n"
+ "\n"
+ "Options:\n"
+ "-help - This screen\n"
+ "-mode1 - The image is raw and has Mode 1 sectors\n"
+ "-mode2 - The image is raw and has Mode 2 sectors (like PlayStation dumps)\n"
+ "-replace - If [path] is specified, the data of the file is\n"
+ " replaced with input from standard input\n"
+ "-showoffset - Show file offset in image\n"
+ "-version - Display version\n\n");
+}
+
+int main(int argc, char **argv)
+{
+ char *path;
+ int e;
+ int nargc;
+ int x;
+
+ for(x = 1; x < argc; x++)
+ {
+ if(strcmp(argv[x], "--") == 0 || argv[x][0] != '-')
+ break;
+
+ if(strcmp(argv[x], "-mode2") == 0 || strcmp(argv[x], "--mode2") == 0)
+ cdcat_cdrom_mode = cdrom_mode_2;
+ else if(strcmp(argv[x], "-mode1") == 0 || strcmp(argv[x], "--mode1") == 0)
+ cdcat_cdrom_mode = cdrom_mode_1_raw;
+ else if(strcmp(argv[x], "--help") == 0 || strcmp(argv[x], "-help") == 0)
+ {
+ cdcat_print_usage();
+ return 0;
+ }
+ else if(strcmp(argv[x], "--version") == 0 || strcmp(argv[x], "-version") == 0)
+ {
+ printf("cdcat version "CDCAT_VERSION"\n");
+ return 0;
+ }
+ else if(strcmp(argv[x], "--replace") == 0 || strcmp(argv[x], "-replace") == 0)
+ cdcat_oper = cdcat_oper_write;
+ else if(strcmp(argv[x], "--showoffset") == 0 || strcmp(argv[x], "-showoffset") == 0)
+ cdcat_oper = cdcat_oper_showoffset;
+ else
+ {
+ printf("Invalid option %s! Aborting.\n", argv[x]);
+ return -1;
+ }
+ }
+
+ nargc = argc-(x-1);
+
+ if (nargc != 2 && nargc != 3) {
+ cdcat_print_usage();
+ exit(2);
+ }
+
+ fn = argv[x];
+
+ if ((fd = open(argv[x], cdcat_oper==cdcat_oper_read?O_RDONLY:O_RDWR)) == -1)
+ error("cannot open");
+ path = argv[x+1] ? argv[x+1] : "";
+
+ if ((e = cdcat(path)) != 0)
+ fprintf(stderr, "cdcat: %s: Not found\n", path);
+ return e;
+}
+
+int cdcat(char *path)
+{
+ unsigned char buf[SECSIZ];
+ char name[NAMLEN + 1];
+ struct cddir *dp, *tp;
+ struct dir xd;
+ char *p, *q;
+ unsigned ext, size, bx, bn, x, i;
+ int type, n;
+
+ /*
+ * find primary volume descriptor
+ * and thence root directory
+ */
+ bx = 64;
+ for (bn = 16; bn < bx; bn++) {
+ readblk(buf, bn);
+ if (strcmp((char *)buf, cdmagic) == 0)
+ break;
+ }
+ if (bn == bx)
+ error("Invalid argument");
+ dp = (struct cddir *)&buf[156];
+ loaddir(dp, &xd);
+
+ /*
+ * lookup, list, print ...
+ */
+ for (p = path; dp; p = q) {
+ while (*p == '/')
+ p++;
+ for (q = p; *q && *q != '/'; q++);
+ if ((n = q - p)) {
+ if (n > NAMLEN)
+ n = NAMLEN;
+ memcpy(name, p, n);
+ name[n] = 0;
+ }
+ ext = xd.ext;
+ size = xd.size;
+ type = xd.type;
+ dp = NULL;
+ bx = ext + (size + (SECSIZ - 1)) / SECSIZ;
+ for (bn = ext; !dp && bn < bx; bn++) {
+ if (type == 'd')
+ {
+ readblk(buf, bn);
+
+ for (i = 0; !dp && buf[i]; i += buf[i]) {
+ tp = (struct cddir *)(buf + i);
+ loaddir(tp, &xd);
+ if (n == 0)
+ printf("%10u %c %s\n",
+ xd.size, xd.type, xd.name);
+ else if (strcmp(name, xd.name) == 0)
+ dp = tp;
+ }
+ }
+ else {
+ if(cdcat_oper == cdcat_oper_read)
+ {
+ readblk(buf, bn);
+ x = size < SECSIZ ? size : SECSIZ;
+ for (i = 0; i < x; i++)
+ putchar(buf[i]);
+ size -= x;
+ }else if(cdcat_oper == cdcat_oper_write)
+ {
+ x = size < SECSIZ ? size : SECSIZ;
+ for (i = 0; i < x; i++)
+ buf[i] = getchar();
+
+ writeblk(buf, bn);
+
+ size -= x;
+ }else if(cdcat_oper == cdcat_oper_showoffset)
+ {
+ printf("%d\n", readblk(buf, bn));
+ goto cdcat_end;
+ }
+ }
+ }
+ }
+cdcat_end:
+ return n != 0;
+}
+
+/*
+ * Gather together the directory information that interests us.
+ * Any and all of this may be altered by a suitable SUSP field.
+ */
+void loaddir(struct cddir *dp, struct dir *xp)
+{
+ int c;
+
+ xp->ext = cv4(dp->ext);
+ xp->size = cv4(dp->size);
+ xp->type = dp->flags & 2 ? 'd' : '-';
+ xp->name[0] = 0;
+ if (dp->fi[0] != 0) {
+ c = dp->len_fi | 1;
+ susp(dp->fi + c, dp->len_dr - 33 - c, xp);
+ }
+ if (xp->name[0] == 0)
+ {
+ if (dp->fi[0] == 0 || dp->fi[0] == 1)
+ strcpy(xp->name, dp->fi[0] == 0 ? "." : "..");
+ else
+ {
+ memcpy(xp->name, dp->fi, dp->len_fi);
+ xp->name[dp->len_fi] = 0;
+ }
+ }
+}
+
+/*
+ * SUSP/RRIP support: allowing UNIX-style file names and directories
+ * nested more than eight deep (among other things).
+ */
+void susp(unsigned char *sp, int n, struct dir *xp)
+{
+ unsigned char buf[SECSIZ];
+ unsigned char *p;
+ int i, j;
+
+ for (p = sp; p < sp + n && *p;) {
+ if (p[3] != 1)
+ return;
+ switch (sw(p[0], p[1])) {
+ /* continuation area */
+ case sw('C', 'E'):
+ readblk(buf, cv4(&p[4]));
+ sp = buf + cv4(&p[12]);
+ n = cv4(&p[20]);
+ p = sp;
+ continue;
+ /* child link */
+ case sw('C', 'L'):
+ xp->ext = cv4(&p[4]);
+ xp->size = SECSIZ;
+ xp->type = 'd';
+ break;
+ /* alternate name */
+ case sw('N', 'M'):
+ for (j = 0; xp->name[j]; j++);
+ for (i = 5; i < p[2]; i++)
+ xp->name[j++] = p[i];
+ xp->name[j] = 0;
+ break;
+ }
+ p += p[2];
+ }
+}
+
+int readblk(void *buf, unsigned int blkno)
+{
+ int r;
+
+ switch(cdcat_cdrom_mode)
+ {
+ case cdrom_mode_1:
+ r = lseek(fd, blkno * SECSIZ, 0);
+ break;
+ case cdrom_mode_1_raw:
+ r = lseek(fd, (blkno * 2352) + 16, 0);
+ break;
+ case cdrom_mode_2:
+ r = lseek(fd, (blkno * 2352) + 24, 0);
+ break;
+ }
+
+ if (read(fd, buf, SECSIZ) != SECSIZ)
+ error("read error");
+
+ return r;
+}
+
+void writeblk(void *buf, unsigned int blkno)
+{
+ switch(cdcat_cdrom_mode)
+ {
+ case cdrom_mode_1:
+ lseek(fd, blkno * SECSIZ, 0);
+ break;
+ case cdrom_mode_1_raw:
+ lseek(fd, (blkno * 2352) + 16, 0);
+ break;
+ case cdrom_mode_2:
+ lseek(fd, (blkno * 2352) + 24, 0);
+ break;
+ }
+
+ if (write(fd, buf, SECSIZ) != SECSIZ)
+ error("write error");
+}
+
+void error(char *msg)
+{
+ fprintf(stderr, "cdcat: %s: %s\n", fn, msg);
+ exit(2);
+}
diff --git a/tools/elf2exe.c b/tools/elf2exe.c
new file mode 100755
index 0000000..89e2c6d
--- /dev/null
+++ b/tools/elf2exe.c
@@ -0,0 +1,231 @@
+/*
+ * elf2exe
+ *
+ * Converts an ELF executable to PS-EXE, using objcopy
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+const unsigned char psexe_magic[8] = {'P','S','-','X',' ','E','X','E'};
+const char *psexe_marker_usa = "Sony Computer Entertainment Inc. for North America area";
+const char *psexe_marker_jpn = "Sony Computer Entertainment Inc. for Japan area";
+const char *psexe_marker_eur = "Sony Computer Entertainment Inc. for Europe area";
+char *psexe_marker;
+
+//#define OBJCOPY_PATH "mipsel-unknown-elf-objcopy"
+
+int main(int argc, char *argv[])
+{
+ FILE *objcopy_out, *psexe;
+ char stringbuf[2048];
+ unsigned char charbuf;
+ int x;
+ unsigned int sz;
+ unsigned int gp = 0;
+
+ if(argc < 3)
+ {
+ printf("elf2exe - Converts an ELF executable to PS-EXE\n");
+ printf("usage: elf2exe [elf] [ps-x exe] <options>\n");
+ printf("\n");
+ printf("Options:\n");
+ printf("-mark_jpn - Use Japanese ascii marker (default: USA)\n");
+ printf("-mark_eur - Use European ascii marker (default: USA)\n");
+ printf("-mark=<mark> - Use custom ascii marker <mark>\n");
+ return -1;
+ }
+
+ psexe_marker = (char*)psexe_marker_usa;
+
+ for(x = 3; x < argc; x++)
+ {
+ if(strcmp(argv[x], "-mark_jpn") == 0)
+ psexe_marker = (char*)psexe_marker_jpn;
+
+ if(strcmp(argv[x], "-mark_eur") == 0)
+ psexe_marker = (char*)psexe_marker_eur;
+
+ if(strncmp(argv[x], "-mark=", 6) == 0)
+ {
+ if(strlen(argv[x]) >= 7)
+ psexe_marker = argv[x] + 6;
+ }
+
+ if(strncmp(argv[x], "-gp=", 4) == 0)
+ {
+ if(strlen(argv[x]) >= 5)
+ sscanf(argv[x] + 4, "%x", &gp);
+ }
+ }
+
+/*
+ * Now open the output file
+ */
+
+ psexe = fopen(argv[2], "wb");
+
+ if(psexe == NULL)
+ {
+ printf("Couldn't open %s for writing. Aborting!\n", argv[2]);
+ return -1;
+ }
+
+/*
+ * Write PSEXE magic string
+ */
+ fwrite(psexe_magic, sizeof(char), 8, psexe);
+
+/*
+ * Seek output file to 0x10, Initial Program Counter
+ */
+ fseek(psexe, 0x10, SEEK_SET);
+
+/*
+ * Write initial program counter = 0x80010000
+ */
+ charbuf = 0x00;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x01;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x80;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+/*
+ * Global Pointer
+ */
+ charbuf = gp & 0xff;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (gp & 0xff00) >> 8;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (gp & 0xff0000) >> 16;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (gp & 0xff000000) >> 24;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+/*
+ * Seek output file to 0x18, Text section start address
+ */
+ fseek(psexe, 0x18, SEEK_SET);
+
+/*
+ * Write text section start address = 0
+ */
+ charbuf = 0x00;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x00;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x01;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x80;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+/*
+ * Seek output file to 0x30, Initial Stack Pointer
+ */
+ fseek(psexe, 0x30, SEEK_SET);
+
+/*
+ * Write Initial Stack Pointer = 0x801FFFF0
+ */
+ charbuf = 0xF0;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0xFF;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x1F;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = 0x80;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+
+/*
+ * Seek output to 0x4C, ASCII marker
+ */
+ fseek(psexe, 0x4C, SEEK_SET);
+
+ x = 0;
+
+/*
+ * Write ASCII marker string
+ */
+ while(psexe_marker[x])
+ fwrite(&psexe_marker[x++], sizeof(char), 1, psexe);
+
+/*
+ * Run objcopy now
+ */
+ sprintf(stringbuf, OBJCOPY_PATH" -O binary %s %s.bin", argv[1], argv[1]);
+ system(stringbuf);
+
+ sprintf(stringbuf, "%s.bin", argv[1]);
+
+/*
+ * Open objcopy output
+ */
+
+ objcopy_out = fopen(stringbuf, "rb");
+ if(objcopy_out == NULL)
+ {
+ printf("Could not open objcopy output at %s. Check your permissions. Aborting.\n",
+ stringbuf);
+ return -1;
+ }
+
+/*
+ * Seek to 0x800, Program Start
+ * and write the output of objcopy into the PS-X EXE
+ */
+ fseek(psexe, 0x800, SEEK_SET);
+
+ while(!feof(objcopy_out))
+ {
+ x = fgetc(objcopy_out);
+ fputc(x, psexe);
+ }
+
+
+ fclose(objcopy_out);
+
+/*
+ * Get the file size of the PS-X EXE
+ */
+ fseek(psexe, 0, SEEK_END);
+ sz = ftell(psexe);
+ fseek(psexe, 0, SEEK_SET);
+
+ if(sz % 2048 != 0)
+ {
+ fseek(psexe, (((sz / 2048) + 1)*2048) - 1, SEEK_SET);
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ sz = ftell(psexe);
+ }
+
+/*
+ * Write the address of the text section in the header of the PS-X EXE
+ */
+
+ sz -= 0x800;
+
+ fseek(psexe, 0x1C, SEEK_SET);
+
+ charbuf = sz & 0xff;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (sz & 0xff00) >> 8;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (sz & 0xff0000) >> 16;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+ charbuf = (sz & 0xff000000) >> 24;
+ fwrite(&charbuf, sizeof(char), 1, psexe);
+
+ fclose(psexe);
+
+/*
+ * Remove objcopy output
+ */
+ sprintf(stringbuf, "%s.bin", argv[1]);
+ remove(stringbuf);
+
+ return 0;
+}
diff --git a/tools/endian.c b/tools/endian.c
new file mode 100644
index 0000000..333390f
--- /dev/null
+++ b/tools/endian.c
@@ -0,0 +1,52 @@
+unsigned short read_le_word(FILE *f)
+{
+ unsigned char c;
+ unsigned short i;
+
+ fread(&c, sizeof(char), 1, f);
+ i = c;
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<8);
+
+ return i;
+}
+
+unsigned int read_le_dword(FILE *f)
+{
+ unsigned char c;
+ unsigned int i;
+
+ fread(&c, sizeof(char), 1, f);
+ i = c;
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<8);
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<16);
+ fread(&c, sizeof(char), 1, f);
+ i|=(c<<24);
+
+ return i;
+}
+
+
+void write_le_word(FILE *f, unsigned short leword)
+{
+ unsigned char c;
+
+ c = leword & 0xff;
+ fwrite(&c, sizeof(char), 1, f);
+ c = leword >> 8;
+ fwrite(&c, sizeof(char), 1, f);
+}
+
+void write_le_dword(FILE *f, unsigned int ledword)
+{
+ unsigned char c;
+ int x;
+
+ for(x = 0; x < 4; x++)
+ {
+ c = (ledword >> (x<<3)) & 0xff;
+ fwrite(&c, sizeof(char), 1, f);
+ }
+}
diff --git a/tools/exefixup.c b/tools/exefixup.c
new file mode 100644
index 0000000..7f555dc
--- /dev/null
+++ b/tools/exefixup.c
@@ -0,0 +1,204 @@
+/*
+ * exefixup.c v0.02.1 Andrew Kieschnick <andrewk@mail.utexas.edu>
+ * (v0.02.1): Giuseppe Gatta <tails92@gmail.com>
+ *
+ * v0.02.1 changes: removed warnings
+ *
+ * displays PS-X EXE header information
+ * offers to fix incorrect t_size
+ * offers to pad to 2048-byte boundary for cd-rom use
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+unsigned int char2int(unsigned char *foo)
+{
+ return foo[3]*16777216 + foo[2]*65536 + foo[1]*256 + foo[0];
+}
+
+void int2char(unsigned int foo, unsigned char *bar)
+{
+ bar[3]=foo>>24;
+ bar[2]=foo>>16;
+ bar[1]=foo>>8;
+ bar[0]=foo;
+}
+
+void usage(void)
+{
+ printf("Usage: exefixup <filename>\n\n");
+ printf("\t<filename>\ta PS-X EXE file\n\n");
+ printf("\tdisplays EXE header\n");
+ printf("\toffers to correct a wrong t_size\n");
+ printf("\toffers to pad to 2048-byte boundary\n\n");
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ FILE *exe;
+ FILE *out;
+ unsigned char data[8];
+ char filename[256];
+ int i;
+ unsigned int header_data[12];
+ unsigned int size;
+ unsigned int padsize;
+ signed char yesno='Z';
+
+ printf("exefixup v0.02.1 Andrew Kieschnick <andrewk@mail.utexas.edu>\n\n");
+
+ if (argc!=2)
+ usage();
+
+ strncpy(filename,argv[1],256);
+
+ exe=fopen(filename, "r");
+
+ strcat(filename, "-fixed"); /* output filename is same as input filename, but with -fix appended */
+
+ if (!exe)
+ {
+ printf("ERROR: Can't open %s\n",filename);
+ exit(-1);
+ }
+
+ for(i=0;i<8;i++)
+ fscanf(exe, "%c", &data[i]);
+ data[8]=0;
+
+ if (strncmp((char*)data, "PS-X EXE", 8))
+ {
+ printf("ERROR: Not a PS-X EXE file\n");
+ exit(-1);
+ }
+
+ for(i=0;i<12;i++)
+ {
+ fscanf(exe, "%c", &data[0]);
+ fscanf(exe, "%c", &data[1]);
+ fscanf(exe, "%c", &data[2]);
+ fscanf(exe, "%c", &data[3]);
+ header_data[i]=char2int(data);
+ }
+
+ printf("id\tPS-X EXE\n");
+ printf("text\t0x%.8x\n", header_data[0]);
+ printf("data\t0x%.8x\n", header_data[1]);
+ printf("pc0\t0x%.8x\n", header_data[2]);
+ printf("gp0\t0x%.8x\n", header_data[3]);
+ printf("t_addr\t0x%.8x\n", header_data[4]);
+ printf("t_size\t0x%.8x\n", header_data[5]);
+ printf("d_addr\t0x%.8x\n", header_data[6]);
+ printf("d_size\t0x%.8x\n", header_data[7]);
+ printf("b_addr\t0x%.8x\n", header_data[8]);
+ printf("b_size\t0x%.8x\n", header_data[9]);
+ printf("s_addr\t0x%.8x\n", header_data[10]);
+ printf("s_size\t0x%.8x\n\n", header_data[11]);
+
+ fseek(exe, 0, SEEK_END);
+
+ size=ftell(exe)-2048;
+
+ padsize=2048-(size%2048);
+
+ if (padsize!=2048)
+ {
+ printf("WARNING: EXE size is not a multiple of 2048!\n");
+ while ((yesno!='Y')&&(yesno!='N'))
+ {
+ printf("Write a padded EXE (to %s) ? ",filename);
+ scanf("%c%*c", &yesno);
+ yesno=toupper(yesno);
+ }
+ if (yesno=='Y')
+ {
+ out = fopen(filename, "w");
+
+ header_data[5]=size+padsize;
+
+ fprintf(out, "PS-X EXE");
+ for(i=0;i<12;i++)
+ {
+ int2char(header_data[i], data);
+ fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]);
+ }
+
+ fseek(exe, 56, SEEK_SET);
+
+ for(i=0;i<size+1992;i++)
+ {
+ fscanf(exe, "%c", &data[0]);
+ fprintf(out, "%c", data[0]);
+ }
+ for(i=0;i<padsize;i++)
+ fprintf(out, "%c", 0);
+
+ size=header_data[5];
+ fclose(out);
+ }
+ }
+
+ yesno='Z';
+
+ if (size!=header_data[5])
+ {
+ printf("WARNING: EXE header t_size does not match filesize-2048\n");
+ printf("EXE header:\t 0x%.8x bytes\n", header_data[5]);
+ printf("filesize-2048:\t 0x%.8x bytes\n", size);
+ while ((yesno!='Y')&&(yesno!='N'))
+ {
+ printf("Write a corrected EXE (to %s) ? ",filename);
+ scanf("%c%*c", &yesno);
+ yesno=toupper(yesno);
+ }
+ if (yesno=='Y')
+ {
+ out = fopen(filename, "w");
+
+ fprintf(out, "PS-X EXE");
+ for(i=0;i<5;i++)
+ {
+ int2char(header_data[i], data);
+ fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]);
+ }
+ int2char(size, data);
+ fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]);
+ for(i=6;i<12;i++)
+ {
+ int2char(header_data[i], data);
+ fprintf(out, "%c%c%c%c", data[0], data[1], data[2], data[3]);
+ }
+
+ fseek(exe, 56, SEEK_SET);
+
+ for(i=0;i<size+1992;i++)
+ {
+ fscanf(exe, "%c", &data[0]);
+ fprintf(out, "%c", data[0]);
+ }
+ fclose(out);
+ }
+ }
+ fclose(exe);
+ return 0;
+}
diff --git a/tools/getpsxiso.c b/tools/getpsxiso.c
new file mode 100644
index 0000000..76cfc72
--- /dev/null
+++ b/tools/getpsxiso.c
@@ -0,0 +1,55 @@
+// Converts a bin suitable for burning for the PlayStation
+// to an ISO by getting only the 2048 data bytes of each sector
+// The reverse of mkpsxiso
+
+// Written by Giuseppe Gatta, 2010
+
+#include <stdio.h>
+
+int main(int argc, char *argv[])
+{
+ FILE *i, *o;
+ int x, s;
+ char buf[2352];
+
+ if (argc < 3)
+ {
+ printf("getpsxiso <input> <output>\n");
+ return -1;
+ }
+
+ i = fopen(argv[1], "rb");
+
+ if(i == NULL)
+ {
+ printf("Could not open specified input file.\n");
+ return -1;
+ }
+
+ fseek(i, 0, SEEK_END);
+ s = ftell(i) / 2352;
+ fseek(i, 0, SEEK_SET);
+
+ if(s % 2352 == 0)
+ {
+ printf("Input file size not a multiplier of 2352.\n");
+ printf("Aborting.\n");
+ return -1;
+ }
+
+ o = fopen(argv[2], "wb");
+
+ for(x = 0; x < s; x++)
+ {
+ fread(buf, sizeof(char), 2352, i);
+ fwrite(buf + 24, sizeof(char), 2048, o);
+ printf("Sector %d/%d written\r", x+1, s);
+ }
+
+ printf("\n");
+
+ fclose(i);
+ fclose(o);
+
+ return 0;
+}
diff --git a/tools/huff.c b/tools/huff.c
new file mode 100644
index 0000000..0e28fc5
--- /dev/null
+++ b/tools/huff.c
@@ -0,0 +1,555 @@
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define BUFFER_SIZE 1024
+
+struct TreeNode {
+ unsigned char value;
+ unsigned long freq;
+ struct TreeNode *left;
+ struct TreeNode *right;
+};
+
+struct CodeNode {
+ unsigned char value;
+ unsigned long code;
+ unsigned int codeSize;
+};
+
+unsigned long table[256];
+unsigned char value[256];
+unsigned long freq[256];
+int tableSize;
+
+struct CodeNode codes[256];
+unsigned int codesUsed;
+
+FILE *inFile;
+unsigned long inputSize;
+
+FILE *outFile;
+
+struct TreeNode *root;
+
+char inBuffer[BUFFER_SIZE];
+char outBuffer[BUFFER_SIZE];
+
+void CreateTable();
+void SiftHeap(struct TreeNode**, unsigned int, unsigned int);
+void SortTrees(struct TreeNode**, unsigned int);
+void DestroyTree(struct TreeNode *);
+void CreateTree();
+void GenerateCodes(struct TreeNode*, unsigned long, unsigned long);
+void SortCodes();
+void Compress();
+void Decompress();
+void DisplayHelp();
+int main(int, char**);
+
+//////////////////////////////////////////////////////////////////////////
+// Create the character frequency table for the file
+//////////////////////////////////////////////////////////////////////////
+void CreateTable() {
+ int x, y;
+ unsigned long maxCount;
+ unsigned char maxIndex;
+ unsigned char ch;
+
+ for(x = 0; x < 256; x++) {
+ value[x] = 0;
+ freq[x] = 0;
+ }
+
+ rewind(inFile);
+ tableSize = 0;
+ for(;;) {
+ ch = fgetc(inFile);
+ if(feof(inFile)) {
+ break;
+ }
+ ++inputSize;
+ if(table[ch] == 0) {
+ ++tableSize;
+ }
+ ++table[ch];
+ }
+
+ y = tableSize;
+ do {
+ --y;
+ maxIndex = 0;
+ maxCount = 0;
+ for(x = 0; x < 256 ; x++) {
+ if(table[x] > maxCount) {
+ maxIndex = x;
+ maxCount = table[(unsigned int)maxIndex];
+ }
+ }
+ freq[y] = maxCount;
+ value[y] = maxIndex;
+ table[(unsigned int)maxIndex] = 0;
+ } while(y > 0);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Max-heapify (for sorting)
+//////////////////////////////////////////////////////////////////////////
+void SiftHeap(struct TreeNode **trees, unsigned int x, unsigned int size) {
+ struct TreeNode *root;
+ int finished;
+ unsigned int y;
+
+ root = trees[x - 1];
+ y = x << 1;
+
+ finished = (y > size);
+ while(!finished) {
+ if(y < size) {
+ if(trees[y + 1 - 1]->freq > trees[y - 1]->freq) {
+ ++y;
+ }
+ }
+ if(trees[y - 1]->freq > root->freq) {
+ trees[x - 1] = trees[y - 1];
+ x = y;
+ y = x << 1;
+ finished = (y > size);
+ } else {
+ finished = 1;
+ }
+ }
+ trees[x - 1] = root;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sort the trees in ascending order by frequency
+//////////////////////////////////////////////////////////////////////////
+void SortTrees(struct TreeNode **trees, unsigned int num) {
+ struct TreeNode *temp;
+ unsigned int x;
+
+ for(x = num >> 1; x > 1; x--) {
+ SiftHeap(trees, x, num);
+ }
+
+ for(x = num; x > 1; x--) {
+ SiftHeap(trees, 1, x);
+ temp = trees[1 - 1];
+ trees[1 - 1] = trees[x - 1];
+ trees[x - 1] = temp;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Release the memory used by the tree
+//////////////////////////////////////////////////////////////////////////
+void DestroyTree(struct TreeNode *ptr) {
+ if(ptr->right) {
+ DestroyTree(ptr->right);
+ }
+ if(ptr->left) {
+ DestroyTree(ptr->left);
+ }
+ free(ptr);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Create the huffman coding tree
+//////////////////////////////////////////////////////////////////////////
+void CreateTree() {
+ struct TreeNode *ptr;
+ struct TreeNode *trees[257];
+ int x, y;
+
+ for(x = 0; x < tableSize; x++) {
+ trees[x] = malloc(sizeof(struct TreeNode));
+ trees[x]->right = 0;
+ trees[x]->left = 0;
+ trees[x]->value = value[x];
+ trees[x]->freq = freq[x];
+ }
+
+ y = tableSize;
+ while(y > 1) {
+ // Combine two smallest nodes into a tree
+ ptr = malloc(sizeof(struct TreeNode));
+ ptr->right = trees[0];
+ ptr->left = trees[1];
+ ptr->freq = trees[0]->freq + trees[1]->freq;
+ ptr->value = 0;
+ trees[0] = ptr;
+ for(x = 1; x < y - 1; x++) {
+ trees[x] = trees[x + 1];
+ }
+ trees[y] = 0;
+ SortTrees(trees, y - 1); // account for the zero
+ --y;
+ }
+
+ root = trees[0];
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Generate code table from the tree
+//////////////////////////////////////////////////////////////////////////
+void GenerateCodes(struct TreeNode *ptr, unsigned long code,
+ unsigned long codeSize) {
+ if(ptr->right) {
+ GenerateCodes(ptr->right, (code << 1), codeSize + 1);
+ GenerateCodes(ptr->left, (code << 1) | 1, codeSize + 1);
+ } else {
+ codes[codesUsed].value = ptr->value;
+ codes[codesUsed].code = code;
+ codes[codesUsed].codeSize = codeSize;
+ ++codesUsed;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Sort the codes in ascending order by size
+// Used for compression
+//////////////////////////////////////////////////////////////////////////
+void SortCodes() {
+ int x, y;
+ struct CodeNode temp;
+
+ for(x = 0; x < codesUsed; x++) {
+ for(y = x; y < codesUsed; y++) {
+ if(codes[x].codeSize > codes[y].codeSize) {
+ temp = codes[x];
+ codes[x] = codes[y];
+ codes[y] = temp;
+ }
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Compress the file
+//////////////////////////////////////////////////////////////////////////
+void Compress() {
+ int x, y;
+ unsigned char temp;
+ unsigned char ch;
+ int offset;
+ int ib, ob;
+ int inBufferSize;
+
+ CreateTable();
+ CreateTree();
+ codesUsed = 0;
+ GenerateCodes(root, 0, 0);
+ DestroyTree(root);
+ SortCodes();
+
+// [nextvolume]: Values are now saved as 32-bit little endian instead of ASCII numbers
+
+ fputc(codesUsed & 0xff, outFile);
+ fputc((codesUsed >> 8) & 0xff, outFile);
+ fputc((codesUsed >> 16) & 0xff, outFile);
+ fputc((codesUsed >> 24) & 0xff, outFile);
+
+ fputc(inputSize & 0xff, outFile);
+ fputc((inputSize >> 8) & 0xff, outFile);
+ fputc((inputSize >> 16) & 0xff, outFile);
+ fputc((inputSize >> 24) & 0xff, outFile);
+
+ for(x = 0; x < codesUsed; x++) {
+ fputc(codes[x].value, outFile);
+ fputc(codes[x].codeSize - 1, outFile);
+ }
+
+ offset = 7;
+ temp = 0;
+ for(x = 0; x < codesUsed; x++) {
+ for(y = codes[x].codeSize - 1; y >= 0; y--) {
+ temp |= ((codes[x].code >> y) & 1) << offset;
+ --offset;
+ if(offset < 0) {
+ fputc(temp, outFile);
+ offset = 7;
+ temp = 0;
+ }
+ }
+ }
+ if(offset != 7) {
+ fputc(temp, outFile);
+ }
+
+ offset = 7;
+ temp = 0;
+ rewind(inFile);
+ ib = BUFFER_SIZE;
+ ob = 0;
+ inBufferSize = 0;
+ for(;;) {
+ if(ib >= BUFFER_SIZE) {
+ inBufferSize = fread(inBuffer, sizeof(char),
+ BUFFER_SIZE, inFile);
+ ib = 0;
+ }
+ if(ib >= inBufferSize) {
+ break;
+ }
+ ch = inBuffer[ib++];
+ for(x = 0; x < codesUsed; x++) {
+ if(ch == codes[x].value) {
+ break;
+ }
+ }
+ for(y = codes[x].codeSize - 1; y >= 0; y--) {
+ temp |= ((codes[x].code >> y) & 1) << offset;
+ --offset;
+ if(offset < 0) {
+ outBuffer[ob++] = temp;
+ if(ob >= BUFFER_SIZE) {
+ fwrite(outBuffer, sizeof(char),
+ BUFFER_SIZE, outFile);
+ ob = 0;
+ }
+ temp = 0;
+ offset = 7;
+ }
+ }
+ }
+ if(offset != 7) {
+ outBuffer[ob++] = temp;
+ }
+ if(ob) {
+ fwrite(outBuffer, sizeof(char), ob, outFile);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Decompress the file
+//////////////////////////////////////////////////////////////////////////
+void Decompress() {
+ int x, y;
+ unsigned int dataSize;
+ unsigned long mask, maskSize;
+ unsigned char ch;
+ int offset;
+// int last;
+
+ int ib, ob;
+
+ ib = fgetc(inFile);
+ codesUsed = ib;
+ ib = fgetc(inFile);
+ codesUsed |= ib << 8;
+ ib = fgetc(inFile);
+ codesUsed |= ib << 16;
+ ib = fgetc(inFile);
+ codesUsed |= ib << 24;
+
+ ib = fgetc(inFile);
+ dataSize = ib;
+ ib = fgetc(inFile);
+ dataSize |= ib << 8;
+ ib = fgetc(inFile);
+ dataSize |= ib << 16;
+ ib = fgetc(inFile);
+ dataSize |= ib << 24;
+
+ for(x = 0; x < codesUsed; x++) {
+ codes[x].value = fgetc(inFile);
+ codes[x].codeSize = fgetc(inFile) + 1;
+ }
+
+ offset = 7;
+ ch = 0;
+ for(x = 0; x < codesUsed; x++) {
+ codes[x].code = 0;
+ for(y = codes[x].codeSize - 1; y >= 0; y--) {
+ if(offset == 7) {
+ ch = fgetc(inFile);
+ }
+ codes[x].code |= ((ch >> offset) & 1) << y;
+ offset = (offset - 1) & 7;
+ }
+ }
+
+ maskSize = 0;
+ mask = 0;
+ offset = 7;
+// last = 0;
+ y = 0;
+ x = 0;
+ ib = BUFFER_SIZE;
+ ob = 0;
+ for(;;) {
+ if(offset == 7) {
+ if(ib >= BUFFER_SIZE) {
+ fread(inBuffer, sizeof(char), BUFFER_SIZE,
+ inFile);
+ ib = 0;
+ }
+ ch = inBuffer[ib++];
+ }
+ mask <<= 1;
+ mask |= (ch >> offset) & 1;
+ ++maskSize;
+ offset = (offset - 1) & 7;
+
+ while(codes[y].codeSize < maskSize) ++y;
+ while(codes[y].codeSize == maskSize) {
+ if(codes[y].code == mask) {
+ if(ob >= BUFFER_SIZE) {
+ fwrite(outBuffer, sizeof(char),
+ BUFFER_SIZE, outFile);
+ ob = 0;
+ }
+ outBuffer[ob++] = codes[y].value;
+ ++x;
+ if(x >= dataSize) {
+ fwrite(outBuffer, sizeof(char),
+ ob, outFile);
+ return;
+ }
+ mask = 0;
+ maskSize = 0;
+ y = 0;
+ break;
+ }
+ ++y;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Display usage
+//////////////////////////////////////////////////////////////////////////
+void DisplayHelp() {
+ printf("Huffman compressor for PSXSDK\n");
+ printf("Original version by Joe Wingbermuehle\n");
+ printf("usage: huff [options] file\n");
+ printf("options:\n");
+ printf("\t-\t\tUse stdin for input\n");
+ printf("\t-c\t\tCompress/Uncompress to stdout\n");
+ printf("\t-k\t\tKeep old file\n");
+ printf("\t-u\t\tUncompress\n");
+}
+
+//////////////////////////////////////////////////////////////////////////
+// Main
+//////////////////////////////////////////////////////////////////////////
+int main(int argc, char **argv) {
+ char *inName;
+ char *outName;
+ int x, y;
+ int error;
+ char uncompress;
+ char keep;
+ double savings;
+
+ if(argc < 2) {
+ DisplayHelp();
+ exit(1);
+ }
+
+ uncompress = 0;
+ keep = 0;
+ inName = 0;
+ outName = 0;
+ outFile = 0;
+ inFile = 0;
+ for(x = 1; x < argc; x++) {
+ if(argv[x][0] == '-') {
+ switch(argv[x][1]) {
+ case 'u':
+ uncompress = 1;
+ break;
+ case 'c':
+ outFile = stdout;
+ keep = 1;
+ break;
+ case 'k':
+ keep = 1;
+ break;
+ case 0:
+ inFile = stdout;
+ break;
+ default:
+ DisplayHelp();
+ exit(1);
+ }
+ } else if(!inName && !inFile) {
+ inName = malloc(strlen(argv[x]) + 1);
+ strcpy(inName, argv[x]);
+ } else {
+ printf("unrecognized option: %s\n", argv[x]);
+ DisplayHelp();
+ exit(1);
+ }
+ }
+
+ if(!inFile) {
+ inFile = fopen(inName, "rb");
+ if(!inFile) {
+ printf("error: could not open input\n");
+ exit(1);
+ }
+ }
+
+ if(!uncompress) {
+ outName = malloc(strlen(inName) + 4);
+ strcpy(outName, inName);
+ strcat(outName, ".jh");
+ } else {
+ error = 0;
+ if(strlen(inName) < 5) {
+ error = 1;
+ } else {
+ y = strlen(inName) - 3;
+ for(x = 0; x < 4; x++) {
+ if(inName[x + y] != ".jh"[x]) {
+ error = 1;
+ break;
+ }
+ }
+ }
+ if(error) {
+ printf("bad file extension\n");
+ DisplayHelp();
+ exit(1);
+ }
+ outName = malloc(strlen(inName) + 1);
+ strcpy(outName, inName);
+ if(strlen(outName) > 4) {
+ outName[strlen(outName) - 3] = 0;
+ }
+ }
+
+ if(!outFile) {
+ outFile = fopen(outName, "wb");
+ if(!outFile) {
+ printf("error: could not open output\n");
+ exit(1);
+ }
+ }
+
+ if(uncompress) {
+ Decompress();
+ } else {
+ Compress();
+ savings = (double)inputSize - (double)ftell(outFile);
+ savings /= (double)inputSize;
+ savings *= 100.00;
+ printf("savings: %.2f%%\n", savings);
+ }
+
+ if(inFile != stdin) {
+ fclose(inFile);
+ if(!keep) {
+ remove(inName);
+ }
+ }
+ if(outFile != stdout) {
+ fclose(outFile);
+ }
+ exit(0);
+}
+
+
diff --git a/tools/lictool.c b/tools/lictool.c
new file mode 100644
index 0000000..4fcf493
--- /dev/null
+++ b/tools/lictool.c
@@ -0,0 +1,100 @@
+/*
+ * lictool
+ *
+ * Tool for manipulating PS1 license files
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+unsigned char lic_buffer[37632]; // 16 CD sectors..
+
+//0x2E08
+
+void display_usage();
+
+void display_usage()
+{
+ printf(""
+ "lictool - PS1 license file manipulation tool\n"
+ "usage: lictool <input> <output> <options>\n"
+ "\n"
+ "Options:\n"
+ " -tmd=<file> TMD file for boot logo\n"
+ " -removelogo Remove logo from license file\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int x,y,z,sz;
+ FILE *f;
+
+ if(argc < 3)
+ {
+ display_usage();
+ return 0;
+ }
+
+ f = fopen(argv[1], "rb");
+
+ if(f == NULL)
+ {
+ printf("Could not open input license file! Aborting.\n");
+ return -1;
+ }
+
+ fread(lic_buffer, sizeof(char), 37632, f);
+ fclose(f);
+
+ for(x = 3; x < argc; x++)
+ {
+ if(strncmp(argv[x], "-tmd=", 5) == 0)
+ {
+ f = fopen(argv[x] + 5, "rb");
+
+ if(f == NULL)
+ printf("Could not open TMD file %s. Ignoring option.\n", argv[x] + 5);
+ else
+ {
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ z = 0x2E08;
+
+ for(y = 0; y < sz; y++)
+ {
+ if((z - ((z / 2352)*2352)) == 2072)
+ z+=304;
+
+ fread(&lic_buffer[z], sizeof(char), 1, f);
+
+ z++;
+ }
+
+ fclose(f);
+ }
+ }
+ else if(strncmp(argv[x], "-removelogo", 11) == 0)
+ {
+ z = 0x2E08;
+
+ for(y = 0; y < 12; y++)
+ lic_buffer[z+y] = 0;
+
+ lic_buffer[z] = 0x41;
+ }
+ }
+
+ f = fopen(argv[2], "wb");
+
+ if(f == NULL)
+ {
+ printf("Could not open output file path for writing! Aborting.\n");
+ return -1;
+ }
+
+ fwrite(lic_buffer, sizeof(char), 37632, f);
+ fclose(f);
+
+ return 0;
+}
diff --git a/tools/mkpsxiso.c b/tools/mkpsxiso.c
new file mode 100644
index 0000000..5fd9da5
--- /dev/null
+++ b/tools/mkpsxiso.c
@@ -0,0 +1,250 @@
+/*
+ * mkpsxiso
+ *
+ * Converts an ISO to a .bin/.cue of a Playstation disk
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+char iso2raw_sec[16];
+char iso2raw_sub[8];
+char iso2raw_buf[2048];
+char iso2raw_edc[4];
+char iso2raw_ecc[276];
+
+void Iso2Raw_init()
+{
+ int x;
+
+ for(x = 0; x < 16; x++)
+ iso2raw_sec[x] = 0xFF;
+
+ iso2raw_sec[0] = 0;
+ iso2raw_sec[11] = 0;
+ iso2raw_sec[12] = 0;
+ iso2raw_sec[13] = 2;
+ iso2raw_sec[14] = 0;
+ iso2raw_sec[15] = 2;
+
+ for(x = 0; x < 8; x++)
+ iso2raw_sub[x] = 0;
+
+ for(x = 0; x < 4; x++)
+ iso2raw_edc[x] = 1;
+
+ for(x = 0; x < 276; x++)
+ iso2raw_ecc[x] = 2;
+}
+
+int Iso2Raw_licenseFile(char *licFile, char *binFile) {
+ FILE *lic, *bin;
+ char buffer[37632];
+ int sz;
+ int ret;
+
+ lic = fopen(licFile, "rb"); //lic = new RandomAccessFile(licFile, "r");
+
+ if(lic == NULL)
+ {
+ printf("Error! Could not open license file!\n");
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ bin = fopen(binFile, "rb+"); //bin = new RandomAccessFile(binFile, "rw");
+
+ if(bin == NULL)
+ {
+ printf("Error! Could not open BIN file!\n");
+ fclose(lic);
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ fseek(lic, 0, SEEK_END);
+ sz = ftell(lic);
+ fseek(lic, 0, SEEK_SET);
+
+
+ if (sz != 37632)
+ {
+ printf("Error! License file size mismatch. Image not licensed!\n");
+ fclose(lic);
+ fclose(bin);
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ fseek(bin, 0, SEEK_END);
+ sz = ftell(bin);
+ fseek(bin, 0, SEEK_SET);
+
+ if ((sz % 2352) != 0)
+ {
+ printf("Error! RAW image file size is not a multiple of 2352. Image not licensed!\n");
+ fclose(lic);
+ fclose(bin);
+ ret = 0;
+ goto Iso2Raw_licenseFile_end;
+ }
+
+ fread(buffer, sizeof(char), 37632, lic);
+ fwrite(buffer, sizeof(char), 37632, bin);
+
+ fclose(lic);
+ fclose(bin);
+ ret = 1;
+
+Iso2Raw_licenseFile_end:
+ if(ret == 0)
+ printf("Error licensing file! You must NOT burn the RAW image!\n");
+
+ return ret;
+}
+
+void Iso2Raw_generateCue(char *binFileName)
+{
+ int x, y;
+ char binBaseName[256];
+ char cueFileName[256];
+ FILE *cue_file;
+
+ for(x = (strlen(binFileName) - 1); x >= 0; x--)
+ {
+ if(binFileName[x] == '/' || binFileName[x] == '\\' || binFileName[x] == ':')
+ break;
+ }
+
+ x++;
+ y = 0;
+
+ for(; x < strlen(binFileName); x++)
+ binBaseName[y++] = binFileName[x];
+
+ binBaseName[y] = 0;
+
+ y = 0;
+
+ for(x = 0; x < strlen(binFileName); x++)
+ {
+ if(binFileName[x] == '.')
+ break;
+ else
+ cueFileName[y++] = binFileName[x];
+ }
+
+ cueFileName[y] = 0;
+
+ strcat(cueFileName, ".cue");
+
+ cue_file = fopen(cueFileName, "wb");
+
+ fprintf(cue_file, "FILE \"%s\" BINARY\n", binBaseName);
+ fprintf(cue_file, "TRACK 01 MODE2/2352\n");
+ fprintf(cue_file, " INDEX 01 00:00:00\n");
+
+ fclose(cue_file);
+}
+
+int Iso2Raw_convert(char *isofile, char *rawfile, char *licfile)
+{
+ FILE *infile, *outfile;
+ int c;
+ int thesec = 0;
+ int filesize, totalsectors, sector;
+
+ infile = fopen(isofile, "rb");
+
+ if(infile == NULL)
+ {
+ printf("An error has occured while trying to open file %s\n", isofile);
+ return 0;
+ }
+
+ fseek(infile, 0, SEEK_END);
+ filesize = ftell(infile);
+ fseek(infile, 0, SEEK_SET);
+
+ if ((filesize % 2048) != 0)
+ {
+ printf("Error! ISO file size is not a multiple of 2048. Operation aborted!\n");
+ fclose(infile);
+ return 0;
+ }
+
+ outfile = fopen(rawfile, "wb+");
+ fseek(outfile, 0, SEEK_SET);
+
+ if(outfile == NULL)
+ {
+ printf("An error has occured while trying to create file %s\n", rawfile);
+ fclose(infile);
+ return 0;
+ }
+
+ sector = 1;
+ totalsectors = filesize / 2048;
+ for(;;)
+ {
+ c = fread(iso2raw_buf, sizeof(char), 2048, infile);
+ if(c!=2048)break;
+
+ fwrite(iso2raw_sec, sizeof(char), 16, outfile);
+ fwrite(iso2raw_sub, sizeof(char), 8, outfile);
+ fwrite(iso2raw_buf, sizeof(char), 2048, outfile);
+ fwrite(iso2raw_edc, sizeof(char), 4, outfile);
+ fwrite(iso2raw_ecc, sizeof(char), 276, outfile);
+
+ thesec++;
+
+ if (thesec > 74)
+ {
+ thesec = 0;
+ iso2raw_sec[13]++;
+ }
+
+ iso2raw_sec[14] = ((thesec/10)<<4)|(thesec - ((thesec/10)*10));
+
+ printf("\r%d%% completed...", sector * 100 / totalsectors);
+ sector++;
+ }
+
+ printf("\r100%% completed! \n");
+
+ fclose(infile);
+ fclose(outfile);
+
+ Iso2Raw_generateCue(rawfile);
+
+ if(!Iso2Raw_licenseFile(licfile, rawfile))
+ return 0;
+
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ puts("mkpsxiso (C Edition) v0.1b - Converts a standard ISO image to .bin/.cue (PSX)");
+ puts("This software is based on Bruno Freitas' mkpsxiso in Java - bootsector@ig.com.br");
+ puts("That version is in turn based on Conyers' mkpsxiso - http://www.conyers.demon.co.uk");
+ puts("Author: Giuseppe Gatta (aka nextvolume) - 01/07/2009 - tails92@gmail.com\n");
+
+ if(argc != 4)
+ {
+ printf("Usage: mkpsxiso <iso file> <bin file> <PSX license file>\n");
+ return 1;
+ }
+
+ Iso2Raw_init();
+
+ if (!Iso2Raw_convert(argv[1], argv[2], argv[3]))
+ {
+ puts("ISO file conversion failed.");
+ return 1;
+ }
+
+ puts("ISO file conversion terminated successfully!!");
+
+ return 0;
+}
diff --git a/tools/mod4psx.c b/tools/mod4psx.c
new file mode 100644
index 0000000..1fc68ef
--- /dev/null
+++ b/tools/mod4psx.c
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "../libmodplay/modplay.h"
+#include "adpcm.h"
+
+unsigned char *mod_data;
+ModMusic *mod;
+
+// Container format
+
+// Header
+
+// 8 bytes - "_mod4psx"
+// 4 bytes - Number of samples contained
+
+// Sample format
+// 4 bytes - Length of ADPCM sample
+// 8 bytes - Reserved
+// ... Data ...
+
+// All multi word numerical values are in little endian format
+// which is used by the processor of the PlayStation.
+// All data is aligned to 4 bytes.
+
+unsigned char adpcm_buffer[0x10000];
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ int sz, x,y;
+
+ if(argc < 3)
+ {
+ printf("mod4psx <mod_music> <adpcm_dat>\n");
+ printf(
+"\nMOD4PSX gets the sound samples from a music module supported by libmodplay, "
+"and then converts them to PS1 ADPCM format and puts them all in a datafile, which will be able to be loaded "
+"by libmodplay. In this way the CPU time needed by the PlayStation processor to convert at runtime from PCM to ADPCM is saved.\n"
+);
+ return -1;
+ }
+
+ f = fopen(argv[1], "rb");
+
+ if(f == NULL)
+ {
+ printf("Could not open %s for reading. Aborting.\n", argv[1]);
+ return -1;
+ }
+
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ mod_data = malloc(sz);
+
+ if(mod_data == NULL)
+ {
+ printf("Could not allocate %d bytes of memory. Aborting.\n", sz);
+ return -1;
+ }
+
+ fread(mod_data, sizeof(char), sz, f);
+
+ fclose(f);
+
+ mod = MODLoad(mod_data);
+
+ printf("Title: %s\n", mod->title);
+
+
+
+ f = fopen(argv[2], "wb");
+
+// Write header
+
+// Magic string
+ fprintf(f, "_mod4psx");
+// Write number of samples
+ fputc(mod->sample_num & 0xff, f);
+ fputc((mod->sample_num >> 8) & 0xff, f);
+ fputc(0, f);
+ fputc(0, f);
+
+ for(x = 0; x < mod->sample_num; x++)
+ {
+ //printf("%d: %s\n", x, mod->sample[x].name);
+ printf("sample[%d].bits = %d, sample[%d].data_type = %d\n", x, mod->sample[x].bits, x, mod->sample[x].data_type);
+
+
+ if(mod->sample[x].length >= 32)
+ {
+ if((mod->sample[x].data_type & 1) && mod->sample[x].bits == 8)
+ {
+ for(y = 0; y < mod->sample[x].length; y++)
+ mod->sample[x].data[y]^=0x80;
+ }
+
+ if(//mod->fmt == MOD_FMT_MOD &&
+ mod->sample[x].repeat_len > 2)
+ {
+
+ sz = SsAdpcmPack(mod->sample[x].data, adpcm_buffer, // FIX THIS!!!
+ mod->sample[x].length / (mod->sample[x].bits / 8), (mod->sample[x].bits==16)?FMT_S16:FMT_U8,
+ sizeof(adpcm_buffer),
+ 1, mod->sample[x].repeat_off);
+
+ }
+ else
+ {
+
+
+ sz = SsAdpcmPack(mod->sample[x].data, adpcm_buffer,
+ mod->sample[x].length / (mod->sample[x].bits / 8), (mod->sample[x].bits==16)?FMT_S16:FMT_U8,
+ sizeof(adpcm_buffer), 0, 0);
+
+ }
+
+ printf("%d) %s, %d -> %d, %d, %d, FIN=%d\n", x, mod->sample[x].name,
+ mod->sample[x].length, sz,
+ mod->sample[x].repeat_off,
+ mod->sample[x].repeat_len, mod->sample[x].finetune);
+ }
+ else
+ {
+ printf("%d) %s, Not written\n", x, mod->sample[x].name);
+ sz = 0;
+ }
+
+// Write length of ADPCM sample
+
+ fputc(sz & 0xff, f);
+ fputc((sz>>8)&0xff, f);
+ fputc((sz>>16)&0xff, f);
+ fputc((sz>>24)&0xff, f);
+
+// Skip 8 reserved bytes - for future expansion...
+
+ fseek(f, 8, SEEK_CUR);
+
+// Write ADPCM sample data
+
+// Manipulate the samples to do looping for Protracker MOD samples
+// The PCM to ADPCM conversion routines haven't been modified to do this yet.
+
+ /*if(mod->sample[x].repeat_len > 2 &&
+ mod->fmt == MOD_FMT_MOD)
+ {
+
+ for(y = 0; y < ((sz / 16)-1); y++)
+ {
+ if((mod->sample[x].repeat_off / 28) == y)
+ adpcm_buffer[(y<<4) + 1] = 6;
+ else
+ adpcm_buffer[(y<<4) + 1] = 2;
+ }
+
+ adpcm_buffer[(y<<4) + 1] = 3;
+ }*/
+
+ fwrite(adpcm_buffer, sizeof(char), sz, f);
+ }
+
+ return 0;
+}
diff --git a/tools/psfex.c b/tools/psfex.c
new file mode 100644
index 0000000..3de4bc8
--- /dev/null
+++ b/tools/psfex.c
@@ -0,0 +1,113 @@
+/*
+ * psfex - Extracts an EXE from a PSF file
+ *
+ * Programmed by Giuseppe Gatta - released in the public domain
+ * It can be used for any platform which the PSF format supports, not only PS1.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+// 0-2: PSF
+// 3: Version byte (0x01 for PlayStation)
+// 4: Size of reserved area (LE unsigned 32-bit)
+// 8: Compressed program length (LE unsigned 32-bit)
+// 12: Compressed program CRC32 (LE unsigned 32-bit)
+// xxx: Reserved area
+// xxx: Compressed program
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ unsigned char *fm;
+ unsigned int res_size;
+ unsigned int cprg_size;
+ unsigned int cprg_crc32;
+ unsigned char *om;
+ unsigned long dest_len;
+ int sz;
+
+ if(argc < 3)
+ {
+ printf("psfex - Extracts an executable from a PSF file\n");
+ printf("psfex [.psf] [output]\n");
+ return -1;
+ }
+
+ f = fopen(argv[1], "rb");
+
+ if (f == NULL)
+ {
+ printf("Could not open file.\n");
+ return -1;
+ }
+
+/*
+ * Get PSF size and load it in memory
+ */
+
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ fm = malloc(sz);
+ fread(fm, sizeof(char), sz, f);
+
+ fclose(f);
+
+/*
+ * Get header information
+*/
+ if(fm[0] == 'P' && fm[1] == 'S' && fm[2] == 'F')
+ {
+ printf("PSF file.\n");
+ }
+ else
+ {
+ printf("Not a PSF file. Aborting.\n");
+ return -1;
+ }
+
+ res_size = fm[4] | (fm[5] << 8) | (fm[6]<<16) | (fm[7]<<24);
+ cprg_size = fm[8] | (fm[9] << 8) | (fm[10]<<16)|(fm[11]<<24);
+ cprg_crc32 = fm[12] | (fm[13]<<8)|(fm[14]<<16)|(fm[15]<<24);
+
+ printf("Reserved area size: %d bytes\n", res_size);
+ printf("Compressed program size: %d bytes\n", cprg_size);
+ printf("Compressed program CRC32: 0x%08x\n", cprg_crc32);
+
+/*
+ * Decompress the program
+ * The PSF format is inherently flawed and so we have to allocate 2 megabytes
+ * of memory (size of PS1 RAM) and then get the real destination size at the end
+ */
+ om = malloc(0x200000);
+ dest_len = 0x200000;
+ uncompress(om, &dest_len, &fm[16 + res_size], cprg_size);
+
+ printf("Real destination length: %ld\n", dest_len);
+
+/*
+ * Now let's write the decompressed program to the output file
+ */
+ f = fopen(argv[2], "wb");
+
+ if(f == NULL)
+ {
+ printf("Could not open %s for writing. Aborting.\n", argv[2]);
+ return -1;
+ }
+
+ fwrite(om, sizeof(char), dest_len, f);
+ fclose(f);
+
+/*
+ * Free memory, at the exit it is done anyway but this helps adaptions
+ */
+
+ free(om);
+ free(fm);
+
+ return 0;
+}
diff --git a/tools/spasm/Makefile b/tools/spasm/Makefile
new file mode 100644
index 0000000..093b9b5
--- /dev/null
+++ b/tools/spasm/Makefile
@@ -0,0 +1,17 @@
+include ../../Makefile.cfg
+
+OUT = spasm$(EXE_SUFFIX)
+
+OBJS = $(patsubst %.c, %.o, $(wildcard *.c))
+
+$(OUT): $(OBJS)
+ $(HOST_CC) $(HOST_CFLAGS) -o $(OUT) $(OBJS)
+
+%.o: %.c
+ $(HOST_CC) $(HOST_CFLAGS) -c -o $@ $<
+
+install:
+ cp -rv $(OUT) $(TOOLCHAIN_PREFIX)/bin
+
+clean:
+ rm -f $(OBJS) $(OUT)
diff --git a/tools/spasm/codegen.c b/tools/spasm/codegen.c
new file mode 100644
index 0000000..fda456d
--- /dev/null
+++ b/tools/spasm/codegen.c
@@ -0,0 +1,144 @@
+#include "spasm.h"
+
+char curIns[128];
+unsigned int curInsArg;
+unsigned int curInsArgT;
+unsigned int insArgv[64];
+unsigned int insArgc;
+unsigned int insArgt[64];
+unsigned int copn;
+int org_found;
+
+void (*INSFUNC)(void);
+
+volatile unsigned int curPc = 0;
+int curPass = 0;
+unsigned int startAddress = 0;
+unsigned int numLabels;
+unsigned int numLabelsAlloc;
+int first_instruction;
+asm_label *labels;
+static int find_label_status = 1;
+
+void codegen_init(void)
+{
+ curPc = startAddress;
+ curPass = 0;
+ numLabels = 0;
+ numLabelsAlloc = 0;
+ labels = NULL;
+}
+
+static asm_label *find_label_internal(char *name)
+{
+ int i;
+
+ for(i = 0; i < numLabels; i++)
+ {
+ if(strcmp(name, labels[i].name) == 0)
+ return &labels[i];
+ }
+
+ return NULL;
+}
+
+static void add_label_internal(char *name, unsigned int pc)
+{
+ // add labels only if current pass >= 1!
+ asm_label *l;
+
+/* if(curPass == )
+ return;
+
+ if(curPass >= 2)
+ return;*/
+
+ //printf("Name = %s\n", name);
+
+ l = find_label_internal(name);
+
+ if(l)
+ {
+ if(l->pc != pc)
+ {
+ //if(l->pass == curPass)
+ // assembler_error("Impossible to redefine label %s", name);
+
+ //printf("Redefining, [%s] = %08X, pass %d\n", l->name, pc, curPass);
+ l->pc = pc;
+ }
+
+ return;
+ }
+
+ if(numLabels == numLabelsAlloc)
+ {
+ numLabelsAlloc += 128;
+ labels = realloc(labels, sizeof(asm_label) * numLabelsAlloc);
+ }
+
+ strncpy(labels[numLabels].name, name, 127);
+ labels[numLabels].pass = curPass;
+
+ labels[numLabels].pc = pc;
+
+ numLabels++;
+
+ //printf("label #%d, [%s] = %08X, pass = %d\n", numLabels, name, pc, curPass);
+
+ /*while(*name)
+ {
+ printf("%x, \'%c\'\n", *name, *name);
+ name++;
+ }*/
+}
+
+
+
+void add_label(char *name, unsigned int pc)
+{
+ if(curPass == -1)
+ return;
+
+ return add_label_internal(name, pc);
+}
+
+void add_label_equ(char *name, unsigned int pc)
+{
+ return add_label_internal(name, pc);
+}
+
+unsigned int find_label(char *name)
+{
+ //printf("find_label(%s)\n", name);
+
+ asm_label *l = find_label_internal(name);
+
+ if(l)
+ {
+ //find_label_status = 1;
+ return l->pc;
+ }
+
+// remember! if pass >= 1, abort if you can't find a label, because that means it was really
+// impossible to find.
+
+// printf(">>DEBUG, PASS = %d << Couldn't find label %s$\n", curPass, name);
+
+ find_label_status = 0;
+
+ if(curPass == 1)
+ instruction_error("Cannot find label %s", name);
+
+ return 0xFFFFFFFF;
+}
+
+void find_label_reset()
+{
+ find_label_status = 1;
+}
+
+int find_label_ok()
+{
+ return find_label_status;
+}
diff --git a/tools/spasm/codegen.h b/tools/spasm/codegen.h
new file mode 100644
index 0000000..e9e0ab7
--- /dev/null
+++ b/tools/spasm/codegen.h
@@ -0,0 +1,33 @@
+#ifndef _SPASM_CODEGEN_H
+#define _SPASM_CODEGEN_H
+
+typedef struct
+{
+ char name[128];
+ unsigned int pc;
+ unsigned int pass;
+}asm_label;
+
+extern asm_label *labels;
+
+extern volatile unsigned int curPc;
+extern int curPass;
+extern unsigned int numLabels;
+extern unsigned int startAddress;
+extern unsigned int copn;
+extern int first_instruction;
+extern int org_found;
+extern void (*INSFUNC)(void);
+
+void codegen_init(void);
+void add_label(char *label, unsigned int pc);
+void add_label_equ(char *label, unsigned int pc);
+unsigned int find_label(char *label);
+void find_label_reset();
+int find_label_ok();
+int label_was_not_found_once(char *name);
+void add_not_found_label(char *name);
+//void resolve_labels();
+
+
+#endif
diff --git a/tools/spasm/error.c b/tools/spasm/error.c
new file mode 100644
index 0000000..019c3da
--- /dev/null
+++ b/tools/spasm/error.c
@@ -0,0 +1,58 @@
+#include "spasm.h"
+
+static void show_line(void)
+{
+ printf("%s\n", curLine);
+ printf("^^^^^^^^^^^^^^\n");
+}
+
+void instruction_error(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ printf("Line %d: Error(%s) - ", line_number, curIns);
+ vprintf(format, ap);
+ printf("\n");
+ show_line();
+
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+void instruction_warning(char *format, ...)
+{
+ if(curPass <= 0)
+ return;
+
+ va_list ap;
+
+ va_start(ap, format);
+
+ printf("Line %d: Warning (%s) - ", line_number, curIns);
+ vprintf(format, ap);
+ printf("\n");
+ show_line();
+
+ va_end(ap);
+}
+
+void assembler_error(char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ printf("Line %d, assembler error: ", line_number);
+ vprintf(format, ap);
+ printf("\n");
+ show_line();
+
+ va_end(ap);
+
+ exit(EXIT_FAILURE);
+}
+
+ \ No newline at end of file
diff --git a/tools/spasm/error.h b/tools/spasm/error.h
new file mode 100644
index 0000000..3951ffd
--- /dev/null
+++ b/tools/spasm/error.h
@@ -0,0 +1,10 @@
+#ifndef _SPASM_ERROR_H
+#define _SPASM_ERROR_H
+
+void instruction_error(char *format, ...);
+void instruction_warning(char *format, ...);
+void assembler_error(char *format, ...);
+extern int yylineno;
+
+#endif
+
diff --git a/tools/spasm/eval.c b/tools/spasm/eval.c
new file mode 100644
index 0000000..01f8f66
--- /dev/null
+++ b/tools/spasm/eval.c
@@ -0,0 +1,130 @@
+#include "spasm.h"
+
+/**
+ * Expressions in SPASM are implemented in a totally broken manner.
+ *
+ * The result of an expression is the value of its initial argument
+ * after executing the operation of the last operand with the last argument.
+ *
+ * For example dw $cafeba00+2+4 is not equal to dw $cafeba06
+ * but to dw $cafeba04.
+ * Likewise, dw $2+$cafeba00+4 is not equal to dw $cafeba06
+ * but to dw $6 !
+ */
+
+unsigned int spasm_eval(char *expr)
+{
+ char *cset = "+-><&|*!";
+ char *csetp;
+ char *cp;
+ int ok;
+ int t = T_INTEGER;
+ char sbuf[128];
+
+ if(strcmp(expr, "*") == 0) // Return current return address
+ return curPc;
+
+ if(strcasecmp(curIns, "incbin") == 0)
+ return 0; // If current instruction is incbin, do not evaluate.
+
+ if(*expr == '"' || *expr == '\'')
+ return 0; // I won't even try to evaluate strings!
+
+ int ispan = strcspn(expr, cset);
+
+ csetp = cset;
+
+ ok = 0; // ok will be 0 if we found no operator, 1 if we found it
+
+ char op = '?';
+
+ cp = NULL;
+
+ while(*csetp)
+ {
+ char *tcp = strrchr(expr, *csetp);
+
+ if(tcp) // Found operator
+ {
+ if(tcp > cp)
+ cp = tcp;
+
+ if(*cp == '>')
+ {
+ if(*(cp-1) != '>')
+ instruction_error("Bad operator");
+ }
+
+ if(*cp == '<')
+ {
+ if(*(cp-1) != '<')
+ instruction_error("Bad operator");
+ }
+
+ cp++;
+ ok = 1;
+ op = *csetp;
+ break;
+ }
+
+ csetp++;
+ }
+
+ unsigned int one = 0;
+ unsigned int two = 0;
+
+ memcpy(sbuf, expr, ispan);
+ sbuf[ispan] = '\0';
+
+ if(strlen(sbuf) == 0)
+ strcpy(sbuf, "0");
+ //instruction_error("Bad expression");
+
+ //printf("sbuf = %s\n", sbuf);
+ one = asm_atoi(sbuf);
+
+ t = atoiT[insArgc];
+
+ if(ok)
+ {
+ strcpy(sbuf, cp);
+
+ if(strlen(sbuf) == 0)
+ instruction_error("Bad expression");
+
+ two = asm_atoi(sbuf);
+
+ if(t == T_INTEGER)
+ t = atoiT[insArgc];
+ }
+
+ atoiT[insArgc] = t;
+
+ switch(op)
+ {
+ case '+':
+ return one + two;
+ break;
+ case '-':
+ return one - two;
+ break;
+ case '<':
+ return one << two;
+ break;
+ case '>':
+ return one >> two;
+ break;
+ case '&':
+ return one & two;
+ break;
+ case '|':
+ case '!':
+ return one | two;
+ break;
+ case '*':
+ return one * two;
+ break;
+ }
+
+ return one;
+}
diff --git a/tools/spasm/eval.h b/tools/spasm/eval.h
new file mode 100644
index 0000000..a568f7e
--- /dev/null
+++ b/tools/spasm/eval.h
@@ -0,0 +1,6 @@
+#ifndef _SPASM_EVAL_H
+#define _SPASM_EVAL_H
+
+unsigned int spasm_eval(char *expr);
+
+#endif
diff --git a/tools/spasm/fcaseopen.c b/tools/spasm/fcaseopen.c
new file mode 100755
index 0000000..20efd5b
--- /dev/null
+++ b/tools/spasm/fcaseopen.c
@@ -0,0 +1,137 @@
+/*
+Copyright (c) 2009 Keith Bauer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "fcaseopen.h"
+
+#if !defined(_WIN32)
+#include <stdlib.h>
+#include <string.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+
+// r must have strlen(path) + 2 bytes
+static int casepath(char const *path, char *r)
+{
+ size_t l = strlen(path);
+ char *p = alloca(l + 1);
+ strcpy(p, path);
+ size_t rl = 0;
+
+ DIR *d;
+ if (p[0] == '/')
+ {
+ d = opendir("/");
+ p = p + 1;
+ }
+ else
+ {
+ d = opendir(".");
+ r[0] = '.';
+ r[1] = 0;
+ rl = 1;
+ }
+
+ int last = 0;
+ char *c = strsep(&p, "/");
+ while (c)
+ {
+ if (!d)
+ {
+ return 0;
+ }
+
+ if (last)
+ {
+ closedir(d);
+ return 0;
+ }
+
+ r[rl] = '/';
+ rl += 1;
+ r[rl] = 0;
+
+ struct dirent *e = readdir(d);
+ while (e)
+ {
+ if (strcasecmp(c, e->d_name) == 0)
+ {
+ strcpy(r + rl, e->d_name);
+ rl += strlen(e->d_name);
+
+ closedir(d);
+ d = opendir(r);
+
+ break;
+ }
+
+ e = readdir(d);
+ }
+
+ if (!e)
+ {
+ strcpy(r + rl, c);
+ rl += strlen(c);
+ last = 1;
+ }
+
+ c = strsep(&p, "/");
+ }
+
+ if (d) closedir(d);
+ return 1;
+}
+#endif
+
+FILE *fcaseopen(char const *path, char const *mode)
+{
+ FILE *f = fopen(path, mode);
+#if !defined(_WIN32)
+ if (!f)
+ {
+ char *r = alloca(strlen(path) + 2);
+ if (casepath(path, r))
+ {
+ f = fopen(r, mode);
+ }
+ }
+#endif
+ return f;
+}
+
+void casechdir(char const *path)
+{
+#if !defined(_WIN32)
+ char *r = alloca(strlen(path) + 2);
+ if (casepath(path, r))
+ {
+ chdir(r);
+ }
+ else
+ {
+ errno = ENOENT;
+ }
+#else
+ chdir(path);
+#endif
+}
diff --git a/tools/spasm/fcaseopen.h b/tools/spasm/fcaseopen.h
new file mode 100755
index 0000000..a6d024b
--- /dev/null
+++ b/tools/spasm/fcaseopen.h
@@ -0,0 +1,40 @@
+/*
+Copyright (c) 2009 Keith Bauer
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef fcaseopen_h
+#define fcaseopen_h
+
+#include <stdio.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+extern FILE *fcaseopen(char const *path, char const *mode);
+
+extern void casechdir(char const *path);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
diff --git a/tools/spasm/opcode.c b/tools/spasm/opcode.c
new file mode 100644
index 0000000..a09c119
--- /dev/null
+++ b/tools/spasm/opcode.c
@@ -0,0 +1,2214 @@
+#include "spasm.h"
+
+struct
+{
+ char *name;
+ void (*func)();
+}instruction_table[] =
+{
+ {"add" ,INS_ADD},
+ {"addi" ,INS_ADDI},
+ {"addiu" ,INS_ADDIU},
+ {"addu" ,INS_ADDU},
+ {"and" ,INS_AND},
+ {"andi" ,INS_ANDI},
+ {"beq" ,INS_BEQ},
+ {"bgez" ,INS_BGEZ},
+ {"bgezal" ,INS_BGEZAL},
+ {"bgtz" ,INS_BGTZ},
+ {"blez" ,INS_BLEZ},
+ {"bltz" ,INS_BLTZ},
+ {"bltzal" ,INS_BLTZAL},
+ {"bne" ,INS_BNE},
+ {"break" ,INS_BREAK},
+ {"cfc0" ,INS_CFC },
+ {"cfc1" ,INS_CFC },
+ {"cfc2" ,INS_CFC },
+ {"cfc3" ,INS_CFC },
+ {"cop0" ,INS_COP },
+ {"cop1" ,INS_COP },
+ {"cop2" ,INS_COP },
+ {"cop3" ,INS_COP },
+ {"ctc0" ,INS_CTC },
+ {"ctc1" ,INS_CTC },
+ {"ctc2" ,INS_CTC },
+ {"ctc3" ,INS_CTC },
+ {"div" ,INS_DIV},
+ {"divu" ,INS_DIVU},
+ {"j" ,INS_J},
+ {"jal" ,INS_JAL},
+ {"jalr" ,INS_JALR},
+ {"jr" ,INS_JR},
+ {"lb" ,INS_LB},
+ {"lbu" ,INS_LBU},
+ {"lh" ,INS_LH},
+ {"lhu" ,INS_LHU},
+ {"lui" ,INS_LUI},
+ {"lw" ,INS_LW},
+ {"lwc0" ,INS_LWC },
+ {"lwc1" ,INS_LWC },
+ {"lwc2" ,INS_LWC },
+ {"lwc3" ,INS_LWC },
+ {"lwl" ,INS_LWL},
+ {"lwr" ,INS_LWR},
+ {"mfc0" ,INS_MFC},
+ {"mfc1" ,INS_MFC},
+ {"mfc2" ,INS_MFC},
+ {"mfc3" ,INS_MFC},
+ {"mfhi" ,INS_MFHI},
+ {"mflo" ,INS_MFLO},
+ {"mtc0" ,INS_MTC },
+ {"mtc1" ,INS_MTC },
+ {"mtc2" ,INS_MTC },
+ {"mtc3" ,INS_MTC },
+ {"mthi" ,INS_MTHI},
+ {"mtlo" ,INS_MTLO},
+ {"mult" ,INS_MULT},
+ {"multu" ,INS_MULTU},
+ {"nor" ,INS_NOR},
+ {"or" ,INS_OR},
+ {"ori" ,INS_ORI},
+ {"sb" ,INS_SB},
+ {"sh" ,INS_SH},
+ {"sll" ,INS_SLL},
+ {"sllv" ,INS_SLLV},
+ {"slt" ,INS_SLT},
+ {"slti" ,INS_SLTI},
+ {"sltiu" ,INS_SLTIU},
+ {"sltu" ,INS_SLTU},
+ {"sra" ,INS_SRA},
+ {"srav" ,INS_SRAV},
+ {"srl" ,INS_SRL},
+ {"srlv" ,INS_SRLV},
+ {"sub" ,INS_SUB},
+ {"subu" ,INS_SUBU},
+ {"sw" ,INS_SW},
+ {"swc0" ,INS_SWC},
+ {"swc1" ,INS_SWC},
+ {"swc2" ,INS_SWC},
+ {"swc3" ,INS_SWC},
+ {"swl" ,INS_SWL},
+ {"swr" ,INS_SWR},
+ {"syscall" ,INS_SYSCALL},
+ {"xor" ,INS_XOR},
+ {"xori" ,INS_XORI},
+
+ {"b" ,INS_B},
+ {"la" ,INS_LA},
+ {"li" ,INS_LI},
+ {"nop" ,INS_NOP},
+ {"move" ,INS_MOVE},
+ {"subi" ,INS_SUBI},
+ {"subiu" ,INS_SUBIU},
+ {"beqz" ,INS_BEQZ},
+ {"bnez" ,INS_BNEZ},
+ {"bal" ,INS_BAL},
+ {"org" ,INS_ORG},
+ {"include" ,INS_INCLUDE},
+ {"incbin" ,INS_INCBIN},
+ {"dcb" ,INS_DCB},
+ {"db" ,INS_DB},
+ {"dh" ,INS_DH},
+ {"dw" ,INS_DW},
+ {"align" ,INS_ALIGN},
+
+ {NULL},
+};
+
+#define I_TYPE(op, rs, rt, imm) \
+ ( (((op) & 63) << 26) | (((rs) & 31) << 21) | (((rt) & 31) << 16) | \
+ ((imm) & 0xFFFF) )
+
+#define J_TYPE(op, target) \
+ ( (((op) & 63) << 26) | ((target) & 0x3FFFFFF) )
+
+#define R_TYPE(op, rs, rt, rd, shamt, funct) \
+ ( (((op) & 63) << 26) | (((rs) & 31) << 21) | (((rt) & 31) << 16) | \
+ (((rd) & 31) << 11) | (((shamt) & 31) << 6) | \
+ ((funct) & 63))
+
+// copn = coprocessor number
+// SET_DIS_CHECK()
+
+#define SET_DIS_CHECK() /* Argument type check. Placeholder, contains nothing for now. */
+
+int set_delay_slot = 0;
+
+void OUTSEEK(unsigned int position)
+{
+ fseek(asmOut, position, SEEK_SET);
+}
+
+void OUTBYTE(unsigned char b)
+{
+ if(curPass>0)
+ fputc(b, asmOut);
+
+ curPc++;
+}
+
+void OUTHALF(unsigned short h)
+{
+ if(curPass>0)
+ {
+ fputc(h&0xff, asmOut);
+ fputc(h>>8, asmOut);
+ }
+
+ curPc += 2;
+}
+
+void OUTWORD(unsigned int w)
+{
+ if(curPass>0)
+ {
+ fputc(w&0xff, asmOut);
+ fputc((w>>8)&0xff, asmOut);
+ fputc((w>>16)&0xff, asmOut);
+ fputc(w>>24, asmOut);
+ }
+
+ curPc += 4;
+}
+
+void OUTINS(unsigned int instruction)
+{
+ OUTWORD(instruction);
+
+ if(set_delay_slot)
+ {
+ OUTWORD(0);
+
+ set_delay_slot = 0;
+ }
+}
+
+
+void OUTSTRING(char *string)
+{
+ int stringt;
+ int esc=0;
+
+ if(*string == '"')
+ stringt = 0;
+ else if(*string == '\'')
+ stringt = 1;
+ else
+ instruction_error("OUTSTRING <INTERNAL ERROR>. Not a string!");
+
+ string++;
+
+ while(*string)
+ {
+ if(*string == '"')
+ {
+ if(stringt == 0 && !esc)
+ break;
+
+ OUTBYTE('"');
+ esc = 0;
+ }
+ else if(*string == '\'')
+ {
+ if(stringt == 1 && !esc)
+ break;
+
+ OUTBYTE('\'');
+ esc = 0;
+ }
+ else if(*string == 'n')
+ {
+ if(esc)
+ OUTBYTE('\n');
+ else
+ OUTBYTE('n');
+
+ esc = 0;
+ }
+ else if(*string == 't')
+ {
+ if(esc)
+ OUTBYTE('\t');
+ else
+ OUTBYTE('t');
+
+ esc = 0;
+ }
+ else if(*string == 'r')
+ {
+ if(esc)
+ OUTBYTE('\r');
+ else
+ OUTBYTE('r');
+
+ esc = 0;
+ }
+ else if(*string == '\\')
+ {
+ if(esc)
+ {
+ OUTBYTE('\\');
+ esc = 0;
+ }
+ else
+ esc = 1;
+ }
+ else
+ {
+ if(esc)
+ instruction_error("Invalid escape sequence \\%c in string", *string);
+
+ OUTBYTE(*string);
+ }
+
+ string++;
+ }
+}
+
+unsigned int OUTSIZE(void)
+{
+ int r;
+ int pos = ftell(asmOut);
+ fseek(asmOut, 0, SEEK_END);
+ r = ftell(asmOut);
+ fseek(asmOut, pos, SEEK_SET);
+ return r;
+}
+
+unsigned short compute_branch(unsigned int imm)
+{
+ unsigned int off = imm - (curPc + 4);
+ //off >>= 2;
+
+ if(curPass <= 0)
+ return 0;
+
+ if(off >= 0x20000 && off < -0x20000)
+ instruction_error("Branch out of range. %04x", off);
+
+ return (off>>2) & 0xFFFF;
+}
+
+unsigned int compute_jump(unsigned int imm)
+{
+ if(curPass <= 0)
+ return 0;
+
+ return (imm >> 2);
+}
+
+struct
+{
+ int size;
+ int pos;
+ unsigned int *el;
+}cannotPredict = {0, 0, NULL};
+
+unsigned int compute_real_offset(unsigned int imm, unsigned int base,
+ unsigned int rt)
+{
+ int i, unpredictable=0;
+ unsigned short hipart;
+ unsigned short lopart;
+
+ if(!find_label_ok())
+ {
+ if(cannotPredict.size == cannotPredict.pos)
+ {
+ cannotPredict.size += 128;
+ cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int));
+ }
+
+ cannotPredict.el[cannotPredict.pos++] = curPc;
+ }
+
+ for(i = 0; i < cannotPredict.pos; i++)
+ {
+ if(curPc == cannotPredict.el[i])
+ {
+ unpredictable = 1;
+ break;
+ }
+ }
+
+ if(!unpredictable && ((imm <= 0xFFFF) || (imm >= 0xFFFF8000 && imm <= 0xFFFFFFFF)))
+ return I_TYPE(0, base, rt, imm);
+
+//compute_real_offset_output_wide:
+ // li at, offset
+ // add at, at, base
+ // lw rt, 0(at)
+ hipart = (imm >> 16);
+ lopart = imm & 0xFFFF;
+ int t=(*curIns == 'l') ? rt : 1;
+
+ // lw at, $CAFEBABE(zero) -> lui at, $CAFE, ori at, at, $BABE, lw at, 0(at)
+ // lw at, $CAFEBABE(v0) -> lui at, $CAFE, ori at, at, $BABE, lw at, 0(v0)
+ // sw at, $CAFEBABE ->
+
+ if(base)
+ {
+ /*OUTINS( I_TYPE(15, 0, t, hipart)); // lui $t, (offset > 16)
+
+ if(lopart || unpredictable)
+ OUTINS( I_TYPE (13, t, t, lopart)); // ori $t, $t, offset & 0xffff
+
+ OUTINS( R_TYPE (0, t, base, t, 0, 33) ); // add $t, $t, base
+ return I_TYPE(0, t, rt, 0);*/
+
+// SPASM is seriously broken regarding this..
+ return I_TYPE(0, base, rt, imm);
+ }
+
+ if(lopart >= 0x8000)
+ hipart++;
+
+ OUTINS( I_TYPE(15, 0, t, hipart)); // lui $t, (offset > 16)
+
+ return I_TYPE(0, t, rt, lopart); // XX rt, lopart(rt)
+}
+
+void INS_ADD(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // ADD rd, rs, rt -> rd = rs + rt
+
+ if(insArgc == 2)
+ {
+ if(atoiT[1] == T_INTEGER)
+ return INS_ADDI();
+
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ if(atoiT[2] == T_INTEGER)
+ return INS_ADDI();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 32) );
+}
+
+void INS_ADDI(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // ADDI rt, rs, imm -> rt = rs + imm;
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ rs = insArgv[0];
+ imm = insArgv[1];
+ }
+ else
+ {
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = insArgv[2];
+ }
+
+ if(imm > 0x7FFF && imm < 0xFFFF0000)
+ instruction_warning("Immediate is possibly out of range.");
+
+ OUTINS( I_TYPE (8, rs, rt, imm) );
+}
+
+void INS_ADDIU(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // ADDIU rt, rs, imm -> rt = rs + imm;
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ rs = insArgv[0];
+ imm = insArgv[1];
+ }
+ else
+ {
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = insArgv[2];
+ }
+
+ if(imm > 0x7FFF && imm < 0xFFFF0000)
+ instruction_warning("Immediate is possibly out of range.");
+
+ OUTINS( I_TYPE (9, rs, rt, imm) );
+}
+
+void INS_ADDU(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // ADDU rd, rs, rt -> rd = rs + rt
+
+ if(insArgc == 2)
+ {
+ if(atoiT[1] == T_INTEGER)
+ return INS_ADDIU();
+
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ if(atoiT[2] == T_INTEGER)
+ return INS_ADDIU();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 33) );
+}
+
+void INS_AND(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // AND rd, rs, rt -> rd = rs + rt
+
+ if(insArgc == 2)
+ {
+ if(atoiT[1] == T_INTEGER)
+ return INS_ANDI();
+
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ if(atoiT[2] == T_INTEGER)
+ return INS_ANDI();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 36) );
+}
+
+void INS_ANDI(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // ANDI rt, rs, imm -> rt = rs + imm;
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ rs = insArgv[0];
+ imm = insArgv[1];
+ }
+ else
+ {
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = insArgv[2];
+ }
+
+ if(imm > 0xFFFF)
+ instruction_warning("Immediate is possibly out of range");
+
+ OUTINS( I_TYPE (12, rs, rt, imm) );
+}
+
+void INS_BEQ(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc != 3)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ rt = insArgv[1];
+ imm = insArgv[2];
+
+ OUTINS( I_TYPE(4, rs, rt, compute_branch(imm)) );
+}
+
+void INS_BGEZ(void)
+{
+ unsigned int rs, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(1, rs, 1, compute_branch(imm)) );
+}
+
+void INS_BGEZAL(void)
+{
+ unsigned int rs, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(1, rs, 17, compute_branch(imm)) );
+}
+
+void INS_BGTZ(void)
+{
+ unsigned int rs, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(7, rs, 0, compute_branch(imm)) );
+}
+
+void INS_BLEZ(void)
+{
+ unsigned int rs, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(6, rs, 0, compute_branch(imm)) );
+}
+
+void INS_BLTZ(void)
+{
+ unsigned int rs, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(1, rs, 0, compute_branch(imm)) );
+}
+
+void INS_BLTZAL(void)
+{
+ unsigned int rs, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(1, rs, 16, compute_branch(imm)) );
+}
+
+void INS_BNE(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc != 3)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ rt = insArgv[1];
+ imm = insArgv[2];
+
+ OUTINS( I_TYPE(5, rs, rt, compute_branch(imm)) );
+}
+
+void INS_BREAK(void)
+{
+ unsigned int imm = 0;
+
+ if(insArgc > 1)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 1)
+ imm = insArgv[0];
+
+ imm &= 0xFFFFF;
+
+ OUTINS( (imm << 6) | 13 );
+}
+
+void INS_CFC(void)
+{
+ unsigned int rt, rd;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rt = insArgv[0];
+ rd = insArgv[1];
+
+ OUTINS ( R_TYPE(16 | copn, 2, rt, rd, 0, 0) );
+}
+
+void INS_COP(void)
+{
+ unsigned int cofun;
+
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ cofun = insArgv[0];
+
+ OUTINS( ( (16 | copn) << 26) | (1<<25) | (cofun & 0x1FFFFFF));
+}
+
+void INS_CTC(void)
+{
+ unsigned int rt, rd;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rt = insArgv[0];
+ rd = insArgv[1];
+
+ OUTINS ( R_TYPE(16 | copn, 6, rt, rd, 0, 0) );
+}
+
+void INS_DIV(void)
+{
+ unsigned int rs, rt;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ rt = insArgv[1];
+
+ OUTINS ( R_TYPE(0, rs, rt, 0, 0, 26));
+}
+
+void INS_DIVU(void)
+{
+ unsigned int rs, rt;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ rt = insArgv[1];
+
+ OUTINS ( R_TYPE(0, rs, rt, 0, 0, 27));
+}
+
+void INS_J(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS ( J_TYPE(2, compute_jump(insArgv[0])));
+}
+
+void INS_JAL(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS ( J_TYPE(3, compute_jump(insArgv[0])));
+}
+
+void INS_JALR(void)
+{
+ unsigned int rd, rs;
+
+ if(insArgc < 1)
+ instruction_error("Not enough arguments");
+ if(insArgc > 2)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 1)
+ {
+ rd = 31; // register ra
+ rs = insArgv[0];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rs = insArgv[1];
+ }
+
+ OUTINS ( R_TYPE(0, rs, 0, rd, 0, 9));
+}
+
+void INS_JR(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS ( R_TYPE(0, insArgv[0], 0, 0, 0, 8));
+}
+
+void INS_LB(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(32, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_LBU(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(36, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_LH(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(33, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_LHU(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(37, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_LUI(void)
+{
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS(I_TYPE(15, 0, insArgv[0], insArgv[1]));
+}
+
+void INS_LW(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(35, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_LWC(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(48 | copn, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_LWL(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(34, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_LWR(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(38, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_MFC(void)
+{
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS(R_TYPE (16 | copn, 0, insArgv[0], insArgv[1], 0, 0));
+}
+
+void INS_MFHI(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS(R_TYPE(0, 0, 0, insArgv[0], 0, 16));
+}
+
+void INS_MFLO(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS(R_TYPE(0, 0, 0, insArgv[0], 0, 18));
+}
+
+void INS_MTC(void)
+{
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS(R_TYPE (16 | copn, 4, insArgv[0], insArgv[1], 0, 0));
+}
+
+void INS_MTHI(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS(R_TYPE(0, insArgv[0], 0, 0, 0, 17));
+}
+
+void INS_MTLO(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ OUTINS(R_TYPE(0, insArgv[0], 0, 0, 0, 19));
+}
+
+void INS_MULT(void)
+{
+ unsigned int rs, rt;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ rt = insArgv[1];
+
+ OUTINS(R_TYPE(0, rs, rt, 0, 0, 24));
+}
+
+void INS_MULTU(void)
+{
+ unsigned int rs, rt;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rs = insArgv[0];
+ rt = insArgv[1];
+
+ OUTINS(R_TYPE(0, rs, rt, 0, 0, 25));
+}
+
+void INS_NOR(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 39) );
+}
+
+void INS_OR(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ if(atoiT[1] == T_INTEGER)
+ return INS_ORI();
+
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ if(atoiT[2] == T_INTEGER)
+ return INS_ORI();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 37) );
+}
+
+void INS_ORI(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // ANDI rt, rs, imm -> rt = rs + imm;
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ rs = insArgv[0];
+ imm = insArgv[1];
+ }
+ else
+ {
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = insArgv[2];
+ }
+
+ if(imm > 0xFFFF)
+ instruction_warning("Immediate is possibly out of range");
+
+ OUTINS( I_TYPE (13, rs, rt, imm) );
+}
+
+void INS_SB(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(40, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_SH(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(41, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_SLL(void)
+{
+ unsigned int rd, rt, sa;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rd = insArgv[0];
+ rt = insArgv[0];
+ sa = insArgv[1];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rt = insArgv[1];
+ sa = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(0, 0, rt, rd, sa, 0));
+}
+
+void INS_SLLV(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rd = insArgv[0];
+ rt = insArgv[0];
+ rs = insArgv[1];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rt = insArgv[1];
+ rs = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 4) );
+}
+
+void INS_SLT(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc != 3)
+ instruction_error("Wrong number of arguments");
+
+ if(atoiT[2] == T_INTEGER)
+ return INS_SLTI();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 42) );
+}
+
+void INS_SLTI(void)
+{
+ unsigned int imm, rs, rt;
+
+ if(insArgc != 3)
+ instruction_error("Wrong number of arguments");
+
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = insArgv[2];
+
+ if(imm > 0x7FFF && imm < 0xFFFF8000)
+ instruction_error("Immediate out of range.");
+
+ OUTINS( I_TYPE (10, rs, rt, imm) );
+}
+
+void INS_SLTIU(void)
+{
+ unsigned int imm, rs, rt;
+
+ if(insArgc != 3)
+ instruction_error("Wrong number of arguments");
+
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = insArgv[2];
+
+ if(imm > 0x7FFF && imm < 0xFFFF8000)
+ instruction_error("Immediate out of range.");
+
+ OUTINS( I_TYPE (11, rs, rt, imm) );
+}
+
+void INS_SLTU(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc != 3)
+ instruction_error("Wrong number of arguments");
+
+ if(atoiT[2] == T_INTEGER)
+ return INS_SLTIU();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 43) );
+}
+
+void INS_SRA(void)
+{
+ unsigned int rd, rt, sa;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rd = insArgv[0];
+ rt = insArgv[0];
+ sa = insArgv[1];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rt = insArgv[1];
+ sa = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(0, 0, rt, rd, sa, 3));
+}
+
+void INS_SRAV(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rd = insArgv[0];
+ rt = insArgv[0];
+ rs = insArgv[1];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rt = insArgv[1];
+ rs = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 7) );
+}
+
+void INS_SRL(void)
+{
+ unsigned int rd, rt, sa;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rd = insArgv[0];
+ rt = insArgv[0];
+ sa = insArgv[1];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rt = insArgv[1];
+ sa = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(0, 0, rt, rd, sa, 2));
+}
+
+void INS_SRLV(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rd = insArgv[0];
+ rt = insArgv[0];
+ rs = insArgv[1];
+ }
+ else
+ {
+ rd = insArgv[0];
+ rt = insArgv[1];
+ rs = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 6) );
+}
+
+void INS_SUB(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+
+ if(insArgc == 2)
+ {
+ if(atoiT[1] == T_INTEGER)
+ return INS_SUBI();
+
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ if(atoiT[2] == T_INTEGER)
+ return INS_SUBI();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 34) );
+}
+
+void INS_SUBU(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ // ADD rd, rs, rt -> rd = rs + rt
+
+ if(insArgc == 2)
+ {
+ if(atoiT[1] == T_INTEGER)
+ return INS_SUBIU();
+
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ if(atoiT[2] == T_INTEGER)
+ return INS_SUBIU();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 35) );
+}
+
+void INS_SW(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(43, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_SWC(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(56 | copn, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_SWL(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(42, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_SWR(void)
+{
+ unsigned int base, rt, offset;
+
+ SET_DIS_CHECK();
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = 0;
+ }
+ else
+ {
+ rt = insArgv[0];
+ offset = insArgv[1];
+ base = insArgv[2];
+ }
+
+ OUTINS(R_TYPE(46, 0, 0, 0, 0, 0) | compute_real_offset(offset, base, rt));
+}
+
+void INS_SYSCALL(void)
+{
+ unsigned int imm = 0;
+
+ if(insArgc > 1)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 1)
+ imm = insArgv[0];
+
+ imm &= 0xFFFFF;
+
+ OUTINS( (imm << 6) | 12 );
+}
+
+void INS_XOR(void)
+{
+ unsigned int rd, rs, rt;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ if(atoiT[1] == T_INTEGER)
+ return INS_XORI();
+
+ rd = insArgv[0];
+ rs = insArgv[0];
+ rt = insArgv[1];
+ }
+ else
+ {
+ if(atoiT[2] == T_INTEGER)
+ return INS_XORI();
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+ rt = insArgv[2];
+ }
+
+ OUTINS( R_TYPE (0, rs, rt, rd, 0, 38) );
+}
+
+void INS_XORI(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ rs = insArgv[0];
+ imm = insArgv[1];
+ }
+ else
+ {
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = insArgv[2];
+ }
+
+ if(imm > 0xFFFF)
+ instruction_warning("Immediate is possibly out of range");
+
+ OUTINS( I_TYPE (14, rs, rt, imm) );
+}
+
+// ***** PSEUDO INSTRUCTIONS *****
+
+void INS_B(void)
+{
+ unsigned int imm;
+
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ imm = insArgv[0];
+
+ OUTINS( I_TYPE(4, 0, 0, compute_branch(imm)) ); // <- beq zero, zero, imm
+}
+
+/*void INS_LI(void)
+{
+ unsigned int rd, imm;
+ unsigned short lopart, hipart;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rd = insArgv[0];
+ imm = insArgv[1];
+
+ hipart = imm >> 16;
+ lopart = imm & 0xFFFF;
+
+ if(atoiT[1] == T_INTEGER && imm >= 0 && imm <= 0xFFFF)
+ OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm
+ else
+ {
+ if(lopart >= 0x8000)
+ hipart++;
+
+ OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16)
+ OUTINS( I_TYPE (9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff
+ }
+}*/
+
+void INS_LI(void)
+{
+ int i;
+ unsigned int rd, imm;
+ unsigned short lopart, hipart;
+ int unpredictable=0;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rd = insArgv[0];
+ imm = insArgv[1];
+
+ hipart = imm >> 16;
+ lopart = imm & 0xFFFF;
+
+ if(!find_label_ok())
+ {
+ if(cannotPredict.size == cannotPredict.pos)
+ {
+ cannotPredict.size += 128;
+ cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int));
+ }
+
+ cannotPredict.el[cannotPredict.pos++] = curPc;
+ }
+
+ for(i = 0; i < cannotPredict.pos; i++)
+ {
+ if(curPc == cannotPredict.el[i])
+ {
+ unpredictable = 1;
+ break;
+ }
+ }
+
+ if(/*atoiT[1] == T_INTEGER &&*/ !unpredictable && imm >= 0 && imm <= 0xFFFF)
+ OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm
+ else if(!unpredictable && !lopart)
+ OUTINS(I_TYPE(15, 0, rd, hipart));
+ else
+ {
+ // if(lopart >= 0x8000)
+ // hipart++;
+
+ OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16)
+
+ // OUTINS( I_TYPE(9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff
+ OUTINS( I_TYPE (13, rd, rd, lopart)); // ori $rd, $rd, imm & 0xffff
+ }
+
+}
+
+void INS_LA(void)
+{
+ int i;
+ unsigned int rd, imm;
+ unsigned short lopart, hipart;
+ int unpredictable=0;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rd = insArgv[0];
+ imm = insArgv[1];
+
+ hipart = imm >> 16;
+ lopart = imm & 0xFFFF;
+
+ if(!find_label_ok())
+ {
+ if(cannotPredict.size == cannotPredict.pos)
+ {
+ cannotPredict.size += 128;
+ cannotPredict.el = realloc(cannotPredict.el, cannotPredict.size * sizeof(int));
+ }
+
+ cannotPredict.el[cannotPredict.pos++] = curPc;
+ }
+
+ for(i = 0; i < cannotPredict.pos; i++)
+ {
+ if(curPc == cannotPredict.el[i])
+ {
+ unpredictable = 1;
+ break;
+ }
+ }
+
+ if(/*atoiT[1] == T_INTEGER &&*/ !unpredictable && imm >= 0 && imm <= 0xFFFF)
+ OUTINS(I_TYPE(13, 0, rd, lopart)); // ori $rd, $zero, imm
+ //else if(!unpredictable && !lopart)
+ // OUTINS(I_TYPE(15, 0, rd, hipart));
+ else
+ {
+ if(lopart >= 0x8000)
+ hipart++;
+
+ OUTINS( I_TYPE(15, 0, rd, hipart)); // lui $rd, (imm > 16)
+
+ OUTINS( I_TYPE(9, rd, rd, lopart)); // addiu $rd, $rd, imm & 0xffff
+// OUTINS( I_TYPE (13, rd, rd, lopart)); // ori $rd, $rd, imm & 0xffff
+ }
+
+}
+
+/*void INS_LA(void)
+{
+ INS_LI(); // The LI and LA pseudo-instructions are the same thing in SPASM
+}*/
+
+void INS_NOP(void)
+{
+ OUTINS(0);
+}
+
+void INS_MOVE(void)
+{
+ unsigned int rd, rs;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rd = insArgv[0];
+ rs = insArgv[1];
+
+ OUTINS( R_TYPE (0, rs, 0, rd, 0, 33) ); // addu $rd, $rs, $zero
+}
+
+void INS_SUBI(void)
+{
+// just like ADDI, but switches the sign of the immediate
+
+ unsigned int rt, rs, imm;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ rs = insArgv[0];
+ imm = -insArgv[1];
+ }
+ else
+ {
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = -insArgv[2];
+ }
+
+ if(imm > 0x7FFF && imm < 0xFFFF0000)
+ instruction_warning("Immediate is possibly out of range");
+
+ OUTINS( I_TYPE (8, rs, rt, imm) );
+}
+
+void INS_SUBIU(void)
+{
+ unsigned int rt, rs, imm;
+
+ if(insArgc < 2)
+ instruction_error("Not enough arguments");
+ if(insArgc > 3)
+ instruction_error("Too many arguments");
+
+ if(insArgc == 2)
+ {
+ rt = insArgv[0];
+ rs = insArgv[0];
+ imm = -insArgv[1];
+ }
+ else
+ {
+ rt = insArgv[0];
+ rs = insArgv[1];
+ imm = -insArgv[2];
+ }
+
+ if(imm > 0x7FFF && imm < 0xFFFF0000)
+ instruction_warning("Immediate is possibly out of range");
+
+ OUTINS( I_TYPE (9, rs, rt, imm) );
+}
+
+void INS_BEQZ(void)
+{
+ unsigned int rt, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rt = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(4, rt, 0, compute_branch(imm)) ); // <- beq zero, rt, imm
+// OUTINS( I_TYPE(4, rs, rt, compute_branch(imm)) );
+
+}
+
+void INS_BNEZ(void)
+{
+ unsigned int rt, imm;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ rt = insArgv[0];
+ imm = insArgv[1];
+
+ OUTINS( I_TYPE(5,rt, 0, compute_branch(imm)) ); // <- bne zero, rt, imm
+}
+
+void INS_BAL(void)
+{
+ unsigned int imm;
+
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ imm = insArgv[0];
+
+ OUTINS( I_TYPE(1, 0, 17, compute_branch(imm)) ); //bgezal $zero, imm
+}
+
+void INS_ORG(void)
+{
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ //if(!first_instruction)
+ // instruction_error("ORG is not the first instruction");
+
+ curPc = insArgv[0];
+ startAddress = insArgv[0];
+ org_found = 1;
+}
+
+void INS_INCBIN(void)
+{
+ FILE *f;
+ char *path, *cp;
+ int sz;
+
+
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ path = rawArgv[0];
+
+ if(*path == '"')
+ {
+ path++;
+ if((cp = strrchr(path, '"')))
+ *cp = '\0';
+ }
+ else if(*path == '\'')
+ {
+ path++;
+ if((cp = strrchr(path, '\'')))
+ *cp = '\0';
+ }
+
+ //printf("DEBUG(INCBIN): including %s\n", path);
+
+ f = spasm_fopen(path, "rb");
+
+ if(!f)
+ instruction_error("Could not open \"%s\"", path);
+
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ if(curPass <= 0)
+ curPc += sz;
+ else
+ {
+ for(;sz;sz--)
+ OUTBYTE(fgetc(f));
+ }
+
+ fclose(f);
+}
+
+void INS_DCB(void)
+{
+ unsigned int num, value;
+
+ if(insArgc != 2)
+ instruction_error("Wrong number of arguments");
+
+ num = insArgv[0];
+ value = insArgv[1];
+
+ if(num & (1<<31))
+ {
+ instruction_warning("Negative number of values, ignoring instruction");
+
+ return;
+ }
+
+ if(curPass <= 0)
+ curPc += num;
+ else
+ {
+ for(;num;num--)
+ OUTBYTE(value);
+ }
+}
+
+void INS_DB(void)
+{
+ int i;
+
+ for(i = 0; i < insArgc; i++)
+ {
+ if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'')
+ OUTSTRING(rawArgv[i]);
+ else
+ OUTBYTE(insArgv[i] & 0xFF);
+ }
+}
+
+void INS_DH(void)
+{
+ int i;
+
+ for(i = 0; i < insArgc; i++)
+ {
+ if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'')
+ OUTSTRING(rawArgv[i]);
+ else
+ OUTHALF(insArgv[i] & 0xFFFF);
+ }
+}
+
+void INS_DW(void)
+{
+ int i;
+
+ for(i = 0; i < insArgc; i++)
+ {
+ if(rawArgv[i][0] == '"' || rawArgv[i][0] == '\'')
+ OUTSTRING(rawArgv[i]);
+ else
+ OUTWORD(insArgv[i]);
+ }
+}
+
+void INS_ALIGN(void)
+{
+ unsigned int unit;
+ unsigned int delta;
+
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ unit = insArgv[0];
+
+ if(unit <= 0)
+ instruction_error("Alignment unit cannot be equal to zero or negative");
+
+ if((curPc % unit))
+ {
+ delta = (unit - (curPc % unit)) % unit;
+
+ if(curPass <= 0)
+ curPc += delta;
+ else
+ {
+ for(;delta;delta--)
+ OUTBYTE(0);
+ }
+ }
+}
+
+void INS_INCLUDE(void)
+{
+ int i, l, o;
+ int sz;
+ int tsz;
+ FILE *f;
+ char *path;
+ char *cp;
+ char *newtext;
+
+ if(insArgc != 1)
+ instruction_error("Wrong number of arguments");
+
+ if(curPass <= 0)
+ {
+
+ path = rawArgv[0];
+
+ if(*path == '"')
+ {
+ path++;
+ if((cp = strrchr(path, '"')))
+ *cp = '\0';
+ }
+ else if(*path == '\'')
+ {
+ path++;
+ if((cp = strrchr(path, '\'')))
+ *cp = '\0';
+ }
+
+ f = spasm_fopen(path, "rb");
+
+ if(!f)
+ instruction_error("Could not open %s", path);
+
+ //printf("DEBUG(INCLUDE): Including %s, line %d\n", path, line_number);
+
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ tsz = strlen(curText);
+
+ newtext = malloc(sz + tsz + 1);
+
+ for(i = 0, l = 1; l < line_number; i++)
+ {
+ if(curText[i] == '\n')
+ l++;
+
+ newtext[i] = curText[i];
+ }
+
+ fread(&newtext[i], sizeof(char), sz, f);
+
+ o = i+sz;
+
+ if(newtext[i+sz-1] != '\n')
+ {
+ instruction_warning("No newline found at end of file %s", path);
+ newtext[o++] = '\n';
+ }
+
+
+ for(; curText[i] != '\n'; i++);
+
+ i++;
+ newtext[o] = '\0';
+
+ strcat(newtext, &curText[i]);
+
+ free(curText);
+ curText = newtext;
+ }
+}
+
+void INS_BLANK(void)
+{
+
+}
+
+void *get_instruction(char *name)
+{
+ int i;
+
+
+ for(i = 0; instruction_table[i].name; i++)
+ {
+ //printf("name = ^%s$, iname = ^%s$\n", name, instruction_table[i].name);
+
+ if(strcasecmp(name, instruction_table[i].name) == 0)
+ {
+ // printf("di sranron\n");
+ return instruction_table[i].func;
+ }
+ }
+
+ return NULL;
+}
diff --git a/tools/spasm/opcode.h b/tools/spasm/opcode.h
new file mode 100644
index 0000000..c2f6857
--- /dev/null
+++ b/tools/spasm/opcode.h
@@ -0,0 +1,108 @@
+#ifndef _SPASM_OPCODE_H
+#define _SPASM_OPCODE_H
+
+// Helper functions
+
+void OUTSEEK(unsigned int position);
+void OUTBYTE(unsigned char b);
+void OUTHALF(unsigned short h);
+void OUTWORD(unsigned int w);
+void OUTINS(unsigned int instruction);
+void OUTSTRING(char *string);
+unsigned int OUTSIZE(void);
+void *get_instruction(char *name);
+
+// Instructions
+
+void INS_ADD(void);
+void INS_ADDI(void);
+void INS_ADDIU(void);
+void INS_ADDU(void);
+void INS_AND(void);
+void INS_ANDI(void);
+void INS_BEQ(void);
+void INS_BGEZ(void);
+void INS_BGEZAL(void);
+void INS_BGTZ(void);
+void INS_BLEZ(void);
+void INS_BLTZ(void);
+void INS_BLTZAL(void);
+void INS_BNE(void);
+void INS_BREAK(void);
+void INS_CFC(void);
+void INS_COP(void);
+void INS_CTC(void);
+void INS_DIV(void);
+void INS_DIVU(void);
+void INS_J(void);
+void INS_JAL(void);
+void INS_JALR(void);
+void INS_JR(void);
+void INS_LB(void);
+void INS_LBU(void);
+void INS_LH(void);
+void INS_LHU(void);
+void INS_LUI(void);
+void INS_LW(void);
+void INS_LWC(void);
+void INS_LWL(void);
+void INS_LWR(void);
+void INS_MFC(void);
+void INS_MFHI(void);
+void INS_MFLO(void);
+void INS_MTC(void);
+void INS_MTHI(void);
+void INS_MTLO(void);
+void INS_MULT(void);
+void INS_MULTU(void);
+void INS_NOR(void);
+void INS_OR(void);
+void INS_ORI(void);
+void INS_SB(void);
+void INS_SH(void);
+void INS_SLL(void);
+void INS_SLLV(void);
+void INS_SLT(void);
+void INS_SLTI(void);
+void INS_SLTIU(void);
+void INS_SLTU(void);
+void INS_SRA(void);
+void INS_SRAV(void);
+void INS_SRL(void);
+void INS_SRLV(void);
+void INS_SUB(void);
+void INS_SUBU(void);
+void INS_SW(void);
+void INS_SWC(void);
+void INS_SWL(void);
+void INS_SWR(void);
+void INS_SYSCALL(void);
+void INS_XOR(void);
+void INS_XORI(void);
+
+// Pseudo instructions
+
+void INS_B(void);
+void INS_LI(void);
+void INS_LA(void);
+void INS_NOP(void);
+void INS_MOVE(void);
+void INS_SUBI(void);
+void INS_SUBIU(void);
+void INS_BEQZ(void);
+void INS_BNEZ(void);
+void INS_BAL(void);
+void INS_ORG(void);
+void INS_INCBIN(void);
+void INS_INCLUDE(void);
+void INS_DCB(void);
+void INS_DB(void);
+void INS_DH(void);
+void INS_DW(void);
+void INS_ALIGN(void);
+
+// Fake instructions
+
+void INS_BLANK(void);
+
+#endif
diff --git a/tools/spasm/parser.c b/tools/spasm/parser.c
new file mode 100644
index 0000000..46fdccb
--- /dev/null
+++ b/tools/spasm/parser.c
@@ -0,0 +1,478 @@
+#include "spasm.h"
+
+int atoiT[64];
+
+static char *reg_names[] =
+{
+ "zero",
+ "at",
+ "v0", "v1",
+ "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
+ "t8", "t9",
+ "k0", "k1",
+ "gp", "sp",
+ "fp",
+ "ra",
+ NULL
+};
+
+static int regtoi(char *arg)
+{
+ int i;
+
+ if(strcmp(arg, "s8") == 0)
+ arg = "fp";
+
+ for(i = 0; reg_names[i]; i++)
+ {
+ if(strcmp(arg, reg_names[i]) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+unsigned int asm_atoi(char *arg)
+{
+ unsigned int i;
+
+
+ if((i = regtoi(arg)) != -1)
+ {
+ atoiT[insArgc] = T_REGISTER;
+ return i;
+ }
+ else if(tolower((int)*arg) == 'r' &&( (strlen(arg) == 2 && isdigit((int)*(arg+1))) ||
+ (strlen(arg) == 3 && isdigit((int)*(arg+1)) && isdigit((int)*(arg+2)))))
+ {
+ sscanf(arg+1, "%d", &i);
+ atoiT[insArgc] = T_REGISTER;
+ return i;
+ }
+ else if(*arg == '-' && *(arg+1) == '$' && isxdigit((unsigned int)*(arg+2)) )
+ {
+ sscanf(arg+2, "%x", &i);
+ atoiT[insArgc] = T_INTEGER;
+ return -i;
+ }
+ else if(*arg == '$' )
+ {
+ sscanf(arg+1, "%x", &i);
+ atoiT[insArgc] = T_INTEGER;
+
+ return i;
+ }
+ else if(strcmp(arg, "*") == 0)
+ {
+ atoiT[insArgc] = T_INTEGER;
+
+ return curPc;
+ }
+ else if(isalpha((unsigned int)*arg) || (*arg) == '_')
+ {
+ atoiT[insArgc] = T_LABEL;
+
+ i = find_label(arg);
+
+ return i;
+ }
+
+ sscanf(arg, "%i", &i);
+ atoiT[insArgc] = T_INTEGER;
+
+ return i;
+}
+
+enum
+{
+ INITIAL, ARG_ENTER, COMMENT
+};
+
+char *spasm_parser(char *text, int pass)
+{
+ int i, j, l, m;
+ char linebuf[1024];
+ char linebuf2[1024];
+ char linebuf3[1024];
+ char argbuf[1024];
+ char *tok[256];
+ int state = INITIAL;
+ int num_of_tok=0;
+ char *t;
+ curText = text;
+ unsigned int v;
+
+theBeginning:
+ i = 0;
+ curPass = pass;
+ org_found = 0;
+ first_instruction = 1;
+ line_number = 0;
+ text = curText;
+
+ while(text[i])
+ {
+ state = INITIAL;
+
+ for(j = 0; text[i] && text[i] != '\n'; i++)
+ {
+ if(j < 1023 && text[i] != '\r')
+ linebuf[j++] = text[i];
+ }
+
+ line_number++;
+ rawArgc = insArgc = 0;
+ INSFUNC = NULL;
+
+ if(text[i] == '\n')
+ i++;
+
+ linebuf[j] = '\0';
+
+//tokenize_line:
+ strcpy(linebuf2, linebuf); // Keep a second copy, we will need it later.
+ strcpy(linebuf3, linebuf);
+ curLine = linebuf3;
+
+ char *a = linebuf;
+ char *s;
+ j = 0;
+
+ num_of_tok = 0;
+
+ for(m = 0; m < 256; m++)
+ tok[m]=NULL;
+
+ while((s = strtok(a, " \t")))
+ {
+ tok[num_of_tok++] = s;
+ a = NULL;
+ }
+
+ tok[num_of_tok] = NULL;
+
+ j = 0;
+
+
+
+ while((s = tok[j]))
+ {
+ //printf("tok[%d] = %s\n", j, tok[j]);
+
+ find_label_reset();
+
+ if(strlen(s) == 0)
+ { // A token with zero length is garbage, skip it
+ j++;
+ continue;
+ }
+
+ //printf("s = ^%s\n", s);
+
+ switch(state)
+ {
+ case INITIAL: // Initial case
+
+ // Is this token a comment?
+
+ if(*s == ';')
+ {
+ state = COMMENT;
+ break;
+ }
+
+ // Is this token an instruction?
+
+ if((INSFUNC = get_instruction(s)))
+ {
+ strncpy(curIns, s, 127);
+ s[127] = '\0';
+ insArgc = 0;
+ state = ARG_ENTER;
+ argbuf[0] = '\0';
+
+ break;
+ }
+
+ // Now we know it's a label
+ // There are two possible cases now
+ // - It's a label with a specified value: i.e. label EQU value
+ // - It's a label which has the current value of the program counter
+
+ // First, we will check for EQU
+
+ if((j + 2) < num_of_tok)
+ { // If there are not enough tokens in the line, don't bother checking for EQU.
+ if(strcasecmp(tok[j+1], "equ") == 0 || strcasecmp(tok[j+1], "=") == 0)
+ { // EQU found! Set label value to the one specified.
+
+ // Set current instruction to EQU
+ strcpy(curIns, "equ");
+
+ // Remove quotes. Yes, in SPASM, values in EQU statements can have quotes
+ // even if they are numerical values!
+
+ if( (t = strchr(tok[j+2], '"')) )
+ {
+ *t = '\0';
+ if( (t = strrchr(tok[j+2], '"')) )
+ *t = '\0';
+ }
+
+ find_label_reset();
+
+ v = spasm_eval(tok[j+2]);
+
+ if(strchr(tok[j+2], ';'))
+ state = COMMENT;
+
+ if(!find_label_ok())
+ assembler_error("Can't resolve expression for EQU statement");
+
+ if(find_label_ok())
+ add_label_equ(tok[j], v);
+ else
+ add_label(tok[j], v);
+
+ j+=2;// As we have processed the EQU, we need to jump two tokens ahead
+ break;
+ }
+ }
+
+ // At this point, it is a label which has the current value of the program counter
+
+ if((t = strrchr(tok[j], ':')))
+ *t='\0'; // Remove trailing colon, if any.
+
+ add_label(tok[j], curPc);
+ break;
+
+ case ARG_ENTER: // Inside instruction
+ if(curPass == -1)
+ break;
+
+ // Is this token a comment?
+ // If so, we do not have arguments anymore.
+
+
+ if(*s == ';')
+ {
+ state = COMMENT;
+ break;
+ }
+
+ strcat(argbuf, s);
+
+ int was_sp = 0;
+ int in_string = 0;
+ int stringt = 0;
+ char *argPlace = &linebuf2[ tok[j] - tok[0] ];
+
+ a = linebuf2;
+
+ l = 0;
+ while(l < j && (argPlace = strtok(a, " \t")))
+ {
+ a = NULL;
+ l++;
+ }
+
+ while(*argPlace)argPlace++;
+ while(!(*argPlace))argPlace++;
+
+ char arg[64];
+ char *argp = arg;
+ int is_ok = 0;
+ int esc = 0;
+
+ rawArgc = 0;
+
+ // Emulate a bug in Hitmen's assembler
+ if(strcasecmp(curIns, "li") == 0)
+ {
+argplace_li_remove_spaces_begin:
+ for(l = 0; argPlace[l]; l++)
+ {
+ if(argPlace[l] == ' ')
+ {
+ l++;
+ for(; argPlace[l]; l++)
+ argPlace[l-1] = argPlace[l];
+
+ argPlace[l-1] = '\0';
+
+ goto argplace_li_remove_spaces_begin;
+ }
+ }
+ }
+
+ for(l = 0; argPlace[l] && rawArgc < 64; l++)
+ {
+ char c = argPlace[l];
+
+ if(in_string)
+ {
+ *(argp++) = c;
+
+ if(!esc)
+ {
+ if(stringt == 0 && c == '"')
+ in_string = 0;
+ else if(stringt == 1 && c == '\'')
+ in_string = 0;
+ else if(c == '\\')
+ esc = 1;
+ }
+ else
+ esc = 0;
+ }
+ else
+ {
+ if(isalnum((unsigned int)c) || c == '_' || c == '$' || c == '.' || c == '*')
+ {
+ is_ok = 0;
+
+ if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') ||
+ (*(argp-1) == '$') || (*(argp-1) == '"')
+ || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*')))
+ goto noMoreArgs;
+
+ *(argp++) = c;
+ was_sp = 0;
+ }
+ else if(c == '"')
+ {
+ if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') ||
+ (*(argp-1) == '$') || (*(argp-1) == '"')
+ || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*')))
+ goto noMoreArgs;
+
+ *(argp++) = c;
+ was_sp = 0;
+ in_string = 1;
+ stringt = 0;
+ esc = 0;
+ }
+ else if(c == '\'')
+ {
+ if(was_sp && (isalnum((unsigned int)*(argp-1)) || (*(argp-1) == '_') ||
+ (*(argp-1) == '$') || (*(argp-1) == '"')
+ || (*(argp-1) == '\'') || (*(argp-1) == '.') || (*(argp-1) == '*')))
+ goto noMoreArgs;
+
+ *(argp++) = c;
+ was_sp = 0;
+ in_string = 1;
+ stringt = 1;
+ esc = 0;
+ }
+ else if(c == ' ' || c == '\t')
+ {
+ is_ok = 0;
+ was_sp = 1;
+ }
+ else if(c == '+' || c == '-' || c == '>' || c == '<'
+ || c == '(' || c == ')' || c == '&' || c == '|' || c == '!')
+ {
+ is_ok = 0;
+ *(argp++) = c;
+ }
+ else if(c == ',')
+ {
+ *argp = '\0';
+ insArgt[rawArgc] = 0;
+ strcpy(rawArgv[rawArgc++], arg);
+ argp = arg;
+ was_sp = 0;
+ is_ok = 1;
+ }
+ else if(c == ';' || c == '/')
+ {
+// '/' added in order to emulate a very buggy behavior of SPASM that is needed
+// in order to assemble the imbNES 1.3.2 sources without modifications.
+// yes, imbNES has some UNDENOTATED comments!
+//
+// pearls such as
+//
+// lw v0,$1074(v1) // this line would be at $DFAC where the jump goes from the patch
+//
+// and...
+//
+// lw v0,$DFFC(v0) load the address to jump back to that was set in _patch_card
+//
+// It makes no sense, but hey it works in the original SPASM!
+
+
+ goto noMoreArgs;
+ }
+ else
+ {
+ instruction_error("Invalid character!\n");
+ break;
+ }
+ }
+ }
+
+noMoreArgs:
+ if(!is_ok)
+ {
+ *argp = '\0';
+
+ char *fb = strchr(arg, '(');
+ char *sb = strrchr(arg, ')');
+
+
+ int pa=(*arg != '"' && *arg != '\'' && fb && sb && fb<sb);
+
+ if(pa)
+ {
+ *fb = '\0';
+ *sb = '\0';
+ insArgt[rawArgc+1] = 1;
+ strcpy(rawArgv[rawArgc+1], fb+1);
+ }
+
+ insArgt[rawArgc] = 0;
+ strcpy(rawArgv[rawArgc++], arg);
+
+ if(pa)rawArgc++;
+ }
+
+ insArgc = 0;
+
+ find_label_reset();
+
+ for(l = 0; l < rawArgc; l++, insArgc++)
+ insArgv[l] = spasm_eval(rawArgv[l]);
+
+ if(curPass == 1 && !find_label_ok())
+ instruction_error("Can't resolve expression");
+
+ goto theNextLine;
+ break;
+
+ case COMMENT: // Inside comment
+
+ break;
+ }
+
+ a = NULL;
+ j++;
+ }
+
+theNextLine:
+ if(INSFUNC)
+ {
+ if(curPass>=0)INSFUNC();
+
+ if(strcasecmp(curIns, "include") == 0 && curPass == 0)
+ goto theBeginning;
+
+ first_instruction = 0;
+ }
+ }
+
+ return curText;
+}
diff --git a/tools/spasm/parser.h b/tools/spasm/parser.h
new file mode 100644
index 0000000..72ecb0d
--- /dev/null
+++ b/tools/spasm/parser.h
@@ -0,0 +1,29 @@
+#ifndef _SPASM_PARSER_H
+#define _SPASM_PARSER_H
+
+enum
+{
+ T_INTEGER = 1, T_REGISTER = 2, T_LABEL = 3
+};
+
+extern char *fileBuffer;
+extern unsigned int fileBufferSize;
+
+extern char *curText;
+extern char *curLine;
+
+unsigned int asm_atoi(char *arg);
+char *spasm_parser(char *text, int pass);
+
+extern char curIns[128];
+extern unsigned int curInsArg;
+extern unsigned int curInsArgT;
+extern unsigned int insArgv[64];
+extern unsigned int insArgt[64];
+extern unsigned int insArgc;
+extern int rawArgc;
+extern char rawArgv[64][128];
+extern FILE *asmOut;
+extern int line_number;
+extern int atoiT[64];
+#endif
diff --git a/tools/spasm/readme.txt b/tools/spasm/readme.txt
new file mode 100644
index 0000000..8baa290
--- /dev/null
+++ b/tools/spasm/readme.txt
@@ -0,0 +1,120 @@
+nv-SPASM 0.34.1
+(c) 2014-2015 Giuseppe Gatta, a.k.a. nextvolume <tails92@gmail.com>
+-------------------------
+
+1. What is this?
+2. Features
+3. How to use
+4. License
+
+1. What is this?
+--------------------------
+
+nv-SPASM is a stand-alone MIPS assembler that can output either a little-endian flat binary file
+or Sony PlayStation PS-X EXE executable (the default).
+
+It is simple and lightweight, and it is ideal for people who just want to play around
+with assembly without bothering with too many details which would arise with
+more advanced tools, such as those provided by the GNU project.
+
+nv-SPASM is also meant to be compatible with the Hitmen group's SPASM assembler,
+which was an early tool for PlayStation homebrew programmers who had no access
+to more advanced software development kits, such as Net Yaroze or PsyQ.
+
+While to most a tool like SPASM isn't as useful as it was back in the day, due to the
+availability of freely usable software development kits like the PSXSDK, as already
+mentioned before there are still some use cases for which it's more feasible
+to use nv-SPASM instead of more advanced tools.
+
+And, why not? nv-SPASM is easier to hack and to learn from than more advanced,
+complex designs.
+
+You could use it as the assembler for your virtual machine, or even embed this assembler inside it.
+
+1. What is this not?
+--------------------------
+
+nv-SPASM isn't a complex and full-fledged macro assembler; in fact it does not
+even support macros currently.
+
+You can't easily mix code written in a high-level language with assembly code
+with nv-SPASM, as nv-SPASM is unable to output object files that can be linked
+against other object files.
+
+nv-SPASM does not currently support complex expressions, just very simple ones.
+
+2. Features
+--------------------------
+
+- Complete MIPS R3000 instruction set support, with the following pseudo-instructions
+ implemented:
+
+ - B - unconditional branch
+ - LA, LI - load label/integer inside a register (LA and LI are the same thing in nv-SPASM)
+ - NOP - No operation
+ - MOVE - Copy the contents of a register into another
+ - SUBI - Subtract immediate from register
+ - SUBIU - Subtract immediate unsigned from register
+ - BEQZ - Branch if register is Equal to Zero
+ - BNEZ - Branch if register is Not Equal to Zero
+ - BAL - unconditional branch-and-link
+ - ORG - move start address to the desired address
+ it relocates every instruction, and must be the first instruction.
+ - INCLUDE - include a source file
+ - INCBIN - include a binary file
+ - DCB - declare a block of bytes
+ - DB - declare bytes and/or strings
+ - DH - declare half-words and/or strings
+ - DW - declare words and/or strings
+ - ALIGN - align incoming code to the specified boundary
+
+- Compatibility with the Hitmen group's SPASM assembler and thus the possibility
+ of assembling software developed with it, barring bugs in nv-SPASM or the fact
+ that the original source code may depend on bugs in the Hitmen's SPASM assembler
+ in order to assemble correctly.
+
+- Very simple expression support, single operator, two operands.
+ For laymen, this means that while for instance 3+3 is rendered as 6,
+ 3+3+2 is rendered as 5, as if it were 3+2.
+ This is coherent with the original SPASM assembler.
+ Supported operators are +, -, >>, <<, & and |
+
+- Automatic generation of Sony PlayStation PS-X EXE executables.
+
+- Symbol names (labels) can be treated just like normal numerical values,
+ the numerical value being either the program counter value at the moment
+ the label was met by the assembler, or a value set with an EQU or = statement.
+
+
+3. How to use
+--------------------------
+
+nv-SPASM is really simple to use, and does not need any special explanation.
+Therefore, it will suffice to report its usage banner.
+
+nv-spASM version 0.34.1 (c) 2014-2015 nextvolume
+Assembler for the Sony PlayStation
+
+Usage: ./spasm [options] infile outfile
+
+Options: -b create binary file instead of PS-X EXE
+ -N do not pad executable
+ -S be case sensitive on file paths
+
+4. License
+--------------------------
+
+It is permitted to do (almost) anything with the nv-SPASM code and documentation.
+The only restriction is that all existing copyright notices must remain intact, and
+modifed versions must ship with the corresponding modified source code.
+
+fcaseopen.c is (C) 2009 Keith Bauer. Read fcaseopen.c for the license (BSD-style)
+
+5. Useful information
+--------------------------
+
+Hitmen's official website <http://www.hitmen-console.org>
+- The README file for Hitmen's original SPASM assembler is a good read,
+ as this assembler tries to closely follow it.
+- The GreenTro source code is another good resource, as it was developed with SPASM,
+ and thus it can be considered an example to write nv-SPASM assembly source code.
diff --git a/tools/spasm/spasm.c b/tools/spasm/spasm.c
new file mode 100644
index 0000000..c94f5a7
--- /dev/null
+++ b/tools/spasm/spasm.c
@@ -0,0 +1,167 @@
+#include "spasm.h"
+#include "fcaseopen.h"
+
+int rawArgc = 0;
+char rawArgv[64][128];
+char *curText;
+char *curLine;
+FILE *asmOut;
+int line_number;
+
+char *fileBuffer;
+unsigned int fileBufferSize;
+
+int open_case_sensitive = 0;
+
+static void titlecard(void)
+{
+ printf("nv-spASM version 0.34.1 (c) 2014-2015 nextvolume\n");
+}
+
+static void usage(char *prog_name)
+{
+ titlecard();
+ printf("Assembler for the Sony PlayStation\n");
+ printf("\n");
+ printf("Usage: %s [options] infile outfile\n", prog_name);
+ printf("\n");
+ printf("Options: -b create binary file instead of PS-X EXE\n");
+ printf(" -N do not pad executable\n");
+ printf(" -S be case sensitive on file paths\n");
+ printf("\n");
+}
+
+FILE *spasm_fopen(const char *path, const char *mode)
+{
+ if(open_case_sensitive)
+ return fopen(path, mode);
+
+ return fcaseopen(path, mode);
+}
+
+int main(int argc, char *argv[])
+{
+ int sz;
+ int farg=1;
+ int bfile=0;
+ int no_newline=0;
+ int no_pad=0;
+
+ if(argc < 3)
+ {
+ usage(argv[0]);
+ return EXIT_SUCCESS;
+ }
+
+ while(argv[farg][0] == '-')
+ {
+ if(strcmp(argv[farg], "-b") == 0)
+ bfile = 1; // Generate binary file
+ else if(strcmp(argv[farg], "-N") == 0)
+ no_pad = 1;
+ else if(strcmp(argv[farg], "-S") == 0)
+ open_case_sensitive = 1;
+ else
+ {
+ usage(argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ farg++;
+ }
+
+ FILE *f = spasm_fopen(argv[farg], "rb");
+
+ if(!f)
+ {
+ printf("Could not open file %s for reading! Exiting.\n", argv[farg]);
+ return EXIT_FAILURE;
+ }
+
+ fseek(f, 0, SEEK_END);
+ sz = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ fileBufferSize = sz + 2; // two newline bytes + two NUL bytes
+ fileBuffer = malloc(fileBufferSize);
+ fread(fileBuffer, sizeof(char), sz, f);
+
+ if(fileBuffer[sz-1] != '\n')
+ no_newline = 1;
+
+ fileBuffer[sz] = '\n';
+ fileBuffer[sz+1] = '\0';
+
+ //fileBuffer = process_includes(fileBuffer);
+
+ fclose(f);
+
+ asmOut = fopen(argv[farg+1], "wb");
+
+ if(!asmOut)
+ {
+ printf("Could not open file %s for writing! Exiting.\n", argv[farg+1]);
+ return EXIT_FAILURE;
+ }
+
+ startAddress = 0x80010000;
+
+ titlecard();
+
+ if(no_newline)
+ printf("Warning: No newline found at end of file.\n");
+
+
+ printf("\nPass 1\n");
+ codegen_init();
+ fileBuffer = spasm_parser(fileBuffer, -1);
+ fileBuffer = spasm_parser(fileBuffer, 0);
+
+ /*if(!org_found)
+ {
+ printf("Warning: no ORG directive found... defaulting to $80010000\n");
+ startAddress = 0x80010000;
+ }*/
+
+ curPc = startAddress;
+
+ printf("Pass 2\n");
+ printf("Writing output file %s ...\n", argv[farg+1]);
+
+ if(!bfile)
+ OUTSEEK(0x800);
+
+ fileBuffer = spasm_parser(fileBuffer, 1);
+
+ if(!bfile)
+ {
+ OUTSEEK(0x0);
+ OUTSTRING("\"PS-X EXE\""); // PSX-EXE magic
+ OUTSEEK(0x10);
+ OUTWORD(startAddress); // Initial program counter
+ OUTSEEK(0x18);
+ OUTWORD(startAddress); // Text section start address
+ OUTSEEK(0x30);
+ OUTWORD(0x801FFFF0); // Initial stack pointer
+
+ sz = OUTSIZE();
+
+
+ if(!no_pad && (sz & 2047)) // Pad executable
+ {
+ sz |= 2047;
+ sz++;
+ OUTSEEK(sz-1);
+ OUTBYTE(0);
+ }
+
+ OUTSEEK(0x1C);
+ OUTWORD(sz - 0x800); // Output size of text section
+ }
+
+ printf("\nAssembly complete\n\n");
+
+ fclose(asmOut);
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/spasm/spasm.h b/tools/spasm/spasm.h
new file mode 100644
index 0000000..b1ea821
--- /dev/null
+++ b/tools/spasm/spasm.h
@@ -0,0 +1,20 @@
+#ifndef _SPASM_H
+#define _SPASM_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include "error.h"
+#include "codegen.h"
+#include "parser.h"
+#include "opcode.h"
+#include "spasm.h"
+#include "eval.h"
+
+FILE *spasm_fopen(const char *path, const char *mode);
+
+#endif
diff --git a/tools/systemcnf.c b/tools/systemcnf.c
new file mode 100644
index 0000000..301936d
--- /dev/null
+++ b/tools/systemcnf.c
@@ -0,0 +1,50 @@
+/*
+ * systemcnf
+ *
+ * Small program that outputs a system.cnf file to standard output
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+int tcb = 4;
+int event = 16;
+unsigned int stack = 0x801FFFF0;
+
+int main(int argc, char *argv[])
+{
+ int x;
+
+ if(argc < 2)
+ {
+ printf("systemcnf\n");
+ printf("usage: systemcnf exe_name [tcb] [event] [stack_addr]\n");
+ printf("\n");
+ printf("This programs outputs a system.cnf file to standard output\n");
+ printf("Only the exe_name argument is necessary because if the others\n");
+ printf("are missing, the default values are used.\n");
+ printf("Specify stack_addr in hexadecimal, without prefixes.\n");
+ return 0;
+ }
+
+ if(argc >= 3)
+ tcb = atoi(argv[2]);
+
+ if(argc >= 4)
+ event = atoi(argv[3]);
+
+ if(argc >= 5)
+ sscanf(argv[4],"%x",&stack);
+
+ for(x=0;x<strlen(argv[1]);x++)
+ argv[1][x] = toupper((int)argv[1][x]);
+
+ printf("BOOT = cdrom:%s;1\n", argv[1]);
+ printf("TCB = %d\n", tcb);
+ printf("EVENT = %d\n", event);
+ printf("STACK = %X", stack);
+
+ return 0;
+}
diff --git a/tools/tim2bmp.c b/tools/tim2bmp.c
new file mode 100644
index 0000000..ccb0c99
--- /dev/null
+++ b/tools/tim2bmp.c
@@ -0,0 +1,390 @@
+/*
+ * TIM2BMP
+ * Converts TIM, emulator save state VRAM data and raw PSX data
+ * to Windows bitmap format
+ *
+ * Written by Giuseppe Gatta (a.k.a. nextvolume), part of PSXSDK
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#define PCSX_1_5_SAVESTATE_SUPPORT
+
+z_stream strm;
+unsigned short *tim_clut;
+
+typedef struct
+{
+ unsigned int clut_off;
+ unsigned int data_off;
+ unsigned short w; // Width in 16-bit unit pixels
+ unsigned short real_w; // Real width in pixels
+ unsigned short h;
+ unsigned char bpp;
+ unsigned char compr; // Compression - 0 = normal, 1 = GZIP
+ unsigned char has_clut;
+ unsigned short *clut;
+}tim2bmp_info;
+
+tim2bmp_info tim_info;
+
+#include "endian.c"
+
+int mpink_flag = 0;
+
+void rgbpsx_to_rgb24(unsigned short psx_c, unsigned char *r,
+ unsigned char *g, unsigned char *b)
+{
+ *r = (psx_c & 31) << 3;
+ *g = ((psx_c >> 5)&31) << 3;
+ *b = ((psx_c >> 10) &31) << 3;
+
+ if(mpink_flag && !(psx_c & 0x8000) && *r == 0 && *g == 0 && *b == 0)
+ {
+ *r = 255;
+ *g = 0;
+ *b = 255;
+ }
+}
+
+// Returns number of bytes to round image row with.
+int write_bitmap_headers(FILE *f, int w, int h, int bpp)
+{
+ int x;
+ int r;
+ int ret;
+
+ if(bpp == 16)
+ bpp = 24;
+
+ fputc('B', f);
+ fputc('M', f);
+
+ // Calculate and write size of bitmap
+
+ if(bpp == 24)
+ r = w * 3;
+ else if(bpp == 8)
+ r = w;
+ else if(bpp == 4)
+ r = w / 2;
+
+ ret = r;
+
+ if(r & 3)
+ {
+ r|=3;
+ r++;
+ }
+
+ ret = r-ret;
+
+ x=r*h;
+ x+=54;
+
+ if(bpp == 8)
+ x+= 1024;
+ else if(bpp == 4)
+ x+= 64;
+
+ write_le_dword(f, x);
+
+ // Write bfReserved1 and bfReserved2 as zero
+ write_le_dword(f, 0);
+
+ // Calculate and write data offset in file
+
+ x = 54;
+
+ if(bpp == 8)
+ x+= 1024;
+ else if(bpp == 4)
+ x+= 64;
+
+ write_le_dword(f, x);
+
+ write_le_dword(f, 40);
+ write_le_dword(f, w); // Width
+ write_le_dword(f, h); // Height
+ write_le_word(f, 1);
+ write_le_word(f, bpp); // Bits Per Pixel
+ write_le_dword(f, 0);
+ write_le_dword(f, r * h); // Image data size
+ write_le_dword(f, 0);
+ write_le_dword(f, 0);
+ write_le_dword(f, 0);
+ write_le_dword(f,0);
+
+ return ret;
+}
+
+int tim2bmp_read_tim(char *ip, tim2bmp_info *t)
+{
+ int tim_pmode;
+ //int tim_w, tim_h;
+ int /* tim_x, tim_y, tim_cx, tim_cy, */ tim_cw, tim_ch;
+ //int bl;
+ int x;
+ FILE *i = fopen(ip, "rb");
+
+ fseek(i,0,SEEK_SET);
+
+ if(read_le_dword(i) != 0x10)
+ {
+ fclose(i);
+ return -1;
+ }
+
+ tim_pmode = read_le_dword(i);
+ t->has_clut = (tim_pmode & 8) ? 1 : 0;
+ tim_pmode &= 7;
+
+ if(tim_pmode == 0) t->bpp = 4;
+ else if(tim_pmode == 1) t->bpp = 8;
+ else if(tim_pmode == 2) t->bpp = 16;
+ else if(tim_pmode == 3) t->bpp = 24;
+
+ if(t->has_clut)
+ {
+ t->clut_off = 8;
+ /* bl = */ read_le_dword(i);
+ /* tim_cx = */ read_le_word(i);
+ /* tim_cy = */ read_le_word(i);
+ tim_cw = read_le_word(i);
+ tim_ch = read_le_word(i);
+
+ t->clut = malloc((tim_cw * tim_ch) * sizeof(short));
+
+ for(x = 0; x < (tim_cw * tim_ch); x++)
+ t->clut[x] = read_le_word(i);
+ }
+
+ /* bl = */ read_le_dword(i);
+
+ // Read framebuffer X,Y coordinates
+
+ /* tim_x = */ read_le_word(i);
+ /* tim_y = */ read_le_word(i);
+
+ // Read width and height
+ t->w = read_le_word(i); // Fix this for 4bpp and 8bpp images !
+ t->h = read_le_word(i);
+
+ switch(tim_pmode)
+ {
+ case 0: t->real_w = t->w * 4; break;
+ case 1: t->real_w = t->w * 2; break;
+ case 2:
+ t->real_w = t->w;
+ break;
+ }
+
+ t->data_off = ftell(i);
+ t->compr = 0;
+
+ fclose(i);
+
+ return 1;
+}
+
+int tim2bmp_read_pcsx15(char *ip, tim2bmp_info *t)
+{
+ t->w = 1024;
+ t->real_w = 1024;
+ t->h = 512;
+ t->bpp = 16;
+ t->has_clut = 0;
+ t->clut_off = 0;
+ t->data_off = 0x2996C0;
+ t->compr = 1;
+
+ return 1;
+}
+
+void tim2bmp_convert_image_data(char *ip, char *fp, tim2bmp_info *t)
+{
+ int row_round;
+ int y,x;
+ //int z;
+ int tim_row_off;
+ unsigned short c;
+ unsigned char r, g, b;
+ gzFile gzf;
+ //unsigned char test[17];
+ FILE *i = fopen(ip, "rb");
+ FILE *f = fopen(fp, "wb");
+
+ if(t->compr == 1)
+ gzf = gzopen(ip, "rb");
+
+ write_bitmap_headers(f, t->real_w, t->h, t->bpp);
+
+ if(t->has_clut)
+ {
+ if(t->bpp == 4) // 4bpp
+ {
+ for(x = 0; x < 16; x++)
+ {
+ rgbpsx_to_rgb24(t->clut[x], &r, &g, &b);
+ fputc(b, f);
+ fputc(g, f);
+ fputc(r, f);
+ fputc(0, f);
+ }
+ }
+ else if(t->bpp == 8) // 8 bpp
+ {
+ for(x = 0; x < 256; x++)
+ {
+ rgbpsx_to_rgb24(t->clut[x], &r, &g, &b);
+ fputc(b, f);
+ fputc(g, f);
+ fputc(r, f);
+ fputc(0, f);
+ }
+ }
+ }
+ else
+ {
+ if(t->bpp == 4) fseek(f, 64, SEEK_CUR);
+ else if(t->bpp == 8) fseek(f, 1024, SEEK_CUR);
+ }
+
+ if(t->bpp == 16)
+ y = (t->real_w * 24) / 8;
+ else
+ y = (t->real_w * t->bpp) / 8;
+
+// printf("y = %d\n", y);
+ row_round = y;
+// printf("row_round = %d\n", y);
+
+ if(row_round & 3)
+ {
+ row_round |= 3;
+ row_round++;
+ }
+// printf("row_round = %d\n", y);
+
+ row_round -= y;
+// printf("row_round = %d\n", row_round);
+
+ for(y = 0; y < t->h; y++)
+ {
+ tim_row_off = (t->w * 2) * ((t->h - 1)-y);
+
+ if(t->compr == 1)
+ gzseek(gzf, t->data_off + tim_row_off, SEEK_SET);
+ else
+ fseek(i, t->data_off + tim_row_off, SEEK_SET);
+
+ // printf("ERRNO SHY = %s\n", gzerror(gzf, &x));
+
+
+ for(x = 0; x < t->w; x++)
+ {
+ if(t->compr == 1)
+ {
+ gzread(gzf, &b, 1);
+ c = b;
+ gzread(gzf, &b, 1);
+ c|=b<<8;
+ }
+ else
+ c = read_le_word(i);
+
+ switch(t->bpp)
+ {
+ case 4:
+ fputc(((c >> 4) & 0xf) | ((c & 0xf) << 4), f);
+ fputc(((c >> 12) & 0xf) | (((c>>8)&0xf)<<4), f);
+ break;
+ case 8:
+ write_le_word(f, c);
+ break;
+ case 16:
+ rgbpsx_to_rgb24(c, &r, &g, &b);
+
+ fputc(b, f);
+ fputc(g, f);
+ fputc(r, f);
+ break;
+ }
+ }
+
+ for(x = 0; x < row_round; x++)
+ fputc(0, f);
+ }
+
+ fclose(i);
+ fclose(f);
+ if(t->compr == 1) gzclose(gzf);
+}
+
+int main(int argc, char *argv[])
+{
+ //int x, y;
+ int x;
+ FILE *i;
+ //int bl;
+ /*int tim_w, tim_h, tim_x, tim_y, tim_cx, tim_cy, tim_cw, tim_ch;
+ int row_round;
+ unsigned short c;*/
+ //unsigned char r, g, b;
+ /*int tim_pdata_fpos;
+ int tim_row_off;
+ int tim_pmode;
+ int tim_has_clut;
+ int actual_w;
+ int bmp_bpp;*/
+ int r;
+ tim2bmp_info *t = &tim_info;
+
+ if(argc < 2)
+ {
+ printf("tim2bmp - converts a TIM image to a bitmap\n");
+ printf("usage: tim2bmp <intim> <outbmp> [options]\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -o=<offset>\n");
+ printf(" -mpink - Convert transparency to magic pink\n");
+ printf("\n");
+ return -1;
+ }
+
+ /*strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ r = deflateInit(&strm, 1);*/
+ //printf("r = %d, Z_OK = %d\n", r, Z_OK);
+
+ for(x = 3; x < argc; x++)
+ {
+ if(strcmp(argv[x], "-mpink") == 0)
+ mpink_flag = 1;
+ }
+
+ i = fopen(argv[1], "rb");
+
+ if(i == NULL)
+ {
+ printf("Couldn't open specified TIM file for reading.\n");
+ return -1;
+ }
+
+ fclose(i);
+
+ r = tim2bmp_read_tim(argv[1], &tim_info);
+
+ if(r != 1)
+ r = tim2bmp_read_pcsx15(argv[1], &tim_info);
+
+ if(argc > 2)
+ tim2bmp_convert_image_data(argv[1], argv[2], t);
+
+ return 0;
+}
diff --git a/tools/vag2wav.c b/tools/vag2wav.c
new file mode 100755
index 0000000..09b474a
--- /dev/null
+++ b/tools/vag2wav.c
@@ -0,0 +1,202 @@
+/*
+ * vag2wav
+ *
+ * Converts a PlayStation VAG sound file to a WAV file
+ *
+ * Based on VAG-Depack 0.1 by bITmASTER
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "endian.c"
+
+double f[5][2] = { { 0.0, 0.0 },
+ { 60.0 / 64.0, 0.0 },
+ { 115.0 / 64.0, -52.0 / 64.0 },
+ { 98.0 / 64.0, -55.0 / 64.0 },
+ { 122.0 / 64.0, -60.0 / 64.0 } };
+
+double samples[28];
+
+int main( int argc, char *argv[] )
+{
+ FILE *vag, *pcm;
+ char vag_name[17];
+ int predict_nr, shift_factor, flags;
+ int i;
+ int d, s;
+ static double s_1 = 0.0;
+ static double s_2 = 0.0;
+ int sz;
+ unsigned int samp_freq;
+ unsigned int data_size;
+
+ if(argc < 3)
+ {
+ printf("vag2wav - Convert a PlayStation VAG sound file to a WAV file\n");
+ printf("usage: vag2wav [vag] [wav file]\n");
+ printf("\n");
+ printf("This utility is based on PSX VAG-Depack by bITmASTER\n");
+ return( -1 );
+ }
+
+ vag = fopen( argv[1], "rb" );
+
+ fread(vag_name, sizeof(char), 4, vag);
+
+ if(vag == NULL)
+ {
+ printf("Can't open %s. Aborting.\n", argv[1]);
+ return -1;
+ }
+
+ if(strncmp(vag_name, "VAGp", 4))
+ {
+ printf("%s is not in VAG format. Aborting.\n", argv[1]);
+ return -1;
+ }
+
+ fseek(vag, 4, SEEK_SET);
+ i = fgetc(vag) << 24;
+ i |= fgetc(vag) << 16;
+ i |= fgetc(vag) << 8;
+ i |= fgetc(vag);
+
+ printf("Version: %x\n", i);
+
+ fseek(vag, 12, SEEK_SET);
+ data_size = fgetc(vag) << 24;
+ data_size |= fgetc(vag) << 16;
+ data_size |= fgetc(vag) << 8;
+ data_size |= fgetc(vag);
+
+ printf("Data size: %d bytes\n", data_size);
+
+ fseek(vag, 32, SEEK_SET);
+ fread(vag_name, sizeof(char), 16, vag);
+ vag_name[16] = 0;
+
+ printf("Name: %s\n", vag_name);
+
+ fseek(vag, 16, SEEK_SET);
+
+ samp_freq = fgetc(vag)<<24;
+ samp_freq|=fgetc(vag)<<16;
+ samp_freq|=fgetc(vag)<<8;
+ samp_freq|=fgetc(vag);
+
+ printf("Sampling frequency: %d\n", samp_freq);
+
+ fseek( vag, 64, SEEK_SET );
+
+ /* strcpy( fname, argv[1] );
+ p = strrchr( fname, '.' );
+ p++;
+ strcpy( p, "PCM" );*/
+ pcm = fopen( argv[2], "wb" );
+
+ if ( pcm == NULL ) {
+ printf( "canīt write output file\n" );
+ return( -8 );
+ }
+
+
+// Write header chunk
+ fprintf(pcm, "RIFF");
+// Skip file size field for now
+ fseek(pcm, 4, SEEK_CUR);
+
+ fprintf(pcm, "WAVE");
+// Write fmt chunk
+ fprintf(pcm, "fmt ");
+// Write chunk 1 size in little endian format
+ fputc(0x10, pcm);
+ fputc(0, pcm);
+ fputc(0, pcm);
+ fputc(0, pcm);
+// Write audio format (1 = PCM)
+ fputc(1, pcm);
+ fputc(0, pcm);
+// Number of channels (1)
+ fputc(1, pcm);
+ fputc(0, pcm);
+// Write sample rate
+ fputc((samp_freq & 0xff), pcm);
+ fputc((samp_freq >> 8) & 0xff, pcm);
+ fputc(0, pcm);
+ fputc(0, pcm);
+// Write byte rate (SampleRate * NumChannels * BitsPerSample/8)
+// That would be 44100*1*(16/8), thus 88200.
+ fputc(((samp_freq*2) & 0xff), pcm);
+ fputc(((samp_freq*2) >> 8) & 0xff, pcm);
+ fputc(((samp_freq*2) >> 16) & 0xff, pcm);
+ fputc(((samp_freq*2) >> 24) & 0xff, pcm);
+// Write block align (NumChannels * BitsPerSample/8), thus 2
+ fputc(2, pcm);
+ fputc(0, pcm);
+// Write BitsPerSample
+ fputc(16, pcm);
+ fputc(0, pcm);
+
+// Write data chunk
+ fprintf(pcm, "data");
+
+// Skip SubChunk2Size, we will return to it later
+ fseek(pcm, 4, SEEK_CUR);
+
+// Now write data...
+
+ while( ftell(vag) < (data_size+48)) {
+ predict_nr = fgetc( vag );
+ shift_factor = predict_nr & 0xf;
+ predict_nr >>= 4;
+ flags = fgetc( vag ); // flags
+ if ( flags == 7 )
+ break;
+ for ( i = 0; i < 28; i += 2 ) {
+ d = fgetc( vag );
+ s = ( d & 0xf ) << 12;
+ if ( s & 0x8000 )
+ s |= 0xffff0000;
+ samples[i] = (double) ( s >> shift_factor );
+ s = ( d & 0xf0 ) << 8;
+ if ( s & 0x8000 )
+ s |= 0xffff0000;
+ samples[i+1] = (double) ( s >> shift_factor );
+ }
+
+ for ( i = 0; i < 28; i++ ) {
+ samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
+ s_2 = s_1;
+ s_1 = samples[i];
+ d = (int) ( samples[i] + 0.5 );
+ fputc( d & 0xff, pcm );
+ fputc( d >> 8, pcm );
+ }
+ }
+
+// Get file size
+ sz = ftell(pcm);
+
+// Now write ChunkSize
+ fseek(pcm, 4, SEEK_SET);
+
+ fputc((sz-8) & 0xff, pcm);
+ fputc(((sz-8)>>8)&0xff, pcm);
+ fputc(((sz-8)>>16)&0xff,pcm);
+ fputc(((sz-8)>>24)&0xff,pcm);
+
+// Now write Subchunk2Size
+ fseek(pcm, 40, SEEK_SET);
+
+ fputc((sz-44) & 0xff, pcm);
+ fputc(((sz-44)>>8)&0xff, pcm);
+ fputc(((sz-44)>>16)&0xff,pcm);
+ fputc(((sz-44)>>24)&0xff,pcm);
+
+ fclose( pcm );
+ fclose( vag );
+ return( 0 );
+}
+
diff --git a/tools/wav2vag.c b/tools/wav2vag.c
new file mode 100644
index 0000000..e78b84c
--- /dev/null
+++ b/tools/wav2vag.c
@@ -0,0 +1,431 @@
+/*
+ * wav2vag
+ *
+ * Converts a WAV file to a PlayStation VAG file.
+ * Based on PSX VAG-Packer 0.1 by bITmASTER.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <math.h>
+
+#include "endian.c"
+
+#define BUFFER_SIZE 128*28
+
+short wave[BUFFER_SIZE];
+
+void find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor );
+void pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor );
+void fputi( int d, FILE *fp );
+
+int main( int argc, char *argv[] )
+{
+ FILE *fp, *vag;
+ short *ptr;
+ double d_samples[28];
+ short four_bit[28];
+ int predict_nr;
+ int shift_factor;
+ int flags;
+ int size;
+ int i, j, k;
+ unsigned char d;
+ char s[4];
+ int chunk_data;
+ short e;
+ int sample_freq = 0, sample_len;
+ char internal_name[16];
+ int enable_looping = 0;
+ int raw_output = 0;
+ int sraw = 0;
+ short sample_size;
+ unsigned char c;
+
+ if (argc < 3)
+ {
+ printf("wav2vag - Convert a WAV file to a PlayStation VAG sound file\n");
+ printf("usage: wav2vag [wav] [vag] <options>\n");
+ printf("\n");
+ printf("WAV files must have one channel (mono)\n");
+ printf("WAV data format must be either unsigned 8-bit or signed 16-bit PCM\n");
+ printf("\n");
+ printf("Options:\n");
+ printf(" -L - Make a looping sample (when it ends it is played again)\n");
+ printf(" -name=<name> - Set sample name\n");
+ printf(" -raw - Output only data, without VAG header\n");
+ printf(" -sraw8 - Source is RAW data, in 8-bit format\n");
+ printf(" -sraw16 - Source is RAW data, in 16-bit format\n");
+ printf(" -freq=<freq> - Force frequency in output VAG\n");
+ printf("\n");
+ printf("This utility is based on PSX VAG-Packer by bITmASTER\n");
+ return -1;
+ }
+
+ for(i = 0; i < sizeof(internal_name); i++)
+ internal_name[i] = 0;
+
+ strcpy(internal_name, "PSXSDK");
+
+ fp = fopen(argv[1], "rb");
+ if (fp == NULL)
+ {
+ printf("Canīt open %s. Aborting.\n", argv[1]);
+ return -2;
+ }
+
+ for(i = 3; i < argc; i++)
+ {
+ if(strcmp(argv[i], "-L") == 0)
+ enable_looping = 1;
+
+ if(strncmp(argv[i], "-name=",6) == 0)
+ strncpy(internal_name, argv[i]+6, 15);
+
+ if(strcmp(argv[i], "-raw") == 0)
+ raw_output = 1;
+
+ if(strcmp(argv[i], "-sraw8") == 0)
+ {
+ fseek(fp, 0, SEEK_END);
+ sample_len = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ sample_size = 8;
+ sample_freq = 44010;
+ sraw = 1;
+ }
+
+ if(strcmp(argv[i], "-sraw16") == 0)
+ {
+ fseek(fp, 0, SEEK_END);
+ sample_len = ftell(fp) / 2;
+ fseek(fp, 0, SEEK_SET);
+ sample_size = 16;
+ sample_freq = 44010;
+ sraw = 1;
+ }
+
+ if(strncmp(argv[i], "-freq=", 6) == 0)
+ {
+ sscanf(argv[i], "-freq=%d", &sample_freq);
+ }
+ }
+
+
+ if(sraw == 1)
+ goto convert_to_vag;
+
+ fread(s, 1, 4, fp);
+ if (strncmp(s, "RIFF", 4))
+ {
+ printf("%s is not in WAV format\n", argv[1]);
+ return -3;
+ }
+
+ fseek(fp, 8, SEEK_SET);
+ fread(s, 1, 4, fp);
+
+ if (strncmp(s, "WAVE", 4))
+ {
+ printf("%s is not in WAV format\n", argv[1]);
+ return -3;
+ }
+
+ fseek(fp, 8 + 4, SEEK_SET);
+ fread(s, 1, 4, fp);
+
+ if (strncmp(s, "fmt", 3))
+ {
+ printf("%s is not in WAV format\n", argv[1]);
+ return -3;
+ }
+
+ // fread(&chunk_data, sizeof(int), 1, fp);
+ chunk_data = read_le_dword(fp);
+ chunk_data += ftell(fp);
+
+ // fread(&e, 2, 1, fp);
+ e = read_le_word(fp);
+
+ if (e!=1)
+ {
+ printf("No PCM found in %s. Aborting.\n", argv[1]);
+ return -4;
+ }
+
+// fread(&e, 2, 1, fp);
+ e = read_le_word(fp);
+
+ if (e!=1)
+ {
+ //printf( "must be MONO\n" );
+ printf("WAV file must have only one channel. Aborting.\n");
+ return -5;
+ }
+
+ if(sample_freq != 0)
+ fseek(fp, 4, SEEK_CUR);
+ else
+ //fread(&sample_freq, 4, 1, fp);
+ sample_freq = read_le_dword(fp);
+
+ fseek(fp, 4 + 2, SEEK_CUR);
+
+// fread(&sample_size, 2, 1, fp);
+ sample_size = read_le_word(fp);
+
+ /* if (e!=16)
+ {
+ //printf( "only 16 bit samples\n" );
+ printf("The size of the samples of the WAV file must be 16-bit."
+ "Aborting.\n");
+ return -6;
+ }*/
+
+ fseek(fp, chunk_data, SEEK_SET);
+
+ fread(s, 1, 4, fp);
+
+ if (strncmp(s, "data", 4))
+ {
+ printf("No data chunk in %s. Aborting.\n", argv[1]);
+ return -7;
+ }
+
+ // fread(&sample_len, 4, 1, fp);
+ sample_len = read_le_dword(fp);
+
+ if(sample_size == 16)
+ sample_len /= 2;
+
+ /*strcpy( fname, argv[1] );
+ p = strrchr( fname, '.' );
+ p++;
+ strcpy( p, "vag" );*/
+convert_to_vag:
+ vag = fopen(argv[2], "wb");
+
+ if (vag == NULL)
+ {
+ printf("Can't open output file. Aborting.\n");
+ return -8;
+ }
+
+/* strcpy(internal_name, "PSXSDK");
+
+ for(i = 3; i < argc; i++)
+ {
+ if(strcmp(argv[i], "-L") == 0)
+ enable_looping = 1;
+
+ if(strncmp(argv[i], "-name=",6) == 0)
+ strncpy(internal_name, argv[i]+6, 15);
+
+ if(strcmp(argv[i], "-raw") == 0)
+ raw_output = 1;
+ }
+*/
+
+ if(raw_output == 0)
+ {
+ fprintf( vag, "VAGp" ); // ID
+ fputi( 0x20, vag ); // Version
+ fputi( 0x00, vag ); // Reserved
+ size = sample_len / 28;
+ if( sample_len % 28 )
+ size++;
+ fputi( 16 * ( size + 2 ), vag ); // Data size
+ fputi( sample_freq, vag ); // Sampling frequency
+
+ for ( i = 0; i < 12; i++ ) // Reserved
+ fputc( 0, vag );
+
+ fwrite(internal_name, sizeof(char), 16, vag);
+
+ for( i = 0; i < 16; i++ )
+ fputc( 0, vag ); // ???
+ }
+
+ if(enable_looping)
+ flags = 6;
+ else
+ flags = 0;
+
+ while( sample_len > 0 ) {
+ size = ( sample_len >= BUFFER_SIZE ) ? BUFFER_SIZE : sample_len;
+
+ if(sample_size == 8)
+ {
+ for(i = 0; i < size; i++)
+ {
+ c = fgetc(fp);
+ wave[i] = c;
+ wave[i] ^= 0x80;
+ wave[i] <<= 8;
+ }
+ }
+ else
+ {
+ // fread( wave, sizeof( short ), size, fp );
+ for(i = 0; i < size; i++)
+ wave[i] = read_le_word(fp);
+ }
+
+ i = size / 28;
+ if ( size % 28 ) {
+ for ( j = size % 28; j < 28; j++ )
+ wave[28*i+j] = 0;
+ i++;
+ }
+
+ for ( j = 0; j < i; j++ ) { // pack 28 samples
+ ptr = wave + j * 28;
+ find_predict( ptr, d_samples, &predict_nr, &shift_factor );
+ pack( d_samples, four_bit, predict_nr, shift_factor );
+ d = ( predict_nr << 4 ) | shift_factor;
+ fputc( d, vag );
+ fputc( flags, vag );
+ for ( k = 0; k < 28; k += 2 ) {
+ d = ( ( four_bit[k+1] >> 8 ) & 0xf0 ) | ( ( four_bit[k] >> 12 ) & 0xf );
+ fputc( d, vag );
+ }
+ sample_len -= 28;
+ if ( sample_len < 28 && enable_looping == 0)
+ flags = 1;
+
+ if(enable_looping)
+ flags = 2;
+ }
+ }
+
+ fputc( ( predict_nr << 4 ) | shift_factor, vag );
+
+ if(enable_looping)
+ fputc(3, vag);
+ else
+ fputc( 7, vag ); // end flag
+
+ for ( i = 0; i < 14; i++ )
+ fputc( 0, vag );
+
+ fclose( fp );
+ fclose( vag );
+// getch();
+ return( 0 );
+}
+
+
+static double f[5][2] = { { 0.0, 0.0 },
+ { -60.0 / 64.0, 0.0 },
+ { -115.0 / 64.0, 52.0 / 64.0 },
+ { -98.0 / 64.0, 55.0 / 64.0 },
+ { -122.0 / 64.0, 60.0 / 64.0 } };
+
+
+
+void find_predict( short *samples, double *d_samples, int *predict_nr, int *shift_factor )
+{
+ int i, j;
+ double buffer[28][5];
+ double min = 1e10;
+ double max[5];
+ double ds;
+ int min2;
+ int shift_mask;
+ static double _s_1 = 0.0; // s[t-1]
+ static double _s_2 = 0.0; // s[t-2]
+ double s_0, s_1, s_2;
+
+ for ( i = 0; i < 5; i++ ) {
+ max[i] = 0.0;
+ s_1 = _s_1;
+ s_2 = _s_2;
+ for ( j = 0; j < 28; j ++ ) {
+ s_0 = (double) samples[j]; // s[t-0]
+ if ( s_0 > 30719.0 )
+ s_0 = 30719.0;
+ if ( s_0 < - 30720.0 )
+ s_0 = -30720.0;
+ ds = s_0 + s_1 * f[i][0] + s_2 * f[i][1];
+ buffer[j][i] = ds;
+ if ( fabs( ds ) > max[i] )
+ max[i] = fabs( ds );
+// printf( "%+5.2f\n", s2 );
+ s_2 = s_1; // new s[t-2]
+ s_1 = s_0; // new s[t-1]
+ }
+
+ if ( max[i] < min ) {
+ min = max[i];
+ *predict_nr = i;
+ }
+ if ( min <= 7 ) {
+ *predict_nr = 0;
+ break;
+ }
+
+ }
+
+// store s[t-2] and s[t-1] in a static variable
+// these than used in the next function call
+
+ _s_1 = s_1;
+ _s_2 = s_2;
+
+ for ( i = 0; i < 28; i++ )
+ d_samples[i] = buffer[i][*predict_nr];
+
+// if ( min > 32767.0 )
+// min = 32767.0;
+
+ min2 = ( int ) min;
+ shift_mask = 0x4000;
+ *shift_factor = 0;
+
+ while( *shift_factor < 12 ) {
+ if ( shift_mask & ( min2 + ( shift_mask >> 3 ) ) )
+ break;
+ (*shift_factor)++;
+ shift_mask = shift_mask >> 1;
+ }
+
+}
+
+void pack( double *d_samples, short *four_bit, int predict_nr, int shift_factor )
+{
+ double ds;
+ int di;
+ double s_0;
+ static double s_1 = 0.0;
+ static double s_2 = 0.0;
+ int i;
+
+ for ( i = 0; i < 28; i++ ) {
+ s_0 = d_samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
+ ds = s_0 * (double) ( 1 << shift_factor );
+
+ di = ( (int) ds + 0x800 ) & 0xfffff000;
+
+ if ( di > 32767 )
+ di = 32767;
+ if ( di < -32768 )
+ di = -32768;
+
+ four_bit[i] = (short) di;
+
+ di = di >> shift_factor;
+ s_2 = s_1;
+ s_1 = (double) di - s_0;
+
+ }
+}
+
+void fputi( int d, FILE *fp )
+{
+ fputc( d >> 24, fp );
+ fputc( d >> 16, fp );
+ fputc( d >> 8, fp );
+ fputc( d, fp );
+}