├── Makefile ├── README.md ├── arc.c ├── arc.h ├── bse.c ├── bse.h ├── cbg.c ├── cbg.h ├── decrypt.c ├── decrypt.h ├── dsc.c ├── dsc.h ├── licence.txt ├── main.c ├── write.c └── write.h /Makefile: -------------------------------------------------------------------------------- 1 | FILES=main.c arc.c dsc.c cbg.c bse.c decrypt.c write.c 2 | OPTS=-Wall -ansi -pedantic -Wno-unused-result -O2 3 | LIBS=-lpng12 4 | 5 | all: 6 | gcc -o ethornell $(OPTS) $(FILES) $(LIBS) 7 | 8 | debug: 9 | gcc -g -o ethornelld $(OPTS) $(FILES) $(LIBS) 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Arc-reader 2 | ========== 3 | 4 | Allow you to read and extract files inside .arc files of the BGI engine (OverDrive/MangaGamer). 5 | 6 | Files 7 | ===== 8 | 9 | * arc.h/c : read an .arc file and get info on the contained files. 10 | * bse.h/c : remove BSE encryption (only the first 64 bytes are encrypted). 11 | * cgb.h/c : decrypt and save a "CompressedBG___" file onto the disk. 12 | * dsc.h/c : decrypt and save a "DSC FORMAT 1.00" file onto the disk. 13 | * decrypt.h/c : helper functions. 14 | * write.h/c : save a RGBA array into a PNG file. 15 | * main.c : a small example extracting all the files inside the given .arc file (compiling with the given Makefile). 16 | 17 | Dependency 18 | ========== 19 | 20 | * libpng 1.2 21 | 22 | -------------------------------------------------------------------------------- /arc.c: -------------------------------------------------------------------------------- 1 | #include "arc.h" 2 | #include 3 | #include 4 | #include 5 | 6 | struct File 7 | { 8 | char name[16]; 9 | uint32_t offset; 10 | uint32_t size; 11 | }; 12 | 13 | struct Arc 14 | { 15 | FILE * file; 16 | uint32_t data; 17 | uint32_t count; 18 | struct File * files; 19 | }; 20 | 21 | /* helper functions */ 22 | static uint32_t read32(FILE * file); 23 | static struct File read_next_file_metadata_v1(FILE * file); 24 | static struct File read_next_file_metadata_v2(FILE * file); 25 | 26 | struct Arc * arc_open(const char * filename) 27 | { 28 | char magic_string[13] = {0}; 29 | uint32_t numberOfFiles; 30 | int i, version = 0; 31 | struct File * files = NULL; 32 | struct Arc * arc = NULL; 33 | struct File (*read_next_file_metadata)(FILE * file) = NULL; 34 | 35 | FILE * file = fopen(filename, "rb"); 36 | if(file == NULL) 37 | goto file_not_found; 38 | 39 | /* check if a valid ARC file */ 40 | fread(magic_string, 1, 12, file); 41 | 42 | if(strncmp(magic_string, "PackFile ", 12) == 0) /* v1 */ 43 | version = 1; 44 | else if(strncmp(magic_string, "BURIKO ARC20", 12) == 0) /* v2 */ 45 | version = 2; 46 | else 47 | goto close_file; 48 | 49 | numberOfFiles = read32(file); 50 | files = malloc(sizeof(*files) * numberOfFiles); 51 | if(files == NULL) 52 | goto close_file; 53 | 54 | arc = malloc(sizeof(*arc)); 55 | if(arc == NULL) 56 | goto clean_files_memory; 57 | 58 | if(version == 1) 59 | read_next_file_metadata = read_next_file_metadata_v1; 60 | else /* version == 2 */ 61 | read_next_file_metadata = read_next_file_metadata_v2; 62 | 63 | for(i = 0;i < numberOfFiles;i++) 64 | { 65 | files[i] = read_next_file_metadata(file); 66 | } 67 | 68 | arc->file = file; 69 | arc->data = ftell(file); 70 | arc->count = numberOfFiles; 71 | arc->files = files; 72 | 73 | return arc; 74 | 75 | clean_files_memory: 76 | free(files); 77 | close_file: 78 | fclose(file); 79 | file_not_found: 80 | return NULL; 81 | } 82 | 83 | void arc_close(struct Arc * arc) 84 | { 85 | if(arc != NULL) 86 | { 87 | fclose(arc->file); 88 | free(arc->files); 89 | free(arc); 90 | } 91 | } 92 | 93 | uint32_t arc_files_count(struct Arc * arc) 94 | { 95 | return arc->count; 96 | } 97 | 98 | uint8_t * arc_get_file_data(struct Arc * arc, uint32_t idx) 99 | { 100 | uint8_t * data = NULL; 101 | struct File * f = NULL; 102 | 103 | if(arc == NULL || arc->count <= idx) 104 | return NULL; 105 | 106 | f = &arc->files[idx]; 107 | data = malloc(sizeof(*data) * f->size); 108 | if(data == NULL) 109 | return NULL; 110 | 111 | fseek(arc->file, arc->data + f->offset, SEEK_SET); 112 | fread(data, f->size, 1, arc->file); 113 | 114 | return data; 115 | } 116 | 117 | uint32_t arc_get_file_size(struct Arc * arc, uint32_t idx) 118 | { 119 | if(arc == NULL || arc->count <= idx) 120 | return 0; 121 | return arc->files[idx].size; 122 | } 123 | 124 | char * arc_get_file_name(struct Arc * arc, uint32_t idx) 125 | { 126 | if(arc == NULL || arc->count <= idx) 127 | return 0; 128 | return arc->files[idx].name; 129 | } 130 | 131 | /* helpers */ 132 | uint32_t read32(FILE * file) 133 | { 134 | uint32_t i = 0; 135 | fread(&i, 1, 4, file); 136 | return i; 137 | } 138 | 139 | struct File read_next_file_metadata_v1(FILE * file) 140 | { 141 | int j; 142 | struct File f; 143 | fread(&f.name, 1, 16, file); 144 | f.offset = read32(file); 145 | f.size = read32(file); 146 | 147 | /* remove non ascii bytes */ 148 | for(j = 0;j < 16;j++) 149 | { 150 | if(f.name[j] != 0 && (f.name[j] < 32 || f.name[j] > 127)) 151 | f.name[j] = '_'; 152 | } 153 | 154 | /* padding */ 155 | read32(file); 156 | read32(file); 157 | 158 | return f; 159 | } 160 | 161 | struct File read_next_file_metadata_v2(FILE * file) 162 | { 163 | int j; 164 | struct File f; 165 | fread(&f.name, 1, 16, file); 166 | 167 | /* padding */ 168 | fseek(file, 20 * 4, SEEK_CUR); 169 | 170 | f.offset = read32(file); 171 | f.size = read32(file); 172 | 173 | /* remove non ascii bytes */ 174 | for(j = 0;j < 16;j++) 175 | { 176 | if(f.name[j] != 0 && (f.name[j] < 32 || f.name[j] > 127)) 177 | f.name[j] = '_'; 178 | } 179 | 180 | /* padding */ 181 | fseek(file, 6 * 4, SEEK_CUR); 182 | 183 | return f; 184 | } 185 | -------------------------------------------------------------------------------- /arc.h: -------------------------------------------------------------------------------- 1 | #ifndef ARC_ETHORNELL_H 2 | #define ARC_ETHORNELL_H 3 | 4 | #include 5 | 6 | struct Arc; 7 | 8 | /** 9 | * open the given file and return NULL in case of an error 10 | */ 11 | struct Arc * arc_open(const char * filename); 12 | 13 | /** 14 | * close the file and free its memory 15 | */ 16 | void arc_close(struct Arc * arc); 17 | 18 | /** 19 | * return the number of files contained in the arc file 20 | */ 21 | uint32_t arc_files_count(struct Arc * arc); 22 | 23 | /** 24 | * get the raw data of the file at position "idx" 25 | * WARNING: make a deep copy of the data, you have to 26 | * free the memory yourself with free() 27 | */ 28 | uint8_t * arc_get_file_data(struct Arc * arc, uint32_t idx); 29 | 30 | /** 31 | * get the (maybe compressed) size of the file at position "idx" 32 | */ 33 | uint32_t arc_get_file_size(struct Arc * arc, uint32_t idx); 34 | 35 | /** 36 | * get the name of the file at position "idx" 37 | */ 38 | char * arc_get_file_name(struct Arc * arc, uint32_t idx); 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /bse.c: -------------------------------------------------------------------------------- 1 | #include "bse.h" 2 | #include "decrypt.h" 3 | 4 | #include 5 | #include 6 | 7 | static int32_t bse_rand(int32_t * seed); 8 | 9 | int bse_is_valid(uint8_t * data, uint32_t size) 10 | { 11 | if(size < 80) return 0; 12 | 13 | return (memcmp((char*)data, "BSE 1.0", 7) == 0); 14 | } 15 | 16 | int bse_decrypt(uint8_t * crypted) 17 | { 18 | int32_t hash = 0; 19 | uint8_t sum_check = 0; 20 | uint8_t xor_check = 0; 21 | uint8_t sum_data = 0; 22 | uint8_t xor_data = 0; 23 | int flags[64] = {0}; 24 | int counter = 0; 25 | 26 | crypted += 8; 27 | /* 0x100 =*/ read16(&crypted); 28 | sum_check = read8(&crypted); 29 | xor_check = read8(&crypted); 30 | hash = read32(&crypted); 31 | 32 | for(counter = 0;counter < 64;counter++) 33 | { 34 | int target = NULL; 35 | int s, k; 36 | int r = bse_rand(&hash); 37 | int i = r & 0x3F; 38 | 39 | while(flags[i]) 40 | { 41 | i = (i + 1) & 0x3F; 42 | } 43 | 44 | r = bse_rand(&hash); 45 | s = r & 0x07; 46 | target = i; 47 | 48 | k = bse_rand(&hash); 49 | r = bse_rand(&hash); 50 | r = ((crypted[target] & 255) - r) & 255; 51 | 52 | if(k & 1) 53 | { 54 | crypted[target] = r << s | r >> (8 - s); 55 | } 56 | else 57 | { 58 | crypted[target] = r >> s | r << (8 - s); 59 | } 60 | 61 | flags[i] = 1; 62 | } 63 | 64 | for(counter = 0;counter < 64;counter++) 65 | { 66 | sum_data = sum_data + (crypted[counter] & 255); 67 | xor_data = xor_data ^ (crypted[counter] & 255); 68 | } 69 | 70 | if(sum_data == sum_check && xor_data == xor_check) 71 | { 72 | return 1; 73 | } 74 | 75 | return 0; 76 | } 77 | 78 | int32_t bse_rand(int32_t * seed) 79 | { 80 | int32_t tmp = (((*seed * 257 >> 8) + *seed * 97) + 23) ^ -1496474763; 81 | *seed = ((tmp >> 16) & 65535) | (tmp << 16); 82 | return *seed & 32767; 83 | } 84 | -------------------------------------------------------------------------------- /bse.h: -------------------------------------------------------------------------------- 1 | #ifndef BSE_ETHORNELL_H 2 | #define BSE_ETHORNELL_H 3 | 4 | #include 5 | 6 | /** 7 | * check if "data" is a "BSE 1.0" file 8 | */ 9 | int bse_is_valid(uint8_t * data, uint32_t size); 10 | 11 | /** 12 | * decrypt the file (only the first 64 bytes are encrypted). 13 | * WARNING: you have to check yourself if "crypted" is a valid file with "bse_is_valid" 14 | * before calling "bse_decrypt" 15 | */ 16 | int bse_decrypt(uint8_t * crypted); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /cbg.c: -------------------------------------------------------------------------------- 1 | #include "cbg.h" 2 | #include "decrypt.h" 3 | #include "write.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct NodeCBG 11 | { 12 | uint32_t vv[6]; 13 | }; 14 | 15 | static uint32_t readVariable(uint8_t ** ptr); 16 | static uint32_t color_add(uint32_t x, uint32_t y); 17 | static uint32_t color_avg(uint32_t x, uint32_t y); 18 | static uint32_t extract(uint8_t ** src, uint32_t bpp); 19 | static int method2(uint32_t table1[256], struct NodeCBG table2[511]); 20 | 21 | int cbg_is_valid(uint8_t * data, uint32_t size) 22 | { 23 | if(size < 48) return 0; 24 | 25 | return (memcmp((char*)data, "CompressedBG___", 15) == 0); 26 | } 27 | 28 | uint8_t * cbg_decrypt(uint8_t * crypted, uint16_t * pwidth, uint16_t * pheight) 29 | { 30 | uint16_t width, height; 31 | uint32_t bpp, data1_len, data0_val, data0_len; 32 | uint8_t sum_check, xor_check; 33 | uint32_t table[256]; 34 | struct NodeCBG table2[511]; 35 | uint32_t n; 36 | uint8_t sum_data = 0; 37 | uint8_t xor_data = 0; 38 | uint8_t * ptr; 39 | int method2_res; 40 | uint8_t *data1, *data3, *psrc, *pdst; 41 | uint32_t mask = 0x80; 42 | int type = 0; 43 | uint32_t * data; 44 | int aa; 45 | uint8_t * src; 46 | uint32_t * dst; 47 | uint32_t c = 0; 48 | uint16_t x, y; 49 | uint8_t *pixels, *pixels_ptr; 50 | uint32_t px; 51 | uint8_t * data0; 52 | 53 | crypted += 16; 54 | width = read16(&crypted); 55 | height = read16(&crypted); 56 | bpp = read32(&crypted); 57 | read32(&crypted); 58 | read32(&crypted); 59 | data1_len = read32(&crypted); 60 | data0_val = read32(&crypted); 61 | data0_len = read32(&crypted); 62 | sum_check = read8(&crypted); 63 | xor_check = read8(&crypted); 64 | /*uint8_t unknown =*/ read16(&crypted); 65 | *pwidth = width; 66 | *pheight = height; 67 | 68 | data0 = malloc(sizeof(*data0) * data0_len); 69 | memcpy(data0, crypted, data0_len); 70 | crypted += data0_len; 71 | 72 | for(n = 0; n < data0_len; n++) 73 | { 74 | data0[n] -= hash_update(&data0_val) & 0xFF; 75 | sum_data += data0[n]; 76 | xor_data ^= data0[n]; 77 | } 78 | 79 | if(sum_data != sum_check || xor_data != xor_check) 80 | { 81 | free(data0); 82 | return NULL; 83 | } 84 | 85 | ptr = data0; 86 | for(n = 0; n < 256; n++) 87 | { 88 | table[n] = readVariable(&ptr); 89 | } 90 | free(data0); 91 | 92 | method2_res = method2(table, table2); 93 | data1 = malloc(sizeof(*data1) * data1_len); 94 | 95 | for(n = 0; n < data1_len; n++) 96 | { 97 | uint32_t cvalue = method2_res; 98 | 99 | if(table2[method2_res].vv[2] == 1) 100 | { 101 | do 102 | { 103 | int bit = !!(*crypted & mask); 104 | mask >>= 1; 105 | 106 | cvalue = table2[cvalue].vv[4 + bit]; 107 | 108 | if (!mask) 109 | { 110 | crypted++; 111 | mask = 0x80; 112 | } 113 | } while(table2[cvalue].vv[2] == 1); 114 | } 115 | 116 | data1[n] = cvalue; 117 | } 118 | 119 | data3 = malloc(sizeof(*data3) * width * height * 4); 120 | psrc = data1; 121 | pdst = data3; 122 | while(psrc < data1 + data1_len) 123 | { 124 | uint32_t len = readVariable(&psrc); 125 | uint32_t i; 126 | if(type) 127 | { 128 | for(i = 0;i < len;i++) 129 | { 130 | pdst[i] = 0; 131 | } 132 | } 133 | else 134 | { 135 | for(i = 0;i < len;i++) 136 | { 137 | pdst[i] = psrc[i]; 138 | } 139 | psrc += len; 140 | } 141 | pdst += len; 142 | type = !type; 143 | } 144 | free(data1); 145 | 146 | data = malloc(sizeof(*data) * width * height); 147 | for(aa = 0;aa < width * height;aa++) 148 | { 149 | data[aa] = 0x00; 150 | } 151 | 152 | src = data3; 153 | dst = data; 154 | 155 | for(x = 0;x < width;x++) 156 | { 157 | c = color_add(c, extract(&src, bpp)); 158 | *dst = c; 159 | dst++; 160 | } 161 | for(y = 1;y < height;y++) 162 | { 163 | c = color_add((*(dst - width)), extract(&src, bpp)); 164 | *dst = c; 165 | dst++; 166 | for(x = 1;x < width;x++) 167 | { 168 | uint32_t moy = color_avg(c, (*(dst - width))); 169 | c = color_add(moy, extract(&src, bpp)); 170 | *dst = c; 171 | dst++; 172 | } 173 | } 174 | free(data3); 175 | 176 | pixels = malloc(sizeof(*pixels) * width*height*4); 177 | pixels_ptr = pixels; 178 | for(px = 0;px < width * height; px++) 179 | { 180 | uint8_t r, g, b, a; 181 | if(bpp == 32) 182 | { 183 | a = (data[px] >> 24) & 0xFF; 184 | r = (data[px] >> 16) & 0xFF; 185 | g = (data[px] >> 8) & 0xFF; 186 | b = data[px] & 0xFF; 187 | } 188 | else 189 | { 190 | b = (data[px] >> 16) & 0xFF; 191 | g = (data[px] >> 8) & 0xFF; 192 | r = data[px] & 0xFF; 193 | a = 0xFF; 194 | } 195 | 196 | *pixels_ptr = r; pixels_ptr++; 197 | *pixels_ptr = g; pixels_ptr++; 198 | *pixels_ptr = b; pixels_ptr++; 199 | *pixels_ptr = a; pixels_ptr++; 200 | } 201 | 202 | free(data); 203 | 204 | return pixels; 205 | } 206 | 207 | int cbg_save(uint8_t * data, uint32_t width, uint32_t height, const char * filename) 208 | { 209 | char file_name[1024]; 210 | sprintf(file_name, "%s.png", filename); 211 | return write_RGBA_to_png(width, height, data, file_name); 212 | } 213 | 214 | uint32_t readVariable(uint8_t ** ptr) 215 | { 216 | uint8_t c; 217 | uint32_t v = 0; 218 | int32_t shift = 0; 219 | do 220 | { 221 | c = **ptr; 222 | (*ptr)++; 223 | v |= (c & 0x7F) << shift; 224 | shift += 7; 225 | } while (c & 0x80); 226 | return v; 227 | } 228 | 229 | uint32_t color_avg(uint32_t x, uint32_t y) 230 | { 231 | uint32_t a = (((x & 0xFF000000) / 2) + ((y & 0xFF000000) / 2)) & 0xFF000000; 232 | uint32_t r = (((x & 0x00FF0000) + (y & 0x00FF0000)) / 2) & 0x00FF0000; 233 | uint32_t g = (((x & 0x0000FF00) + (y & 0x0000FF00)) / 2) & 0x0000FF00; 234 | uint32_t b = (((x & 0x000000FF) + (y & 0x000000FF)) / 2) & 0x000000FF; 235 | return (a | r | g | b); 236 | } 237 | 238 | uint32_t color_add(uint32_t x, uint32_t y) 239 | { 240 | uint32_t a = ((x & 0xFF000000) + (y & 0xFF000000)) & 0xFF000000; 241 | uint32_t r = ((x & 0x00FF0000) + (y & 0x00FF0000)) & 0x00FF0000; 242 | uint32_t g = ((x & 0x0000FF00) + (y & 0x0000FF00)) & 0x0000FF00; 243 | uint32_t b = ((x & 0x000000FF) + (y & 0x000000FF)) & 0x000000FF; 244 | 245 | return (a | r | g | b); 246 | } 247 | 248 | uint32_t extract(uint8_t ** src, uint32_t bpp) 249 | { 250 | if(bpp == 32) 251 | { 252 | return read32(src); 253 | } 254 | else 255 | { 256 | uint8_t r = read8(src), g = r, b = r; 257 | if(bpp == 24) 258 | { 259 | g = read8(src); 260 | b = read8(src); 261 | } 262 | return (0xff000000 | r << 16 | g << 8 | b); 263 | } 264 | } 265 | 266 | int method2(uint32_t table1[256], struct NodeCBG table2[511]) 267 | { 268 | uint32_t sum_of_values = 0; 269 | struct NodeCBG node; 270 | uint32_t n; 271 | uint32_t cnodes = 256; 272 | uint32_t vinfo[2]; 273 | 274 | for(n = 0;n < 256;n++) 275 | { 276 | table2[n].vv[0] = table1[n] > 0; 277 | table2[n].vv[1] = table1[n]; 278 | table2[n].vv[2] = 0; 279 | table2[n].vv[3] =-1; 280 | table2[n].vv[4] = n; 281 | table2[n].vv[5] = n; 282 | sum_of_values += table1[n]; 283 | } 284 | 285 | node.vv[0] = 0; 286 | node.vv[1] = 0; 287 | node.vv[2] = 1; 288 | node.vv[3] =-1; 289 | node.vv[4] =-1; 290 | node.vv[5] =-1; 291 | 292 | for(n = 0; n < 255; n++) 293 | table2[256 + n] = node; 294 | 295 | while(1) 296 | { 297 | uint32_t m; 298 | for(m = 0; m < 2; m++) 299 | { 300 | uint32_t min_value = 0xFFFFFFFF; 301 | vinfo[m] = UINT_MAX; 302 | 303 | for(n = 0; n < cnodes; n++) 304 | { 305 | struct NodeCBG * cnode = &table2[n]; 306 | 307 | if(cnode->vv[0] && (cnode->vv[1] < min_value)) 308 | { 309 | vinfo[m] = n; 310 | min_value = cnode->vv[1]; 311 | } 312 | } 313 | 314 | if(vinfo[m] != UINT_MAX) 315 | { 316 | table2[vinfo[m]].vv[0] = 0; 317 | table2[vinfo[m]].vv[3] = cnodes; 318 | } 319 | } 320 | 321 | node.vv[0] = 1; 322 | node.vv[1] = ((vinfo[1] != 0xFFFFFFFF) ? table2[vinfo[1]].vv[1] : 0) + table2[vinfo[0]].vv[1]; 323 | node.vv[2] = 1; 324 | node.vv[3] =-1; 325 | node.vv[4] = vinfo[0]; 326 | node.vv[5] = vinfo[1]; 327 | 328 | table2[cnodes++] = node; 329 | 330 | if(node.vv[1] == sum_of_values) 331 | break; 332 | } 333 | 334 | return cnodes - 1; 335 | } 336 | 337 | -------------------------------------------------------------------------------- /cbg.h: -------------------------------------------------------------------------------- 1 | #ifndef CBG_ETHORNELL_H 2 | #define CBG_ETHORNELL_H 3 | 4 | #include 5 | 6 | /** 7 | * check if "data" is a "CompressedBG___" file 8 | */ 9 | int cbg_is_valid(uint8_t * data, uint32_t size); 10 | 11 | /** 12 | * decrypt the file. 13 | * WARNING: you have to check yourself if "crypted" is a valid file with "cbg_is_valid" 14 | * before calling "cbg_decrypt" 15 | */ 16 | uint8_t * cbg_decrypt(uint8_t * crypted, uint16_t * pwidth, uint16_t * pheight); 17 | 18 | /** 19 | * save the file in PNG format (and append ".png" to "filename"). 20 | */ 21 | int cbg_save(uint8_t * data, uint32_t width, uint32_t height, const char * filename); 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /decrypt.c: -------------------------------------------------------------------------------- 1 | #include "decrypt.h" 2 | 3 | uint32_t read32(uint8_t ** c) 4 | { 5 | uint32_t i = *(uint32_t*)*c; 6 | (*c) += 4; 7 | return i; 8 | } 9 | 10 | uint16_t read16(uint8_t ** c) 11 | { 12 | uint16_t i = *(uint16_t*)*c; 13 | (*c) += 2; 14 | return i; 15 | } 16 | 17 | uint8_t read8(uint8_t ** c) 18 | { 19 | uint8_t i = *(uint8_t*)*c; 20 | (*c)++; 21 | return i; 22 | } 23 | 24 | uint8_t myHIBYTE(uint16_t v) { return (v >> 8); } 25 | uint8_t myLOBYTE(uint16_t v) { return (v & 0xFFFF); } 26 | uint16_t myHIWORD(uint32_t v) { return (v >> 16); } 27 | uint16_t myLOWORD(uint32_t v) { return (v & 0xFFFF); } 28 | 29 | uint32_t hash_update(uint32_t * hash_val) 30 | { 31 | uint32_t eax, edx; 32 | edx = (20021 * myLOWORD(*hash_val)); 33 | eax = (20021 * myHIWORD(*hash_val)) + (346 * *hash_val) + myHIWORD(edx); 34 | *hash_val = (myLOWORD(eax) << 16) + myLOWORD(edx) + 1; 35 | return eax & 0x7FFF; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /decrypt.h: -------------------------------------------------------------------------------- 1 | #ifndef DECRYPT_ETHORNELL_H 2 | #define DECRYPT_ETHORNELL_H 3 | 4 | #include 5 | 6 | uint32_t read32(uint8_t ** c); 7 | uint16_t read16(uint8_t ** c); 8 | uint8_t read8 (uint8_t ** c); 9 | 10 | uint8_t myHIBYTE(uint16_t v); 11 | uint8_t myLOBYTE(uint16_t v); 12 | uint16_t myHIWORD(uint32_t v); 13 | uint16_t myLOWORD(uint32_t v); 14 | 15 | uint32_t hash_update(uint32_t * hash_val); 16 | 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /dsc.c: -------------------------------------------------------------------------------- 1 | #include "dsc.h" 2 | #include "decrypt.h" 3 | #include "write.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | struct NodeDSC 10 | { 11 | uint32_t has_childs; 12 | uint32_t leaf_value; 13 | uint32_t childs[2]; 14 | }; 15 | 16 | static int buffer_sorting(const void * a, const void * b); 17 | static int dsc_is_image(uint8_t * data); 18 | 19 | int dsc_is_valid(uint8_t * data, uint32_t size) 20 | { 21 | if(size < 32) return 0; 22 | 23 | return (memcmp((char*)data, "DSC FORMAT 1.00", 15) == 0); 24 | } 25 | 26 | uint8_t * dsc_decrypt(uint8_t * crypted, uint32_t crypted_size, uint32_t * decrypted_size) 27 | { 28 | int i, n; 29 | uint32_t hash, size; 30 | uint8_t * begin_of_raw_data = crypted; 31 | int buffer_len = 0; 32 | uint32_t buffer[512] = {0}; 33 | uint32_t vector0[1024] = {0}; 34 | uint32_t nn = 0, toggle = 0x200, dec0 = 1, value_set = 1; 35 | uint32_t * v13 = vector0; 36 | int buffer_cur; 37 | uint8_t * data; 38 | uint32_t bits = 0, nbits = 0; 39 | uint32_t src_ptr = 0, dst_ptr = 0; 40 | uint32_t src_end, dst_end; 41 | struct NodeDSC * nodes; 42 | 43 | crypted += 16; 44 | hash = read32(&crypted); 45 | size = read32(&crypted); 46 | /*uint32_t v2 = */ read32(&crypted); 47 | /*uint32_t padding = */ read32(&crypted); 48 | *decrypted_size = size; 49 | 50 | nodes = malloc(sizeof(*nodes) * 1024); 51 | for(i = 0;i < 1024;i++) 52 | { 53 | nodes[i].has_childs = 0; 54 | nodes[i].leaf_value = 0; 55 | } 56 | 57 | for(n = 0;n < 512;n++) 58 | { 59 | uint8_t v = crypted[n] - (uint8_t)hash_update(&hash); 60 | if(v) 61 | { 62 | buffer[buffer_len] = (v << 16) + n; 63 | buffer_len++; 64 | } 65 | } 66 | 67 | qsort(buffer, buffer_len, sizeof(buffer[0]), buffer_sorting); 68 | 69 | for(buffer_cur = 0;buffer_cur < buffer_len;nn++) 70 | { 71 | uint32_t * vector0_ptr = &vector0[toggle]; 72 | uint32_t * vector0_ptr_init = vector0_ptr; 73 | uint32_t group_count = 0; 74 | uint32_t v18; 75 | 76 | for( ;nn == myHIWORD(buffer[buffer_cur]); buffer_cur++, v13++, group_count++) 77 | { 78 | nodes[*v13].has_childs = 0; 79 | nodes[*v13].leaf_value = buffer[buffer_cur] & 0x1FF; 80 | } 81 | 82 | v18 = 2 * (dec0 - group_count); 83 | if(group_count < dec0) 84 | { 85 | uint32_t dd; 86 | dec0 = (dec0 - group_count); 87 | for(dd = 0; dd < dec0; dd++) 88 | { 89 | int m; 90 | nodes[*v13].has_childs = 1; 91 | for(m = 0; m < 2; m++) 92 | { 93 | *vector0_ptr++ = nodes[*v13].childs[m] = value_set; 94 | value_set++; 95 | } 96 | v13++; 97 | } 98 | } 99 | dec0 = v18; 100 | v13 = vector0_ptr_init; 101 | toggle ^= 0x200; 102 | } 103 | 104 | crypted += 512; 105 | data = malloc(sizeof(*data) * size); 106 | 107 | src_end = crypted_size - (crypted - begin_of_raw_data); 108 | dst_end = size; 109 | 110 | while(src_ptr < src_end && dst_ptr < dst_end) 111 | { 112 | uint32_t nentry = 0; 113 | uint16_t info; 114 | 115 | for(; nodes[nentry].has_childs; nbits--, bits = (bits << 1) & 0xFF) 116 | { 117 | if(!nbits) 118 | { 119 | nbits = 8; 120 | bits = (uint8_t)crypted[src_ptr++]; 121 | } 122 | nentry = nodes[nentry].childs[(bits >> 7) & 1]; 123 | } 124 | 125 | info = myLOWORD(nodes[nentry].leaf_value); 126 | 127 | if(myHIBYTE(info) == 1) 128 | { 129 | uint32_t cvalue = bits >> (8 - nbits); 130 | uint32_t nbits2 = nbits; 131 | int32_t offset; 132 | uint32_t ring_ptr, count; 133 | if(nbits < 12) 134 | { 135 | uint32_t bytes = ((11 - nbits) >> 3) + 1; 136 | nbits2 = nbits; 137 | while (bytes--) 138 | { 139 | cvalue = (uint8_t)crypted[src_ptr++] + (cvalue << 8); 140 | nbits2 += 8; 141 | } 142 | } 143 | nbits = nbits2 - 12; 144 | bits = myLOBYTE(cvalue << (8 - (nbits2 - 12))); 145 | 146 | offset = ((uint32_t)cvalue >> (nbits2 - 12)) + 2; 147 | ring_ptr = dst_ptr - offset; 148 | count = myLOBYTE(info) + 2; 149 | 150 | while (count--) 151 | { 152 | uint8_t tmp = data[ring_ptr++]; 153 | data[dst_ptr++] = tmp; 154 | } 155 | } 156 | else 157 | { 158 | data[dst_ptr++] = myLOBYTE(info); 159 | } 160 | } 161 | 162 | free(nodes); 163 | 164 | return data; 165 | } 166 | 167 | int dsc_is_image(uint8_t * data) 168 | { 169 | /* 170 | header is 16b: 171 | - width 2b 172 | - height 2b 173 | - bpp 1b 174 | - 11 zeros 1b 175 | */ 176 | uint8_t * ptr = data; 177 | uint16_t width, height; 178 | uint8_t bpp; 179 | int i; 180 | 181 | width = read16(&ptr); 182 | if(width == 0 || width > 8096) 183 | goto not_an_image; 184 | 185 | height = read16(&ptr); 186 | if(height == 0 || height > 8096) 187 | goto not_an_image; 188 | 189 | bpp = read8(&ptr); 190 | if(bpp != 8 && bpp != 24 && bpp != 32) 191 | goto not_an_image; 192 | 193 | for(i = 0;i < 11;i++) 194 | { 195 | uint8_t blank = read8(&ptr); 196 | if(blank != 0) 197 | goto not_an_image; 198 | } 199 | 200 | return 1; 201 | 202 | not_an_image: 203 | return 0; 204 | } 205 | 206 | int dsc_save(uint8_t * data, uint32_t size, const char * filename) 207 | { 208 | int ret = 1; 209 | /* avoid segfault in case data is less than 16 bits */ 210 | if(size > 15 && dsc_is_image(data)) 211 | { 212 | int y; 213 | char file_name[1024]; 214 | uint16_t width = read16(&data); 215 | uint16_t height = read16(&data); 216 | uint8_t bpp = read8(&data); 217 | uint8_t * pixels = malloc(sizeof(*pixels) * width * height * 4); 218 | uint8_t * pixels_ptr = pixels; 219 | data += 11; 220 | 221 | for(y = 0;y < height;y++) 222 | { 223 | int x; 224 | for(x = 0;x < width;x++) 225 | { 226 | uint8_t a = 255; 227 | uint8_t b = read8(&data); 228 | uint8_t g = read8(&data); 229 | uint8_t r = read8(&data); 230 | if(bpp == 32) 231 | a = read8(&data); 232 | 233 | *pixels_ptr = r; pixels_ptr++; 234 | *pixels_ptr = g; pixels_ptr++; 235 | *pixels_ptr = b; pixels_ptr++; 236 | *pixels_ptr = a; pixels_ptr++; 237 | } 238 | } 239 | 240 | sprintf(file_name, "%s.png", filename); 241 | ret = write_RGBA_to_png(width, height, pixels, file_name); 242 | 243 | free(pixels); 244 | } 245 | else 246 | { 247 | FILE * f = fopen(filename, "wb"); 248 | fwrite(data, size, 1, f); 249 | fclose(f); 250 | } 251 | 252 | return ret; 253 | } 254 | 255 | int buffer_sorting(const void * a, const void * b) 256 | { 257 | const uint32_t * qa = a; 258 | const uint32_t * qb = b; 259 | return *qa - *qb; 260 | } 261 | 262 | -------------------------------------------------------------------------------- /dsc.h: -------------------------------------------------------------------------------- 1 | #ifndef DSC_ETHORNELL_H 2 | #define DSC_ETHORNELL_H 3 | 4 | #include 5 | 6 | /** 7 | * check if "data" is a "DSC FORMAT 1.00" file 8 | */ 9 | int dsc_is_valid(uint8_t * data, uint32_t size); 10 | 11 | /** 12 | * decrypt the file. 13 | * WARNING: you have to check yourself if "crypted" is a valid file with "dsc_is_valid" 14 | * before calling "dsc_decrypt" 15 | */ 16 | uint8_t * dsc_decrypt(uint8_t * crypted, uint32_t crypted_size, uint32_t * decrypted_size); 17 | 18 | /** 19 | * save the file, if it's an image, save it in PNG format (and append ".png" to "filename"). 20 | */ 21 | int dsc_save(uint8_t * data, uint32_t size, const char * filename); 22 | 23 | #endif 24 | 25 | -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | Arc Reader - Reading .arc files from the BGI engine 2 | Copyright (C) 2011 Alexander Roper (minirop@peyj.com) 3 | 4 | This software is provided 'as-is', without any express or implied warranty. 5 | In no event will the authors be held liable for any damages arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it freely, 9 | subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; 12 | you must not claim that you wrote the original software. 13 | If you use this software in a product, an acknowledgment 14 | in the product documentation would be appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, 17 | and must not be misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "arc.h" 5 | #include "dsc.h" 6 | #include "cbg.h" 7 | #include "bse.h" 8 | 9 | #ifdef _WIN32 10 | #include 11 | #define makedir(path) CreateDirectory(path, NULL); 12 | #else 13 | #include 14 | #include 15 | #define makedir(path) mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 16 | #endif 17 | 18 | int main(int argc, char** argv) 19 | { 20 | uint32_t i, count; 21 | char file_name_with_path[1024] = {0}; 22 | struct Arc * arc = NULL; 23 | 24 | if(argc != 2 && argc != 3) 25 | { 26 | puts("Usage: a.exe [path]"); 27 | return 1; 28 | } 29 | 30 | arc = arc_open(argv[1]); 31 | if(arc == NULL) 32 | { 33 | printf("can't read file: %s\n", argv[1]); 34 | return 1; 35 | } 36 | 37 | count = arc_files_count(arc); 38 | 39 | if(argc == 3) 40 | makedir(argv[2]); 41 | 42 | printf("number of file: %d\n", count); 43 | 44 | for(i = 0;i < count;i++) 45 | { 46 | uint8_t * data = NULL; 47 | uint8_t * raw_data = arc_get_file_data(arc, i); 48 | uint8_t * bse_data = raw_data; 49 | uint32_t filesize = arc_get_file_size(arc, i); 50 | int good = 1; 51 | 52 | if(argc == 3) 53 | sprintf(file_name_with_path, "%s/%s", argv[2], arc_get_file_name(arc, i)); 54 | else 55 | sprintf(file_name_with_path, "%s", arc_get_file_name(arc, i)); 56 | 57 | printf("%s...", arc_get_file_name(arc, i)); fflush(stdout); 58 | 59 | if(bse_is_valid(raw_data, filesize)) 60 | { 61 | printf("BSE..."); fflush(stdout); 62 | if(bse_decrypt(raw_data)) 63 | { 64 | bse_data = raw_data + 16; 65 | } 66 | } 67 | 68 | if(dsc_is_valid(bse_data, filesize)) 69 | { 70 | uint32_t fsize; 71 | printf("DSC..."); fflush(stdout); 72 | 73 | data = dsc_decrypt(bse_data, filesize, &fsize); 74 | good = dsc_save(data, fsize, file_name_with_path); 75 | } 76 | else if(cbg_is_valid(bse_data, filesize)) 77 | { 78 | uint16_t w, h; 79 | printf("CBG..."); fflush(stdout); 80 | 81 | data = cbg_decrypt(bse_data, &w, &h); 82 | good = cbg_save(data, w, h, file_name_with_path); 83 | } 84 | else 85 | { 86 | FILE * f = fopen(file_name_with_path, "wb"); 87 | printf("uncompressed..."); fflush(stdout); 88 | fwrite(bse_data, filesize, 1, f); 89 | fclose(f); 90 | } 91 | 92 | if(good) 93 | puts("ok"); 94 | else 95 | puts("ERROR"); 96 | 97 | if(raw_data != NULL) 98 | free(raw_data); 99 | if(data != NULL) 100 | free(data); 101 | } 102 | 103 | arc_close(arc); 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /write.c: -------------------------------------------------------------------------------- 1 | #include "write.h" 2 | #include 3 | #include 4 | 5 | #define PNGSETJMP if(setjmp(png_jmpbuf(png_ptr))) \ 6 | { \ 7 | png_destroy_write_struct(&png_ptr, &info_ptr); \ 8 | return 0; \ 9 | } 10 | 11 | int write_RGBA_to_png(uint16_t width, uint16_t height, uint8_t * array, const char * filename) 12 | { 13 | int x, y; 14 | png_structp png_ptr; 15 | png_infop info_ptr; 16 | png_bytep * row_pointers; 17 | FILE * fp = fopen(filename, "wb"); 18 | 19 | if(fp == NULL) 20 | return 0; 21 | 22 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 23 | info_ptr = png_create_info_struct(png_ptr); 24 | 25 | PNGSETJMP 26 | 27 | png_init_io(png_ptr, fp); 28 | 29 | PNGSETJMP 30 | 31 | png_set_IHDR(png_ptr, info_ptr, width, height, 32 | 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, 33 | PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 34 | 35 | png_write_info(png_ptr, info_ptr); 36 | 37 | /* write bytes */ 38 | PNGSETJMP 39 | 40 | /* convert uint8_t[] to pngbyte[][] */ 41 | row_pointers = malloc(sizeof(*row_pointers) * height); 42 | for(y = 0;y < height;y++) 43 | { 44 | row_pointers[y] = malloc(png_get_rowbytes(png_ptr, info_ptr)); 45 | for(x = 0;x < width * 4;x += 4) 46 | { 47 | row_pointers[y][x] = array[y * width * 4 + x]; 48 | row_pointers[y][x+1] = array[y * width * 4 + x+1]; 49 | row_pointers[y][x+2] = array[y * width * 4 + x+2]; 50 | row_pointers[y][x+3] = array[y * width * 4 + x+3]; 51 | } 52 | } 53 | 54 | png_write_image(png_ptr, row_pointers); 55 | 56 | /* end write */ 57 | PNGSETJMP 58 | 59 | png_write_end(png_ptr, NULL); 60 | png_destroy_write_struct(&png_ptr, &info_ptr); 61 | 62 | for(y = 0;y < height;y++) 63 | { 64 | free(row_pointers[y]); 65 | } 66 | free(row_pointers); 67 | 68 | fclose(fp); 69 | 70 | return 1; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /write.h: -------------------------------------------------------------------------------- 1 | #ifndef WRITE_ETHORNELL_H 2 | #define WRITE_ETHORNELL_H 3 | 4 | #include 5 | 6 | /** 7 | * save a RGBA-array in a PNG file. 8 | */ 9 | int write_RGBA_to_png(uint16_t width, uint16_t height, uint8_t * array, const char * filename); 10 | 11 | #endif 12 | 13 | --------------------------------------------------------------------------------