Advertisement
Guest User

Compresses thomson binary files using EXOMIZER2

a guest
Mar 4th, 2014
501
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * exobin
  3.  *    Compress a binary thomson file. To be compiled & linked along with exomizer2 code.
  4.  *
  5.  * (c) Samuel Devulder 2013-2014.
  6.  */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11.  
  12. #include <sys/stat.h>
  13. #include <sys/types.h>
  14. #include <dirent.h>
  15.  
  16. #include "membuf_io.h"
  17. #include "exo_helper.h"
  18.  
  19.  
  20. #ifndef TRUE
  21. #define TRUE    1
  22. #define FALSE   0
  23. #endif
  24.  
  25. typedef struct {
  26.     char mem[65536];
  27.     int min,max,exe;
  28. } BIN;
  29.  
  30. #define HEXADDR_NONE -1
  31. #define HEXADDR_AUTO -2
  32.  
  33. static void convert(char *infile, char *outfile, int hexaddr);
  34. static void *alloc(int size);
  35. static BIN  *read_bin(char *infile);
  36. static void process(char *filename, int hexaddr);
  37. static int get8(FILE *f);
  38. static int get16(FILE *f);
  39. static int endsWithIgnoreCase(char *s, char *end);
  40.  
  41. int total = 0, total2 = 0, total3 = 0, num = 0;
  42.  
  43. int main(int ac, char **av) {
  44.     int i;
  45.     int hexaddr = HEXADDR_NONE;
  46.    
  47.     LOG_INIT_CONSOLE(LOG_WARNING);
  48.        
  49.     for(i=1; i<ac; ++i) {
  50.         struct stat buf;
  51.        
  52.         if(!strcmp(av[i], "-h") || !strcmp(av[i], "--help") || !strcmp(av[i], "?")) {
  53.             fprintf(stderr, "Usage: %s [?|-h|--help] [-x[HEXADDR]] <files.bin or folder>\n",av[0]);
  54.             fprintf(stderr, "\n\n");
  55.             fprintf(stderr, "Compresse un binaire thomson. Le fichier resultat est place a cote\n");
  56.             fprintf(stderr, "du fichier source, mais avec l'extension EXO au lieu de BIN.\n");
  57.             fprintf(stderr, "\n");
  58.             fprintf(stderr, "L'option -x produit une binaire auto-extractible. HEXADDR contient\n");
  59.             fprintf(stderr, "l'addresse hexadecimale du chargement. Si HEXADDR est absent, une\n");
  60.             fprintf(stderr, "une adresse est choisie automatiquement (eventuellement en ram video).\n");
  61.             exit(EXIT_SUCCESS);
  62.         } else if(av[i][0]=='-' && av[i][1]=='x') {
  63.                 if(av[i][2]) {
  64.                    char *s = av[i]+2;
  65.                    int t = 0;
  66.                    while((*s>='0' && *s<='9') ||
  67.                      (*s>='a' && *s<='f') ||
  68.                      (*s>='A' && *s<='F')) {
  69.                     t <<= 4;
  70.                     if(*s>='0' && *s<='9') t += *s - '0';
  71.                     if(*s>='a' && *s<='f') t += *s - 'a' + 10;
  72.                     if(*s>='A' && *s<='F') t += *s - 'A' + 10;
  73.                     ++s;
  74.                    }
  75.                    hexaddr = t & 0xFFFF;
  76.             } else {
  77.                    hexaddr = HEXADDR_AUTO;
  78.             }
  79.         } else  if(!stat(av[i], &buf)) {
  80.             if(S_ISDIR(buf.st_mode)) {
  81.                 DIR *dir = opendir(av[i]);
  82.                 if(dir) {
  83.                     struct dirent *dirent;
  84.                     while((dirent = readdir(dir))!=NULL) if(endsWithIgnoreCase(dirent->d_name, ".BIN")) {
  85.                         char *s = alloc(strlen(av[i]) + strlen(dirent->d_name) + 2);
  86.                         strcpy(s, av[i]);
  87.                         strcat(s, "/");
  88.                         strcat(s, dirent->d_name);
  89.                         process(s, hexaddr);
  90.                         free(s);
  91.                     }
  92.                     closedir(dir);
  93.                 }
  94.             } else if(S_ISREG(buf.st_mode)) {
  95.                 process(av[i], hexaddr);
  96.             }
  97.         }
  98.     }
  99.    
  100.    
  101.     LOG_FREE;
  102.    
  103.     return EXIT_SUCCESS;
  104. }
  105.  
  106. /**
  107.  * compress the given file
  108.  */
  109. static void process(char *filename, int hexaddr) {
  110.     char *out = alloc(3+strlen(filename));
  111.     char *s;
  112.    
  113.     /* change extension */
  114.     strcpy(out, filename);
  115.     for(s=out; *s; ++s) {}
  116.     while(s>out && *s!='.') --s;
  117.     if(*s=='.') strcpy(s, ".EXO");
  118.     else        strcat(s, ".EXO");
  119.    
  120.     convert(filename, out, hexaddr);
  121.        
  122.     free(out);
  123. }
  124.    
  125. /**
  126.  * A calloc() that checks out of memory.
  127.  */
  128. static void *alloc(int size) {
  129.     void *p = calloc(size, 1);
  130.     if(p==NULL) {fprintf(stderr, "Out of memory!\n"); exit(EXIT_FAILURE);}
  131.     return p;
  132. }
  133.  
  134. unsigned char binary[] = {
  135. 0x1A,0x50,              /* ORCC #$50 */
  136. 0x10,0xCE,0x60,0xCC,    /* LDS  #$60CC */
  137. #define OFFSET 6
  138. 0x86,0x80,      /* 8000 86   80              lda    #*<-8     */
  139. 0x1F,0x8B,      /* 8002 1F   8B              tfr    a,dp      */
  140. 0xCE,0x83,0xE7,     /* 8004 CE   83E7            ldu    #biba     */
  141. 0x31,0xC4,      /* 8007 31   C4              leay   ,u        */
  142. 0x5F,           /* 8009 5F                   clrb             */
  143. 0xD7,0x89,      /* 800A D7   89              stb    <bitbuf+1 */
  144. 0x4F,           /* 800C 4F            nxt    clra             */
  145. 0x34,0x06,      /* 800D 34   06              pshs   a,b       */
  146. 0xC5,0x0F,      /* 800F C5   0F              bitb   #$0f      */
  147. 0x26,0x03,      /* 8011 26   03              bne    skp       */
  148. 0x8E,0x00,0x01,     /* 8013 8E   0001            ldx    #$0001    */
  149. 0xC6,0x04,      /* 8016 C6   04       skp    ldb    #4        */
  150. 0x8D,0x6A,      /* 8018 8D   6A              bsr    getbits   */
  151. 0xE7,0xC0,      /* 801A E7   C0              stb    ,u+       */
  152. 0x53,           /* 801C 53                   comb             */
  153. 0x69,0xE4,      /* 801D 69   E4       roll   rol    ,s        */
  154. 0x49,           /* 801F 49                   rola             */
  155. 0x5C,           /* 8020 5C                   incb             */
  156. 0x2B,0xFA,      /* 8021 2B   FA              bmi    roll      */
  157. 0xE6,0xE4,      /* 8023 E6   E4              ldb    ,s        */
  158. 0xAF,0xC1,      /* 8025 AF   C1              stx    ,u++      */
  159. 0x30,0x8B,      /* 8027 30   8B              leax   d,x       */
  160. 0x35,0x06,              /* 8029 35   06              puls   a,b       */
  161. 0x5C,           /* 802B 5C                   incb             */
  162. 0xC1,0x34,      /* 802C C1   34              cmpb   #52       */
  163. 0x26,0xDC,      /* 802E 26   DC              bne    nxt       */
  164. 0xCE,0xA9,0x02,     /* 8030 CE   A902     go     ldu    #DEB+LEN  */
  165. 0xC6,0x01,      /* 8033 C6   01       mloop  ldb    #1        */
  166. 0x8D,0x4D,      /* 8035 8D   4D              bsr    getbits   */
  167. 0x26,0x17,      /* 8037 26   17              bne    cpy       */
  168. 0xD7,0x44,      /* 8039 D7   44              stb    <idx+1    */
  169. 0x8C,           /* 803B 8C                   fcb    $8c       */
  170. 0x0C,0x44,      /* 803C 0C   44       rbl    inc    <idx+1    */
  171. 0x5C,           /* 803E 5C                   incb             */
  172. 0x8D,0x43,      /* 803F 8D   43              bsr    getbits   */
  173. 0x27,0xF9,      /* 8041 27   F9              beq    rbl       */
  174. 0xC6,0x00,      /* 8043 C6   00       idx    ldb    #$00      */
  175. 0xC1,0x10,      /* 8045 C1   10              cmpb   #$10      */
  176. 0x10,0x27,0x1F,0xB5,    /* 8047 1027 1FB5            lbeq   EXE       */
  177. 0x25,0x0F,      /* 804B 25   0F              blo    coffs     */
  178. 0x5A,           /* 804D 5A                   decb             */
  179. 0x8D,0x34,      /* 804E 8D   34              bsr    getbits   */
  180. 0x1F,0x01,      /* 8050 1F   01       cpy    tfr    d,x       */
  181. 0xA6,0xA2,      /* 8052 A6   A2       cpyl   lda    ,-y       */
  182. 0xA7,0xC2,      /* 8054 A7   C2              sta    ,-u       */
  183. 0x30,0x1F,      /* 8056 30   1F              leax   -1,x      */
  184. 0x26,0xF8,      /* 8058 26   F8              bne    cpyl      */
  185. 0x20,0xD7,      /* 805A 20   D7              bra    mloop     */
  186. 0x8D,0x3F,      /* 805C 8D   3F       coffs  bsr    cook      */
  187. 0x34,0x06,      /* 805E 34   06              pshs   d         */
  188. 0x8E,0x80,0xA8,         /* 8060 8E   80A8            ldx    #tab1     */
  189. 0x10,0x83,0x00,0x03,    /* 8063 1083 0003            cmpd   #$03      */
  190. 0x24,0x01,      /* 8067 24   01              bhs    scof      */
  191. 0x3A,           /* 8069 3A                   abx              */
  192. 0x8D,0x16,      /* 806A 8D   16       scof   bsr    getbix    */
  193. 0xEB,0x03,      /* 806C EB   03              addb   3,x       */
  194. 0x8D,0x2D,      /* 806E 8D   2D              bsr    cook      */
  195. 0xDD,0x78,      /* 8070 DD   78              std    <offs+2   */
  196. 0x35,0x10,      /* 8072 35   10              puls   x         */
  197. 0x33,0x5F,      /* 8074 33   5F       cpy2   leau   -1,u      */
  198. 0xA6,0xC9,0x55,0X55,    /* 8076 A6   C9 5555  offs   lda    $5555,u   */
  199. 0xA7,0xC4,      /* 807A A7   C4              sta    ,u        */
  200. 0x30,0x1F,      /* 807C 30   1F              leax   -1,x      */
  201. 0x26,0xF4,      /* 807E 26   F4              bne    cpy2      */
  202. 0x20,0xB1,      /* 8080 20   B1              bra    mloop     */
  203. 0xE6,0x84,      /* 8082 E6   84       getbix ldb    ,x        */
  204. 0x6F,0xE2,      /* 8084 6F   E2       getbits clr   ,-s       */
  205. 0x6F,0xE2,      /* 8086 6F   E2              clr    ,-s       */
  206. 0x86,0x55,      /* 8088 86   55       bitbuf lda    #$55      */
  207. 0x20,0x09,      /* 808A 20   09              bra    get3      */
  208. 0xA6,0xA2,      /* 808C A6   A2       get1   lda    ,-y       */
  209. 0x46,           /* 808E 46            get2   rora             */
  210. 0x27,0xFB,      /* 808F 27   FB              beq    get1      */
  211. 0x69,0x61,      /* 8091 69   61              rol    1,s       */
  212. 0x69,0xE4,      /* 8093 69   E4              rol    ,s        */
  213. 0x5A,           /* 8095 5A            get3   decb             */
  214. 0x2A,0xF6,      /* 8096 2A   F6              bpl    get2      */
  215. 0x97,0x89,      /* 8098 97   89              sta    <bitbuf+1 */
  216. 0xEC,0xE1,      /* 809A EC   E1              ldd    ,s++      */
  217. 0x39,           /* 809C 39                   rts              */
  218. 0x8E,0x83,0xE7,     /* 809D 8E   83E7     cook   ldx    #biba     */
  219. 0x3A,           /* 80A0 3A                   abx              */
  220. 0x58,           /* 80A1 58                   aslb             */
  221. 0x3A,           /* 80A2 3A                   abx              */
  222. 0x8D,0xDD,      /* 80A3 8D   DD              bsr    getbix    */
  223. 0xE3,0x01,      /* 80A5 E3   01              addd   1,x       */
  224. 0x39,           /* 80A7 39                   rts              */
  225. 0x04,0x02,0x04,     /* 80A8 04 02 04      tab1   fcb    4,2,4     */
  226. 0x10,0x30,0x20      /* 80AB 10 30 20             fcb    16,48,32  */
  227.             /* 80AE                      incdat FILE.exo  */
  228.             /* 83E7               biba   rmb    156       */
  229. };
  230.  
  231. /**
  232.  * converts BIN file "infile" to EXO file "outfile"
  233.  */
  234. static void convert(char *infile, char *outfile, int hexaddr) {
  235.     BIN *bin = read_bin(infile);
  236.     struct membuf inbuf[1];
  237.     struct membuf outbuf[1];
  238.     struct crunch_info info[1];
  239.     static struct crunch_options options[1] = { CRUNCH_OPTIONS_DEFAULT };
  240.     char *name = infile;
  241.     int len;
  242.     int decomp_size;
  243.    
  244.     if(!bin) return;
  245.    
  246.     while(*name) ++name;
  247.         while(name>infile) {
  248.         if(*name=='/' || *name=='\\') {++name; break;}
  249.         --name;
  250.         }
  251.    
  252.     /*data_start = membuf_memlen(inbuf);*/
  253.    
  254.     membuf_init(outbuf);
  255.     membuf_init(inbuf);
  256.     len = bin->max - bin->min;
  257.     membuf_append(inbuf, bin->mem + bin->min, len);
  258.    
  259.     crunch_backwards(inbuf, outbuf, options, info);
  260.     /*reverse_buffer(membuf_get(outbuf), membuf_memlen(outbuf));*/
  261.    
  262.     if(0) {
  263.         struct membuf tstbuf[1];
  264.         char *src, *tst; int i;
  265.        
  266.         membuf_init(tstbuf);
  267.         decrunch_backwards(LOG_NORMAL, outbuf, tstbuf);
  268.         src = membuf_get(inbuf);
  269.         tst = membuf_get(tstbuf);
  270.         for(i=0; i<membuf_memlen(inbuf); ++i) {
  271.             if(src[i]!=tst[i]) printf("%d: %d!=%d", i, src[i]&255, tst[i]&255);
  272.         }
  273.     }
  274.     decomp_size = 156 + sizeof(binary) + membuf_memlen(outbuf);
  275.     if(hexaddr == HEXADDR_AUTO) {
  276.         hexaddr = bin->min - decomp_size;
  277.         if(!((0x4000<=hexaddr && hexaddr+decomp_size<=0x5F40) ||
  278.                  (0x6100<=hexaddr && hexaddr+decomp_size<=0xE000)))
  279.         hexaddr = 0x5f40 - decomp_size;
  280.         while(hexaddr>=0x4000 &&
  281.              ((hexaddr+0x44+OFFSET)>>8) != ((hexaddr+0x89+OFFSET)>>8)) --hexaddr;
  282.     }
  283.    
  284.     /* validation */
  285.     if(hexaddr != HEXADDR_NONE) {
  286.         fprintf(stderr, "%s: debut decomp: $%04x ", name, hexaddr);
  287.         if(((hexaddr+0x44+OFFSET)>>8) != ((hexaddr+0x89+OFFSET)>>8)) {
  288.             fprintf(stderr, "KO (PAGE-BOUNDARY CROSSING)\n");
  289.             hexaddr = HEXADDR_NONE;
  290.         } else if((bin->min<=hexaddr && hexaddr<bin->max) ||
  291.            (bin->min<=hexaddr+decomp_size-1 && hexaddr+decomp_size-1<bin->max)) {
  292.             fprintf(stderr, "KO (COLLISION)\n");
  293.             hexaddr = HEXADDR_NONE;
  294.         } else if(hexaddr<0x4000 || hexaddr+decomp_size>0xE000) {
  295.             fprintf(stderr, "KO (ROM)\n");
  296.             hexaddr = HEXADDR_NONE;
  297.         } else if((0x4000<=hexaddr && hexaddr+decomp_size<=0x5F40) ||
  298.               (0x6100<=hexaddr && hexaddr+decomp_size<=0xE000)) {
  299.             fprintf(stderr, "OK\n");
  300.         } else {
  301.                 fprintf(stderr, "KO (PAGE0 CROSSING)\n");
  302.             hexaddr = HEXADDR_NONE;
  303.         }
  304.     }
  305.  
  306.     if(hexaddr != HEXADDR_NONE) {
  307.         int len    = sizeof(binary) + membuf_memlen(outbuf);
  308.         int biba   = hexaddr + len;
  309.         int idx    = hexaddr+0x43+OFFSET;
  310.         int offs   = hexaddr+0x76+OFFSET;
  311.         int bitbuf = hexaddr+0x88+OFFSET;
  312.         int tab1   = hexaddr+0xA8+OFFSET;
  313.         char *buf  = membuf_get(outbuf);
  314.        
  315.         int i;
  316.         for(i=0; i<sizeof(binary); ++i) bin->mem[hexaddr + i] = binary[i];
  317.         for(i=membuf_memlen(outbuf); --i>=0;)
  318.             bin->mem[hexaddr + sizeof(binary)+i] = buf[i];
  319.        
  320.         bin->mem[hexaddr + 0x01 + OFFSET] = (idx+1)>>8;
  321.         bin->mem[hexaddr + 0x05 + OFFSET] = (biba)>>8;
  322.         bin->mem[hexaddr + 0x06 + OFFSET] = (biba)&255;
  323.         bin->mem[hexaddr + 0x0B + OFFSET] = (bitbuf+1)&255;
  324.         bin->mem[hexaddr + 0x31 + OFFSET] = bin->max>>8;
  325.         bin->mem[hexaddr + 0x32 + OFFSET] = bin->max&255;
  326.         bin->mem[hexaddr + 0x3A + OFFSET] = (idx+1)&255;
  327.         bin->mem[hexaddr + 0x3D + OFFSET] = (idx+1)&255;
  328.         bin->mem[hexaddr + 0x49 + OFFSET] = (bin->exe - (hexaddr+0x47+4+OFFSET))>>8;
  329.         bin->mem[hexaddr + 0x4A + OFFSET] = (bin->exe - (hexaddr+0x47+4+OFFSET))&255;
  330.         bin->mem[hexaddr + 0x61 + OFFSET] = (tab1)>>8;
  331.         bin->mem[hexaddr + 0x62 + OFFSET] = (tab1)&255;
  332.         bin->mem[hexaddr + 0x71 + OFFSET] = (offs+2)&255;
  333.         bin->mem[hexaddr + 0x99 + OFFSET] = (bitbuf+1)&255;
  334.         bin->mem[hexaddr + 0x9E + OFFSET] = (biba)>>8;
  335.         bin->mem[hexaddr + 0x9F + OFFSET] = (biba)&255;
  336.        
  337.         membuf_truncate(outbuf, 0);
  338.        
  339.         membuf_append_char(outbuf, 0x00);
  340.         membuf_append_char(outbuf, len>>8);
  341.         membuf_append_char(outbuf, len&255);
  342.         membuf_append_char(outbuf, hexaddr>>8);
  343.         membuf_append_char(outbuf, hexaddr&255);
  344.         for(i=0; i<len; ++i)
  345.             membuf_append_char(outbuf, bin->mem[hexaddr + i]);
  346.        
  347.         membuf_append_char(outbuf, (char)0xFF);
  348.         membuf_append_char(outbuf, 0x00);
  349.         membuf_append_char(outbuf, 0x00);
  350.         membuf_append_char(outbuf, hexaddr>>8);
  351.         membuf_append_char(outbuf, hexaddr&255);
  352.     }
  353.    
  354.     ++num;
  355.     total += membuf_memlen(outbuf);
  356.     total2 += len;
  357.    
  358.     fprintf(stdout, "%s ($%04x): %d -> %d (%d%%) (avg #%d : %d -> %d (%d%%))\n",
  359.         name, bin->exe,
  360.         len, membuf_memlen(outbuf), (100*membuf_memlen(outbuf))/len,
  361.         num,
  362.         total2/num, total/num, (100*total)/total2);
  363.    
  364.    
  365.     /*
  366.     membuf_truncate(outbuf, 0);
  367.     crunch_backwards(inbuf, outbuf, options, info);
  368.     fprintf(stdout, "%s : <<< %d -> %d\n", name, img->length, membuf_memlen(outbuf));
  369.     */
  370.    
  371.         /*
  372.     LOG(LOG_NORMAL, (" Literal sequences are %sused and",
  373.                          info->literal_sequences_used ? "" : "not "));
  374.         LOG(LOG_NORMAL, (" the safety offset is %d.\n",
  375.                          info->needed_safety_offset));
  376.     */
  377.    
  378.     write_file(outfile, outbuf);
  379.    
  380.     membuf_free(outbuf);
  381.     membuf_free(inbuf);
  382.     free(bin);
  383. }
  384.  
  385. static BIN *read_bin(char *infile) {
  386.     FILE *f = fopen(infile, "rb");
  387.     BIN *bin = NULL;
  388.     if(f!=NULL) {
  389.         int c;
  390.         bin = alloc(sizeof(BIN));
  391.         bin->min = sizeof(bin->mem);
  392.         bin->max = 0;
  393.         while((c=get8(f))>=0) {
  394.             int len = get16(f), adr;
  395.             if(len<0) {c=len; break;}
  396.             if(c==0xFF) {
  397.                 if((c = get16(f))<0) break;
  398.                 bin->exe = c;
  399.                 break;
  400.             } else if(c==0x00) {
  401.                 if((c = get16(f))<0) break;
  402.                 adr = c;
  403.                 if(adr<0x6100) {
  404.                     fprintf(stderr, "Can't write below page 0 ($%04X)\n", adr);
  405.                     break;
  406.                 }
  407.                 if(adr<bin->min) bin->min = adr;
  408.                 while(len--) {
  409.                     if((c=get8(f))<0) break;
  410.                     if(adr>=0xE000) {
  411.                         fprintf(stderr, "Can't write in ROM space ($%04X)\n", adr);
  412.                         break;
  413.                     }
  414.                     bin->mem[adr++] = c;
  415.                 }
  416.                 if(adr>bin->max) bin->max = adr;
  417.             } else {
  418.                 fprintf(stderr, "Skipping unknown chunk (len=%d)\n", len);
  419.                 while(len--) if((c=get8(f))<0) break;
  420.             }
  421.         }
  422.         if(c<0) {perror(infile); free(bin); bin = NULL;}
  423.         fclose(f);
  424.     } else perror(infile);
  425.     return bin;
  426. }
  427.  
  428. static int get8(FILE *f) {
  429.     int c = fgetc(f);
  430.     if(c==EOF) return -1;
  431.     return c;
  432. }
  433.  
  434. static int get16(FILE *f) {
  435.     int t = get8(f), r;
  436.     if(t<0) return t;
  437.     r = get8(f);
  438.     if(r<0) return r;
  439.     return (t<<8) | r;
  440. }
  441.  
  442. static int endsWithIgnoreCase(char *s, char *end) {
  443.     char *t = s, *u=end;
  444.     while(*t) ++t;
  445.     while(*u) ++u;
  446.     while(--u>=end && --t>=s && tolower((int)*u) != tolower((int)*t));
  447.     return u<end;
  448. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement