Browse code

Make uxn256emu able to load the init.rom inside an erom (uncompressed zip containing rom files and assets)

Dario Rodriguez authored on 13/01/2024 18:14:13
Showing 4 changed files
... ...
@@ -107,5 +107,6 @@ if [ $norun = 1 ]; then exit; fi
107 107
 ./bin/uxn256emu -v
108 108
 
109 109
 ./bin/uxnasm projects/examples/devices/mouse.tal bin/mouse.rom
110
+./rom2erom.sh bin/mouse.rom bin/mouse.erom
110 111
 
111
-./bin/uxn256emu -2x bin/mouse.rom
112
+./bin/uxn256emu -2x bin/mouse.erom
112 113
new file mode 100755
... ...
@@ -0,0 +1,12 @@
1
+#!/bin/bash
2
+if [ "m$1" = "m" ] || [ "m$2" = "m" ] || [ ! -e "$1" ] || [ "m$1" == "m--help" ] ; then
3
+        echo "Syntax: $0 romfile.rom romfile.erom"
4
+        exit 1
5
+fi
6
+dirname=$(mktemp -d eromdata.tmpXXXX)
7
+cp "$1" $dirname/init.rom
8
+(cd $dirname && zip -0 result.erom init.rom )
9
+mv $dirname/result.erom "$2"
10
+rm -rf "$dirname"
11
+ls -l "$2"
12
+
... ...
@@ -18,7 +18,8 @@ WITH REGARD TO THIS SOFTWARE.
18 18
 
19 19
 extern char *boot_rom;
20 20
 
21
-void system_reboot(Uxn *u, char *rom, int soft, char *romdata, int sizeromdata);
21
+void system_reboot(Uxn *u, char *rom, int soft, char *romdata, int sizeromdata
22
+);
22 23
 void system_inspect(Uxn *u);
23 24
 int system_version(char *emulator, char *date);
24 25
 int system_error(char *msg, const char *err);
... ...
@@ -1,6 +1,7 @@
1 1
 #include <stdio.h>
2 2
 #include <time.h>
3 3
 #include <unistd.h>
4
+#include <sys/stat.h>
4 5
 
5 6
 #include "uxn.h"
6 7
 
... ...
@@ -61,6 +62,7 @@ typedef struct video_t {
61 62
 } video_t;
62 63
 
63 64
 typedef struct instance_t {
65
+        void *varvara;
64 66
         video_t *video;
65 67
         Uint8 dev[0x100];
66 68
         Uxn u;
... ...
@@ -76,18 +78,37 @@ typedef struct instance_t {
76 78
         Uint64 ms_interval;
77 79
 } instance_t;
78 80
 
81
+typedef struct romdir_t {
82
+        int sizename;
83
+        char *name;
84
+        long offset;
85
+        long csize;
86
+        int method;
87
+        char *data;
88
+} romdir_t;
89
+
79 90
 typedef struct varvara_t {
80 91
         video_t video;
81 92
         int sizeinstances;
93
+        long sizeromdata;
94
+        char *romdata;
95
+        int sizeromdir;
96
+        romdir_t *romdir;
82 97
         instance_t **instances;
83 98
 } varvara_t;
84 99
 
85 100
 varvara_t *varvara_init(char *romfilename, int flag_fullscreen, int zoom);
101
+int varvara_romdata2romdir(varvara_t *varvara);
86 102
 void varvara_free(varvara_t *varvara);
87 103
 
88
-instance_t *instance_init(video_t *video);
104
+char *varvara_romdata(varvara_t *varvara,char *romdirname);
105
+int varvara_sizeromdata(varvara_t *varvara,char *romdirname);
106
+
107
+instance_t *instance_init(varvara_t *varvara);
89 108
 void instance_free(instance_t *instance);
90 109
 
110
+static char *load_file(char *filename, long *loadedsize);
111
+static int is_extension(char *filename, char *extension);
91 112
 
92 113
 /* prototypes: devices */
93 114
 
... ...
@@ -138,11 +159,18 @@ varvara_init(char *romfilename, int flag_fullscreen, int zoom)
138 159
                 system_error("Init", "Failed to allocate varvara structs.");
139 160
                 return(NULL);
140 161
         }
141
-
162
+        if(is_extension(romfilename,".erom")
163
+           && ((varvara->romdata=load_file(romfilename,&(varvara->sizeromdata)))==NULL
164
+             || varvara_romdata2romdir(varvara)!=0)
165
+        ) {
166
+                varvara_free(varvara),varvara=NULL;
167
+                system_error("Init", "Failed to load erom.");
168
+                return(NULL);
169
+        }
142 170
         set_zoom(&(varvara->video),zoom, 0);
143 171
         set_flag_fullscreen(&(varvara->video), flag_fullscreen, 0);
144 172
         /* Start system. */
145
-        if((varvara->instances[0]=instance_init(&(varvara->video)))==NULL) {
173
+        if((varvara->instances[0]=instance_init(varvara))==NULL) {
146 174
                 varvara_free(varvara),varvara=NULL;
147 175
                 system_error("Init", "Failed to initialize varvara unx instance.");
148 176
                 return NULL;
... ...
@@ -154,8 +182,9 @@ varvara_init(char *romfilename, int flag_fullscreen, int zoom)
154 182
         if((varvara->instances[0]->ram=(Uint8 *)calloc(0x10000 * RAM_PAGES, sizeof(Uint8)))==NULL
155 183
           || (varvara->instances[0]->rom=(char *)malloc(strlen(romfilename)+1))==NULL
156 184
           || memcpy(varvara->instances[0]->rom,romfilename,strlen(romfilename)+1)==NULL
157
-          || !system_init(&(varvara->instances[0]->u), varvara->instances[0]->ram, varvara->instances[0]->rom, NULL, 0)
158
-          || !system_init(&(varvara->instances[0]->u_audio), varvara->instances[0]->ram, varvara->instances[0]->rom, NULL, 0)) {
185
+          || !system_init(&(varvara->instances[0]->u), varvara->instances[0]->ram, varvara->instances[0]->rom, varvara_romdata(varvara,"init.rom"),varvara_sizeromdata(varvara,"init.rom"))
186
+          || !system_init(&(varvara->instances[0]->u_audio), varvara->instances[0]->ram, varvara->instances[0]->rom, varvara_romdata(varvara,"init.rom"),varvara_sizeromdata(varvara,"init.rom"))
187
+        ) {
159 188
                 varvara_free(varvara),varvara=NULL;
160 189
                 system_error("Init", "Failed to initialize uxn.");
161 190
                 return(NULL);
... ...
@@ -168,6 +197,89 @@ varvara_init(char *romfilename, int flag_fullscreen, int zoom)
168 197
         return(varvara);
169 198
 }
170 199
 
200
+int
201
+varvara_romdata2romdir(varvara_t *varvara)
202
+{
203
+        int i;
204
+        unsigned char *start,*central,*end;
205
+        int nrecords,sizeregs,offset;
206
+        unsigned char *regsend,*ptr;
207
+        char *strerr;
208
+        romdir_t *romdir;
209
+        unsigned char *header;
210
+        int namelen,extralen;
211
+        if(varvara==NULL || varvara->romdata==NULL)
212
+                return(-1);
213
+        start=((unsigned char *)varvara->romdata);
214
+        end=start+varvara->sizeromdata;
215
+        /* look for end-of-zip directory */
216
+        for(i=varvara->sizeromdata-4;i>=0;i--) {
217
+                if(start[i]==0x50 && start[i+1]==0x4b && start[i+2]==0x05 && start[i+3]==0x06)
218
+                        break;
219
+        }
220
+        /* extract header */
221
+        if((strerr="without end-of-zip directory")==NULL
222
+          || i<0
223
+          || (strerr="with corrupt end-of-zip directory")==NULL
224
+          || (central=start+i+4)>=end
225
+          || (central+15)>=end
226
+          || (nrecords=central[6]|(central[7]<<8))<=0
227
+          || (sizeregs=central[8]|(central[9]<<8)|(central[10]<<16)|(central[11]<<24))<4
228
+          || (offset=central[12]|(central[13]<<8)|(central[14]<<16)|(central[15]<<24))<=0
229
+          || (regsend=start+offset+sizeregs)>end
230
+          || memcmp(start+offset,"\x50\x4b\x01\x02",4)!=0
231
+          || (strerr="couldn't be processed because of insufficient memory")==NULL
232
+          || (varvara->romdir=calloc(nrecords,sizeof(romdir_t)))==NULL
233
+        ) {
234
+                fprintf(stderr,"WARNING: erom %s\n",strerr);
235
+                return(-1); /* corrupt file or insuf. mem. */
236
+        }
237
+        /* parse entries */
238
+        for(i=0,ptr=start+offset+4;i<nrecords && ptr<(start+offset+sizeregs);i++) {
239
+                romdir=varvara->romdir+i;
240
+                if((strerr="is corrupt (too small)")==NULL
241
+                  || (ptr+25)>=regsend
242
+                  || (strerr="is corrupt (sizename is negative)")==NULL
243
+                  || (romdir->sizename=(ptr[24]|(ptr[25]<<8)))<0
244
+                  || (strerr="is corrupt (name too big)")==NULL
245
+                  || (ptr+42+romdir->sizename)>regsend
246
+                  || (strerr="is corrupt (name)")==NULL /* never used */
247
+                  || (romdir->name=((char *)ptr)+42)==NULL
248
+                  || (strerr="is corrupt (offset is negative)")==NULL
249
+                  || (romdir->offset=ptr[38]|(ptr[39]<<8)|(ptr[40]<<16)|(ptr[41]<<24))<0
250
+                  || (strerr="is corrupt (csize is negative)")==NULL
251
+                  || (romdir->csize=ptr[16]|(ptr[17]<<8)|(ptr[18]<<16)|(ptr[19]<<24))<0
252
+                  || (strerr="indicate compressed files but we require zips without compression (i.e. \"zip -0 myfile.erom init.rom\")")==NULL
253
+                  || (romdir->method=ptr[6]|(ptr[7]<<8))!=0
254
+                  || (strerr="is corrupt (data header)")==NULL
255
+                  || (header=start+romdir->offset)<0
256
+                  || (header+30)>central
257
+                  || (namelen=header[26]|(header[27]<<8))<0
258
+                  || (extralen=header[28]|(header[29]<<8))<0
259
+                  || (romdir->data=((char *)start)+romdir->offset+30+namelen+extralen)>((char *)central)
260
+                  || (romdir->data+romdir->csize)>((char *)central)
261
+                ) {
262
+                        fprintf(stderr,"WARNING: erom directory entry %s\n",strerr);
263
+                        free(varvara->romdir),varvara->romdir=NULL;
264
+                        return(-1); /* corrupt file or insuf. mem. */
265
+                }
266
+                /* search next entry */
267
+                for(;ptr<(start+offset+sizeregs-4);ptr++) {
268
+                        if(ptr[0]==0x50 && ptr[1]==0x4b && ptr[2]==0x01 && ptr[3]==0x02) {
269
+                                ptr+=4;
270
+                                break;
271
+                        }
272
+                }
273
+        }
274
+        if(i<nrecords) {
275
+                fprintf(stderr,"WARNING: erom with corrupt end-of-zip directory\n");
276
+                free(varvara->romdir),varvara->romdir=NULL;
277
+                return(-1); /* incomplete file */
278
+        }
279
+        varvara->sizeromdir=i;
280
+        return(0);
281
+}
282
+
171 283
 void
172 284
 varvara_free(varvara_t *varvara)
173 285
 {
... ...
@@ -182,17 +294,49 @@ varvara_free(varvara_t *varvara)
182 294
                 }
183 295
                 free(varvara->instances),varvara->instances=NULL,varvara->sizeinstances=0;
184 296
         }
297
+        if(varvara->romdata!=NULL)
298
+                free(varvara->romdata),varvara->romdata=NULL,varvara->sizeromdata=0;
185 299
         free(varvara),varvara=NULL;
186 300
 }
187 301
 
302
+char *
303
+varvara_romdata(varvara_t *varvara,char *romdirname)
304
+{
305
+        int i,l;
306
+        if(varvara==NULL || romdirname==NULL || varvara->romdata==NULL)
307
+                return(NULL);
308
+        l=strlen(romdirname);
309
+        for(i=0;i<varvara->sizeromdir;i++) {
310
+                if(varvara->romdir[i].sizename==l && memcmp(varvara->romdir[i].name,romdirname,l)==0)
311
+                        return(varvara->romdir[i].data);
312
+        }
313
+        return(NULL);
314
+}
315
+
316
+int
317
+varvara_sizeromdata(varvara_t *varvara,char *romdirname)
318
+{
319
+        int i,l;
320
+        if(varvara==NULL || romdirname==NULL || varvara->romdata==NULL)
321
+                return(0);
322
+        l=strlen(romdirname);
323
+        for(i=0;i<varvara->sizeromdir;i++) {
324
+                if(varvara->romdir[i].sizename==l && memcmp(varvara->romdir[i].name,romdirname,l)==0)
325
+                        return(varvara->romdir[i].csize);
326
+        }
327
+        return(0);
328
+}
329
+
330
+
188 331
 instance_t *
189
-instance_init(video_t *video)
332
+instance_init(varvara_t *varvara)
190 333
 {
191 334
         instance_t *instance;
192 335
         if((instance=malloc(sizeof(instance_t)))==NULL)
193 336
                 return(NULL);
194 337
         memset(instance,0,sizeof(instance_t));
195
-        instance->video=video;
338
+        instance->varvara=varvara;
339
+        instance->video=&(varvara->video);
196 340
         return(instance);
197 341
 }
198 342
 
... ...
@@ -209,6 +353,46 @@ instance_free(instance_t *instance)
209 353
         return;
210 354
 }
211 355
 
356
+static char *
357
+load_file(char *filename, long *loadedsize)
358
+{
359
+        FILE *fp;
360
+        struct stat st;
361
+        char *buf;
362
+        long filesize;
363
+        /* NOTE: we add a trailing zero byte to be able to return something if the file size is zero */
364
+        if((fp=fopen(filename,"rb"))==NULL
365
+          || fstat(fileno(fp),&st)!=0
366
+          || (filesize=st.st_size)!=st.st_size
367
+          || (buf=malloc(filesize+1))==NULL
368
+          || fread(buf,1,filesize,fp)!=filesize
369
+          || (buf[filesize]=0)!=0
370
+          || fclose(fp)!=0
371
+          || (fp=NULL)!=NULL
372
+        ) {
373
+                if(fp!=NULL)
374
+                        fclose(fp),fp=NULL;
375
+                return(NULL);
376
+        }
377
+        if(loadedsize!=NULL)
378
+                *loadedsize=filesize;
379
+        return(buf);
380
+}
381
+
382
+static int
383
+is_extension(char *filename, char *extension)
384
+{
385
+        /* NOTE: extension MUST include the leading dot, i.e. ".txt" */
386
+        int lfilename,lextension;
387
+        if(filename==NULL || extension==NULL)
388
+                return((filename==extension || extension==NULL)?1:0);
389
+        lfilename=strlen(filename);
390
+        lextension=strlen(extension);
391
+        if(lfilename>=lextension)
392
+                return((strcmp(filename+lfilename-lextension,extension)==0)?1:0);
393
+        return(0);
394
+}
395
+
212 396
 /* devices */
213 397
 
214 398
 static int
... ...
@@ -442,7 +626,7 @@ emu_restart(Uxn *u, char *rom, int soft)
442 626
         screen_resize(u,WIDTH, HEIGHT);
443 627
         screen_fill(uxn_screen.bg, 0);
444 628
         screen_fill(uxn_screen.fg, 0);
445
-        system_reboot(u, rom, soft, NULL, 0);
629
+        system_reboot(u, rom, soft, varvara_romdata((varvara_t *)(instance->varvara),"init.rom"), varvara_sizeromdata((varvara_t *)(instance->varvara),"init.rom"));
446 630
         SDL_SetWindowTitle(instance->video->emu_window, boot_rom);
447 631
 }
448 632