commit f5f03a72cf4a1df4c1e2916f2823b45cdf7e99fd
parent 11a41ffbdba04d88382988ca5937f9d1b1c7d2d2
Author: lostd <lostd@2f30.org>
Date: Mon, 2 Sep 2013 14:10:49 +0300
dreamcast selfboot burning guide and tools
Diffstat:
7 files changed, 682 insertions(+), 0 deletions(-)
diff --git a/selfboot/1strdchk.c b/selfboot/1strdchk.c
@@ -0,0 +1,159 @@
+/***********************************************************************************
+ * 1st_read.bin File Checker - Visual Basic to C conversion
+ *
+ * LyingWake <LyingWake@gmail.com>
+ * http://www.consolevision.com/members/fackue/
+ *
+ * This is a port of 1st_read.bin File Checker 1.5's source code
+ * from Visual Basic 6.0 to Microsoft Visual C++.
+ *
+ * Thanks to all those that helped; JustBurn, Quzar, GPFerror and
+ * and anyond else I forgot.
+ ***********************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+ #include <io.h>
+#else
+ #include <unistd.h>
+ #define _O_BINARY 0
+ #define _O_RDONLY O_RDONLY
+#endif
+
+#define Unscrambled 0
+#define Scrambled 1
+
+#ifndef _WIN32
+ long _filelength(int fd) {
+ long pos;
+ lseek(fd, 0, SEEK_SET);
+ pos = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ return pos;
+ }
+#endif
+
+char *memoryspace; /* A pointer that will be stored the file */
+
+////////////////////////////////////////////////////////////////////////////////////
+
+/* //////////////////////////////////////////////////////////////////// */
+/* // // */
+/* // Function: IdentifyBin // */
+/* // Description: Reads file, checks if any given string is inside, // */
+/* // if so, it's unscrambled, else it is scrambled. // */
+/* // // */
+/* // Returns: integer (rIdentifyBin) // */
+/* // // */
+/* //////////////////////////////////////////////////////////////////// */
+
+int IdentifyBin(char *filename) {
+ int pFile;
+ int rIdentifyBin;
+ int bytesreaded;
+ int i;
+
+ char abc1[] = "abcdefghijklmnopqrstuvwxyz1234567890";
+ char abc2[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+ char abc3[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+ char abc4[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ char abc5[] = "1234567890abcdefghijklmnopqrstuvwxyz";
+ char abc6[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+ char abc7[] = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char abc8[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char temp[] = "#...'...*...-.../...2...4...7...9...;...=...?...A...C...E...G...I...J...L...N...O...Q...R...T...U...W...X...Z...";
+ char temp2[] = "0123456789abcdef....(null)..0123456789ABCDEF";
+ char punch[] = "PORTDEV INFOENBLSTATRADRTOUTDRQCFUNCEND";
+ char tetris[] = "abcdefghijklEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()";
+ char netbsd[] = "$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
+ char bortmnt[] = "0123456789ABCDEF....Inf.NaN.0123456789abcdef....(null)...";
+ char dreamsnes[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789-";
+
+ rIdentifyBin = Scrambled;
+
+ pFile = open(filename, _O_BINARY | _O_RDONLY);
+ if (pFile == -1) {
+ return -1;
+ }
+
+ /* Get the number of bytes in the file */
+ bytesreaded = _filelength(pFile);
+
+ /* Allocate memory to store the file plus the NULL-termination char */
+ memoryspace = (char *)malloc(bytesreaded + 1);
+
+ /* Ok, now we read the file */
+ bytesreaded = read(pFile, memoryspace, bytesreaded);
+
+ /* This is the little cheat ;) */
+ for (i = 0; i < bytesreaded; i++) {
+ if (memoryspace[i] == 0x00) {
+ memoryspace[i] = '.';
+ }
+ }
+
+ /* We can't forget the NULL-termination char or program will crash! */
+ memoryspace[bytesreaded] = '\0';
+ close(pFile);
+
+ if (strstr(memoryspace, abc1) ||
+ strstr(memoryspace, abc2) ||
+ strstr(memoryspace, abc3) ||
+ strstr(memoryspace, abc4) ||
+ strstr(memoryspace, abc5) ||
+ strstr(memoryspace, abc6) ||
+ strstr(memoryspace, abc7) ||
+ strstr(memoryspace, abc8) ||
+ strstr(memoryspace, temp) ||
+ strstr(memoryspace, temp2) ||
+ strstr(memoryspace, bortmnt) ||
+ strstr(memoryspace, dreamsnes) ||
+ strstr(memoryspace, tetris) ||
+ strstr(memoryspace, punch) ||
+ strstr(memoryspace, netbsd)) {
+ rIdentifyBin = Unscrambled;
+ }
+
+ /* We dont need the memory anymore */
+ free(memoryspace);
+
+ return rIdentifyBin;
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char *argv[]) {
+ int i;
+ int retval = 0;
+
+ /* The program was opened without a filename argument */
+ if(argc < 2) {
+ fprintf(stderr, "1st_read.bin File Checker\nhttp://www.consolevision.com/members/fackue/\n\n");
+ fprintf(stderr, "Usage: %s filename\n", argv[0]);
+ return 1;
+ }
+
+ /* The program was opened with a filename argument */
+ for(i = 1; i < argc; i++) {
+ switch(IdentifyBin(argv[i])) {
+ case 0:
+ printf("%s is unscrambled\n", argv[i]);
+ break;
+ case 1:
+ printf("%s is scrambled\n", argv[i]);
+ break;
+ default:
+ fprintf(stderr, "%s not found\n", argv[i]);
+ retval = 1;
+ break;
+ }
+ }
+
+ return retval;
+}
diff --git a/selfboot/IP.TMPL b/selfboot/IP.TMPL
Binary files differ.
diff --git a/selfboot/Makefile b/selfboot/Makefile
@@ -0,0 +1,8 @@
+# dreamcast selfboot tools
+
+BINS = makeip scramble 1strdchk
+
+all: $(BINS)
+
+clean:
+ rm -f $(BINS)
diff --git a/selfboot/README b/selfboot/README
@@ -0,0 +1,54 @@
+Self-boot disc burning guide for the Dreamcast
+==============================================
+
+Original information and software from:
+
+ http://mc.pp.se/dc/sw.html
+ http://mc.pp.se/dc/cdr.html
+ http://dchelp.dcemulation.org/downloads/
+ http://chui.dcemu.co.uk/snes4all.html
+
+First get the compiled binary of the game/program. Here we will use the
+latest SNES4ALL release. Test if the file is scrambled with the
+`1strdchk' program.
+
+ $ ./1strdchk snes4all.bin
+ snes4all.bin is unscrambled
+
+If it is not you need to `scramble' it. It can have any given name but
+keep it simple.
+
+ $ ./scramble snes4all.bin 1ST_READ.BIN
+
+Generate the IP.BIN boot sector using the `makeip' program and its text
+configuration file. The ip.txt contains the boot filename 1ST_READ.BIN.
+Change it if you are using another filename.
+
+ $ ./makeip ip.txt IP.BIN
+
+Create a dummy audio track and burn it to an empty disc.
+
+ $ dd if=/dev/zero bs=2352 count=300 of=audio.raw
+ $ cdrecord -multi -audio speed=1 audio.raw
+
+Query the information for the session you just burned on disc.
+
+ $ cdrecord -msinfo
+ 0,11702
+
+Use the above information to create the image for the second session.
+Create this file as follows:
+
+ $ mkisofs -C 0,11702 -V SNES4ALL -l -o tmp.iso 1ST_READ.BIN roms/
+ $ (cat IP.BIN; dd if=tmp.iso bs=2048 skip=16) > data.raw
+or
+ $ mkisofs -C 0,11702 -V SNES4ALL -l -G IP.BIN -o data.raw \
+ 1ST_READ.BIN roms/
+
+Read your `cdrecord' manpage to see which of the -xa, -xa{1,2} option is
+the correct CD-ROM XA mode 2 form 1 with data size multiple of 2056
+bytes. For example, `wodim' has this as -xa but `cdrtools' as -xa1.
+
+ $ cdrecord -multi -xa1 speed=1 data.raw
+
+Put the disc in your Dreamcast and enjoy!
diff --git a/selfboot/ip.txt b/selfboot/ip.txt
@@ -0,0 +1,11 @@
+Hardware ID : SEGA SEGAKATANA
+Maker ID : SEGA ENTERPRISES
+Device Info : 0000 CD-ROM1/1
+Area Symbols : JUE
+Peripherals : E000F10
+Product No : T0000
+Version : V1.000
+Release Date : 20000627
+Boot Filename : 1ST_READ.BIN
+SW Maker Name : YOUR NAME HERE
+Game Title : TITLE OF THE SOFTWARE
diff --git a/selfboot/makeip.c b/selfboot/makeip.c
@@ -0,0 +1,191 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#define NUM_FIELDS 11
+
+struct field;
+
+int check_areasym(char *, struct field *);
+
+struct field {
+ char *name;
+ int pos;
+ int len;
+ int (*extra_check)(char *, struct field *);
+} fields[NUM_FIELDS] = {
+ { "Hardware ID", 0x0, 0x10, NULL },
+ { "Maker ID", 0x10, 0x10, NULL },
+ { "Device Info", 0x20, 0x10, NULL },
+ { "Area Symbols", 0x30, 0x8, check_areasym },
+ { "Peripherals", 0x38, 0x8, NULL },
+ { "Product No", 0x40, 0xa, NULL },
+ { "Version", 0x4a, 0x6, NULL },
+ { "Release Date", 0x50, 0x10, NULL },
+ { "Boot Filename", 0x60, 0x10, NULL },
+ { "SW Maker Name", 0x70, 0x10, NULL },
+ { "Game Title", 0x80, 0x80, NULL },
+};
+
+int filled_in[NUM_FIELDS];
+
+int check_areasym(char *ptr, struct field *f)
+{
+ int i, a = 0;
+ for(i=0; i<f->len; i++)
+ switch(ptr[i]) {
+ case 'J':
+ a |= (1<<0);
+ break;
+ case 'U':
+ a |= (1<<1);
+ break;
+ case 'E':
+ a |= (1<<2);
+ break;
+ case ' ':
+ break;
+ default:
+ fprintf(stderr, "Unknown area symbol '%c'.\n", ptr[i]);
+ return 0;
+ }
+ for(i=0; i<f->len; i++)
+ if((a & (1<<i)) == 0)
+ ptr[i] = ' ';
+ else
+ ptr[i] = "JUE"[i];
+ return 1;
+}
+
+void trim(char *str)
+{
+ int l = strlen(str);
+ while(l>0 && (str[l-1] == '\r' || str[l-1] == '\n' ||
+ str[l-1] == ' ' || str[l-1] == '\t'))
+ str[--l]='\0';
+}
+
+int parse_input(FILE *fh, char *ip)
+{
+ static char buf[80];
+ int i;
+ memset(filled_in, 0, sizeof(filled_in));
+ while(fgets(buf, sizeof(buf), fh)) {
+ char *p;
+ trim(buf);
+ if(*buf)
+ if((p = strchr(buf, ':'))) {
+ *p++ = '\0';
+ trim(buf);
+ for(i=0; i<NUM_FIELDS; i++)
+ if(!strcmp(buf, fields[i].name))
+ break;
+ if(i>=NUM_FIELDS) {
+ fprintf(stderr, "Unknown field \"%s\".\n", buf);
+ return 0;
+ }
+ memset(ip+fields[i].pos, ' ', fields[i].len);
+ while(*p == ' ' || *p == '\t')
+ p++;
+ if(strlen(p)>fields[i].len) {
+ fprintf(stderr, "Data for field \"%s\" is too long.\n", fields[i]);
+ return 0;
+ }
+ memcpy(ip+fields[i].pos, p, strlen(p));
+ if(fields[i].extra_check!=NULL &&
+ !(*fields[i].extra_check)(ip+fields[i].pos, &fields[i]))
+ return 0;
+ filled_in[i] = 1;
+ } else {
+ fprintf(stderr, "Missing : on line.\n");
+ return 0;
+ }
+ }
+
+ for(i=0; i<NUM_FIELDS; i++)
+ if(!filled_in[i]) {
+ fprintf(stderr, "Missing value for \"%s\".\n", fields[i]);
+ return 0;
+ }
+
+ return 1;
+}
+
+int calcCRC(const unsigned char *buf, int size)
+{
+ int i, c, n = 0xffff;
+ for (i = 0; i < size; i++)
+ {
+ n ^= (buf[i]<<8);
+ for (c = 0; c < 8; c++)
+ if (n & 0x8000)
+ n = (n << 1) ^ 4129;
+ else
+ n = (n << 1);
+ }
+ return n & 0xffff;
+}
+
+void update_crc(char *ip)
+{
+ int n = calcCRC((unsigned char *)(ip+0x40), 16);
+ char buf[5];
+ sprintf(buf, "%04X", n);
+ if(memcmp(buf, ip+0x20, 4)) {
+ printf("Setting CRC to %s (was %.4s)\n", buf, ip+0x20);
+ memcpy(ip+0x20, buf, 4);
+ }
+}
+
+void makeip(char *ip_tmpl, char *in, char *out)
+{
+ static char ip[0x8000];
+ FILE *fh = fopen(ip_tmpl, "rb");
+ if(fh == NULL) {
+ fprintf(stderr, "Can't open \"%s\".\n", ip_tmpl);
+ exit(1);
+ }
+ if(fread(ip, 1, 0x8000, fh) != 0x8000) {
+ fprintf(stderr, "Read error.\n");
+ exit(1);
+ }
+ fclose(fh);
+ fh = fopen(in, "r");
+ if(fh == NULL) {
+ fprintf(stderr, "Can't open \"%s\".\n", in);
+ exit(1);
+ }
+ if(!parse_input(fh, ip))
+ exit(1);
+ fclose(fh);
+ update_crc(ip);
+ fh = fopen(out, "wb");
+ if(fh == NULL) {
+ fprintf(stderr, "Can't open \"%s\".\n", out);
+ exit(1);
+ }
+ if(fwrite(ip, 1, 0x8000, fh) != 0x8000) {
+ fprintf(stderr, "Write error.\n");
+ exit(1);
+ }
+ fclose(fh);
+}
+
+int main(int argc, char *argv[])
+{
+ char *ip_tmpl;
+
+ if(argc != 3) {
+ fprintf(stderr, "Usage: %s ip.txt IP.BIN\n", argv[0]);
+ exit(1);
+ }
+
+ ip_tmpl = getenv("IP_TEMPLATE_FILE");
+ if(ip_tmpl == NULL)
+ ip_tmpl = "IP.TMPL";
+
+ makeip(ip_tmpl, argv[1], argv[2]);
+
+ return 0;
+}
diff --git a/selfboot/scramble.c b/selfboot/scramble.c
@@ -0,0 +1,259 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#define MAXCHUNK (2048*1024)
+
+static unsigned int seed;
+
+void my_srand(unsigned int n)
+{
+ seed = n & 0xffff;
+}
+
+unsigned int my_rand()
+{
+ seed = (seed * 2109 + 9273) & 0x7fff;
+ return (seed + 0xc000) & 0xffff;
+}
+
+void load(FILE *fh, unsigned char *ptr, unsigned long sz)
+{
+ if(fread(ptr, 1, sz, fh) != sz)
+ {
+ fprintf(stderr, "Read error!\n");
+ exit(1);
+ }
+}
+
+void load_chunk(FILE *fh, unsigned char *ptr, unsigned long sz)
+{
+ static int idx[MAXCHUNK/32];
+ int i;
+
+ /* Convert chunk size to number of slices */
+ sz /= 32;
+
+ /* Initialize index table with unity,
+ so that each slice gets loaded exactly once */
+ for(i = 0; i < sz; i++)
+ idx[i] = i;
+
+ for(i = sz-1; i >= 0; --i)
+ {
+ /* Select a replacement index */
+ int x = (my_rand() * i) >> 16;
+
+ /* Swap */
+ int tmp = idx[i];
+ idx[i] = idx[x];
+ idx[x] = tmp;
+
+ /* Load resulting slice */
+ load(fh, ptr+32*idx[i], 32);
+ }
+}
+
+void load_file(FILE *fh, unsigned char *ptr, unsigned long filesz)
+{
+ unsigned long chunksz;
+
+ my_srand(filesz);
+
+ /* Descramble 2 meg blocks for as long as possible, then
+ gradually reduce the window down to 32 bytes (1 slice) */
+ for(chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
+ while(filesz >= chunksz)
+ {
+ load_chunk(fh, ptr, chunksz);
+ filesz -= chunksz;
+ ptr += chunksz;
+ }
+
+ /* Load final incomplete slice */
+ if(filesz)
+ load(fh, ptr, filesz);
+}
+
+void read_file(char *filename, unsigned char **ptr, unsigned long *sz)
+{
+ FILE *fh = fopen(filename, "rb");
+ if(fh == NULL)
+ {
+ fprintf(stderr, "Can't open \"%s\".\n", filename);
+ exit(1);
+ }
+ if(fseek(fh, 0, SEEK_END)<0)
+ {
+ fprintf(stderr, "Seek error.\n");
+ exit(1);
+ }
+ *sz = ftell(fh);
+ *ptr = malloc(*sz);
+ if( *ptr == NULL )
+ {
+ fprintf(stderr, "Out of memory.\n");
+ exit(1);
+ }
+ if(fseek(fh, 0, SEEK_SET)<0)
+ {
+ fprintf(stderr, "Seek error.\n");
+ exit(1);
+ }
+ load_file(fh, *ptr, *sz);
+ fclose(fh);
+}
+
+void save(FILE *fh, unsigned char *ptr, unsigned long sz)
+{
+ if(fwrite(ptr, 1, sz, fh) != sz)
+ {
+ fprintf(stderr, "Write error!\n");
+ exit(1);
+ }
+}
+
+void save_chunk(FILE *fh, unsigned char *ptr, unsigned long sz)
+{
+ static int idx[MAXCHUNK/32];
+ int i;
+
+ /* Convert chunk size to number of slices */
+ sz /= 32;
+
+ /* Initialize index table with unity,
+ so that each slice gets saved exactly once */
+ for(i = 0; i < sz; i++)
+ idx[i] = i;
+
+ for(i = sz-1; i >= 0; --i)
+ {
+ /* Select a replacement index */
+ int x = (my_rand() * i) >> 16;
+
+ /* Swap */
+ int tmp = idx[i];
+ idx[i] = idx[x];
+ idx[x] = tmp;
+
+ /* Save resulting slice */
+ save(fh, ptr+32*idx[i], 32);
+ }
+}
+
+void save_file(FILE *fh, unsigned char *ptr, unsigned long filesz)
+{
+ unsigned long chunksz;
+
+ my_srand(filesz);
+
+ /* Descramble 2 meg blocks for as long as possible, then
+ gradually reduce the window down to 32 bytes (1 slice) */
+ for(chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
+ while(filesz >= chunksz)
+ {
+ save_chunk(fh, ptr, chunksz);
+ filesz -= chunksz;
+ ptr += chunksz;
+ }
+
+ /* Save final incomplete slice */
+ if(filesz)
+ save(fh, ptr, filesz);
+}
+
+void write_file(char *filename, unsigned char *ptr, unsigned long sz)
+{
+ FILE *fh = fopen(filename, "wb");
+ if(fh == NULL)
+ {
+ fprintf(stderr, "Can't open \"%s\".\n", filename);
+ exit(1);
+ }
+ save_file(fh, ptr, sz);
+ fclose(fh);
+}
+
+void descramble(char *src, char *dst)
+{
+ unsigned char *ptr = NULL;
+ unsigned long sz = 0;
+ FILE *fh;
+
+ read_file(src, &ptr, &sz);
+
+ fh = fopen(dst, "wb");
+ if(fh == NULL)
+ {
+ fprintf(stderr, "Can't open \"%s\".\n", dst);
+ exit(1);
+ }
+ if( fwrite(ptr, 1, sz, fh) != sz )
+ {
+ fprintf(stderr, "Write error.\n");
+ exit(1);
+ }
+ fclose(fh);
+ free(ptr);
+}
+
+void scramble(char *src, char *dst)
+{
+ unsigned char *ptr = NULL;
+ unsigned long sz = 0;
+ FILE *fh;
+
+ fh = fopen(src, "rb");
+ if(fh == NULL)
+ {
+ fprintf(stderr, "Can't open \"%s\".\n", src);
+ exit(1);
+ }
+ if(fseek(fh, 0, SEEK_END)<0)
+ {
+ fprintf(stderr, "Seek error.\n");
+ exit(1);
+ }
+ sz = ftell(fh);
+ ptr = malloc(sz);
+ if( ptr == NULL )
+ {
+ fprintf(stderr, "Out of memory.\n");
+ exit(1);
+ }
+ if(fseek(fh, 0, SEEK_SET)<0)
+ {
+ fprintf(stderr, "Seek error.\n");
+ exit(1);
+ }
+ if( fread(ptr, 1, sz, fh) != sz )
+ {
+ fprintf(stderr, "Read error.\n");
+ exit(1);
+ }
+ fclose(fh);
+
+ write_file(dst, ptr, sz);
+
+ free(ptr);
+}
+
+int main(int argc, char *argv[])
+{
+ int opt = 0;
+
+ if(argc > 1 && !strcmp(argv[1], "-d"))
+ opt ++;
+
+ if(argc != 3+opt)
+ {
+ fprintf(stderr, "Usage: %s [-d] from to\n", argv[0]);
+ exit(1);
+ }
+
+ if(opt)
+ descramble(argv[2], argv[3]);
+ else
+ scramble(argv[1], argv[2]);
+
+ return 0;
+}