├── COPYING ├── README.md └── buram.c /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Ian Karlsson 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # buram 2 | This program allows you to manipulate Mega CD/Sega CD backup RAM images 3 | using a tar-like interface. 4 | 5 | ## Compiling 6 | `gcc -o buram buram.c` 7 | 8 | ## Usage 9 | `<>` indicates optional arguments and `[]` indicates mandatory arguments. 10 | 11 | `buram [-operations] <-options> ` 12 | 13 | ### Operations 14 | - `-c `: Format new BRAM image and add files 15 | - `-a [files...]`: Add files 16 | - `-d [pattern]`: Delete files 17 | - `-t `: List files 18 | - `-x `: Extract files 19 | 20 | ### Options 21 | - `-f [filename]`: Set filename for reading (and writing if `-o` argument not given) 22 | - `-o [filename]`: Set filename for writing only 23 | - `-v`: Verbose output 24 | - `-i`: Display volume information 25 | - `-s [size]`: Set size in bytes when creating a new BRAM image with `-c` 26 | - `-p` Add files with ECC protection 27 | - `-n` Add files without ECC protection (default) 28 | 29 | If no filename is given with `-f` or `-o` options, the default is `test.brm` 30 | 31 | ### Examples 32 | Create a new archive `myarchive.brm` containing a savefile called `SONICCD`: 33 | 34 | $ buram -cf myarchive.brm SONICCD.bin 35 | 36 | Check the number of free blocks in `myarchive.brm`: 37 | 38 | $ buram -if myarchive.brm 39 | 40 | files: 1, free blocks: 114 41 | 42 | List all files in `myarchive.brm`: 43 | 44 | $ buram -tf myarchive.brm 45 | 1 11 SONICCD 46 | 47 | The last two operations could be combined like this, additionally adding the `-v` flag to see more information: 48 | 49 | $ buram -tvfi myarchive.brm 50 | 1 11 SONICCD 51 | volume: _ 52 | format: SEGA_CD_ROM 53 | media_id: RAM_CARTRIDGE 54 | 55 | files: 1, free blocks: 114 56 | 57 | Extract `SONICCD` from `myarchive.brm`: 58 | 59 | $ buram -xf myarchive.brm SONICCD 60 | 61 | Delete `SONICCD` from `myarchive.brm`: 62 | 63 | $ buram -df myarchive.brm SONICCD 64 | 65 | ### License 66 | © 2022 Ian Karlsson 67 | 68 | Licensed under the MIT license, see COPYING 69 | 70 | -------------------------------------------------------------------------------- /buram.c: -------------------------------------------------------------------------------- 1 | //===================================================================== 2 | // 3 | // Mega CD BackupRAM management tool 4 | // 5 | // Copyright (c) 2022 Ian Karlsson 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | //===================================================================== 25 | 26 | #include //printf, snprintf 27 | #include //uint8_t, uint16_t, uint32_t 28 | #include //malloc, calloc 29 | #include //memcpy, memcmp 30 | #include //isalnum, tolower, toupper 31 | #include //PATH_MAX 32 | 33 | #ifndef NDEBUG 34 | #define DEBUG(...) {fprintf(stdout,__VA_ARGS__);} 35 | #else 36 | #define DEBUG(...) {} 37 | #endif 38 | 39 | #define ERROR(...) {fprintf(stderr,__VA_ARGS__); exit(-1);} 40 | 41 | struct buram_rs 42 | { 43 | uint8_t mm; // Bits per symbol 44 | uint8_t nn; // Symbols per block 45 | uint8_t index_of[256]; // Antilog lookup 46 | uint8_t alpha_to[256]; // Log lookup 47 | const uint8_t *polygen; // Generator polynomial lookup 48 | }; 49 | 50 | struct buram 51 | { 52 | struct buram_rs rs8; 53 | struct buram_rs rs6; 54 | 55 | uint16_t crc_tab[256]; 56 | 57 | uint8_t *data; 58 | uint32_t size; 59 | uint32_t top_of_dir; 60 | 61 | uint8_t rs_buffer[36]; // Last decoded block 62 | uint32_t rs_buffer_addr; 63 | uint16_t rs_status; 64 | }; 65 | 66 | struct buram_stat 67 | { 68 | uint8_t format[12]; 69 | uint8_t volume[12]; 70 | uint16_t num_free_blocks; 71 | uint16_t num_files; 72 | }; 73 | 74 | struct buram_file 75 | { 76 | uint8_t filename[11]; 77 | uint8_t use_ecc; 78 | uint16_t start_block; 79 | uint16_t size; 80 | }; 81 | 82 | //===================================================================== 83 | // Initialize Reed-Solomon context 84 | //--------------------------------------------------------------------- 85 | static void buram_rs_init(struct buram_rs *rs, uint8_t poly, uint8_t symsize, const uint8_t* polygen) 86 | { 87 | rs->polygen = polygen; 88 | 89 | rs->mm = symsize; 90 | rs->nn = (1 << symsize) - 1; 91 | 92 | rs->index_of[0] = 0; 93 | rs->alpha_to[0] = 0; 94 | 95 | // Generate the Galois field tables 96 | for(int i = 1, sr = 1; i <= rs->nn; i++) 97 | { 98 | rs->index_of[sr] = i; 99 | rs->alpha_to[i] = sr; 100 | sr <<= 1; 101 | if(sr & (1 << symsize)) 102 | sr ^= poly; 103 | sr &= rs->nn; 104 | } 105 | 106 | } 107 | 108 | //===================================================================== 109 | // Add with modulo 110 | //--------------------------------------------------------------------- 111 | static uint8_t buram_rs_add_mod(struct buram_rs *rs, int i, uint16_t d) 112 | { 113 | d += rs->polygen[i]; 114 | while(d >= rs->nn) 115 | d -= rs->nn; 116 | return rs->alpha_to[d + 1]; 117 | } 118 | 119 | //===================================================================== 120 | // Encode Reed-Solomon block 121 | //--------------------------------------------------------------------- 122 | static void buram_rs_encode(struct buram_rs *rs, uint8_t *data) 123 | { 124 | uint8_t *p1 = data + 6; 125 | uint8_t *p2 = data + 7; 126 | *p1 = 0; 127 | *p2 = 0; 128 | 129 | for(int i = 0; i < 6; i++) 130 | { 131 | uint8_t d = data[i]; 132 | if(d) 133 | { 134 | d = rs->index_of[d] - 1; 135 | *p1 ^= buram_rs_add_mod(rs, i, d); 136 | *p2 ^= buram_rs_add_mod(rs, i+6, d); 137 | } 138 | } 139 | } 140 | 141 | //===================================================================== 142 | // Decode Reed-Solomon block and perform error correction 143 | //--------------------------------------------------------------------- 144 | static uint8_t buram_rs_decode(struct buram_rs *rs, uint8_t *data) 145 | { 146 | uint8_t error_mask = 0; 147 | uint8_t error_loc = 0; 148 | 149 | // calculate error location (syndrome) 150 | for(int i = 0; i < 8; i++) 151 | { 152 | uint16_t d = data[i]; 153 | error_mask ^= d; 154 | if(d) 155 | { 156 | d = rs->index_of[d] + 6 - i; 157 | while(d >= rs->nn) 158 | d -= rs->nn; 159 | error_loc ^= rs->alpha_to[d + 1]; 160 | } 161 | } 162 | 163 | // correct a single error 164 | if(error_mask) 165 | { 166 | uint16_t d = rs->nn + rs->index_of[error_loc] - rs->index_of[error_mask]; 167 | while(d >= rs->nn) 168 | d -= rs->nn; 169 | if(d < 8) 170 | { 171 | data[7 - d] ^= error_mask; 172 | return 1<<1; // error found and corrected 173 | } 174 | return 1<<2; // error found but not corrected 175 | } 176 | return 0; // no error found 177 | } 178 | 179 | 180 | //===================================================================== 181 | // Generate the CRC table using polynomial for CRC-16 CCITT. 182 | //--------------------------------------------------------------------- 183 | static void buram_crc_init(struct buram *brm) 184 | { 185 | for(int i = 0; i < 256; i++) 186 | { 187 | uint16_t d = i << 8; 188 | for(int j = 0; j < 8; j++) 189 | d = (d << 1) ^ ((d & 0x8000) ? 0x1021 : 0); 190 | brm->crc_tab[i] = d; 191 | } 192 | } 193 | 194 | //===================================================================== 195 | // Calculate CCITT checksum over 32 bytes 196 | //--------------------------------------------------------------------- 197 | static uint16_t buram_crc(struct buram *brm, uint8_t* msg) 198 | { 199 | uint16_t d = 0; 200 | for(int i = 0; i < 32; i++) 201 | d = (d << 8) ^ brm->crc_tab[msg[i] ^ (d >> 8)]; 202 | return d; 203 | } 204 | 205 | //===================================================================== 206 | // Interleave 36 bytes of data into 48 bytes 207 | //--------------------------------------------------------------------- 208 | static void buram_interleave_data(uint8_t* in, uint8_t* out) 209 | { 210 | uint16_t sr; 211 | for(int i = 0; i < 12; i++) 212 | { 213 | uint16_t sr = *in++; // ---- ---- aaaa aaaa 214 | *out++ = sr; // **** **-- 215 | sr <<= 8; 216 | sr |= *in++; // aaaa aaaa bbbb bbbb 217 | *out++ = sr >> 2; // ** **** -- 218 | sr <<= 8; 219 | sr |= *in++; // bbbb bbbb aaaa aaaa 220 | *out++ = sr >> 4; // **** **-- 221 | *out++ = sr << 2; // ** **** 222 | } 223 | } 224 | 225 | //===================================================================== 226 | // Deinterleave 36 bytes of data from 48 bytes 227 | //--------------------------------------------------------------------- 228 | static void buram_deinterleave_data(uint8_t* in, uint8_t* out) 229 | { 230 | uint16_t sr; 231 | for(int i = 0; i < 12; i++) 232 | { 233 | uint16_t sr = *in++; // ---- ---- AAAA AAaa 234 | sr <<= 6; // --AA AAAA aa-- ---- 235 | sr = (sr & 0xff00) | *in++; // --AA AAAA BBBB BBbb 236 | *out++ = sr >> 6; // ** **** ** 237 | sr <<= 6; // AABB BBBB bb-- ---- 238 | sr = (sr & 0xff00) | *in++; // AABB BBBB CCCC CCcc 239 | *out++ = sr >> 4; // **** **** 240 | sr <<= 6; // BBCC CCCC 241 | sr = (sr & 0xff00) | *in++; // BBCC CCCC DDDD DDdd 242 | *out++ = sr >> 2; // ** **** ** 243 | } 244 | } 245 | 246 | //===================================================================== 247 | // Interleave the 6-bit RS code 248 | //--------------------------------------------------------------------- 249 | // The 6-bit code consists of the most significant 6 bits of each byte in 250 | // the message. ECC is done on a per-subblock level. 251 | // The order of the bytes in the complete 64-byte block is as follows. 252 | // A* is the first subblock, B* is the second subblock and so on. 253 | // The number indicates the order of the byte within that block. 254 | // 255 | // This diagram shows the assignments of blocks and bytes in the packet. 256 | // 00 01 02 03 04 05 06 07 - BYTE number 257 | // ------------------------ 258 | // 00 | A0 B0 C0 D0 E0 F0 G0 H0 --- Data bits 259 | // 08 | D5 A1 B1 C1 D1 E1 F1 G1 260 | // 10 | H1 E5 A2 B2 C2 D2 E2 F2 261 | // 18 | G2 H2 F5 A3 B3 C3 D3 E3 262 | // 20 | F3 G3 H3 G5 A4 B4 C4 D4 263 | // 28 | E4 F4 G4 H4 H5 A5 B5 C5 264 | // ---+------------------------ 265 | // 30 | A6 B6 C6 D6 E6 F6 G6 H6 --- Parity bits 266 | // 38 | A7 B7 C7 D7 E7 F7 G7 H7 267 | static void buram_interleave_rs6(int block, uint8_t *in, uint8_t *out) 268 | { 269 | static const uint8_t last_data_byte[8] = {0x2d,0x2e,0x2f,0x08,0x11,0x1a,0x23,0x2c}; 270 | for(int i=0; i<5; i++) 271 | out[block + i*9] = in[i] << 2; 272 | out[last_data_byte[block]] = in[5] << 2; 273 | out[0x30 + block] = in[6] << 2; 274 | out[0x38 + block] = in[7] << 2; 275 | } 276 | 277 | //===================================================================== 278 | // Deinterleave the 6-bit RS code so that parity bits are at the bottom 279 | //--------------------------------------------------------------------- 280 | static void buram_deinterleave_rs6(int block, uint8_t *in, uint8_t *out) 281 | { 282 | static const uint8_t last_data_byte[8] = {0x2d,0x2e,0x2f,0x08,0x11,0x1a,0x23,0x2c}; 283 | for(int i=0; i<5; i++) 284 | out[i] = in[block + i*9] >> 2; 285 | out[5] = in[last_data_byte[block]] >> 2; 286 | out[6] = in[0x30 + block] >> 2; 287 | out[7] = in[0x38 + block] >> 2; 288 | } 289 | 290 | //===================================================================== 291 | // Interleave the 8-bit RS code 292 | //--------------------------------------------------------------------- 293 | // ECC is done on a per-subblock level. Each block consists of 8 subblocks. 294 | // 295 | // The first byte of the subblock consists of bit 7 of the corresponding 296 | // bytes in the packet. The second byte consists of bit 6, and so on. 297 | // This is done so that bytes 6 and 7 of the subblock (corresponding to 298 | // bits 6 and 7 in the packet) will contain the parity bits. 299 | // 300 | // This diagram shows the assignment of bits within one packet 301 | // 07 06 05 04 03 02 | 01 00 - BIT number 302 | // -------------------+------ 303 | // 38 | A0 A1 A2 A3 A4 A5 | A6 A7 --- Parity bits 304 | // 30 | B0 B1 B2 B3 B4 B5 | B6 B7 305 | // 28 | C0 C1 C2 C3 C4 C5 | C6 C7 306 | // 20 | D0 D1 D2 D3 D4 D5 | D6 D7 307 | // 18 | E0 E1 E2 E3 E4 E5 | E6 E7 308 | // 10 | F0 F1 F2 F3 F4 F5 | F6 F7 309 | // 08 | G0 G1 G2 G3 G4 G5 | G6 G7 310 | // 00 | H0 H1 H2 H3 H4 H5 | H6 H7 311 | // | 312 | // - Data bits 313 | static void buram_interleave_rs8(int block, uint8_t *in, uint8_t *out) 314 | { 315 | for(int i=0; i<8; i++) 316 | { 317 | uint8_t tmp = in[i]; 318 | for(int j=0; j<8; j++) 319 | { 320 | out[block + j*8] = (out[block + j*8] << 1) | (tmp >> 7); 321 | tmp <<= 1; 322 | } 323 | } 324 | } 325 | 326 | //===================================================================== 327 | // Deinterleave the 8-bit RS code so that parity bits are at the bottom 328 | //--------------------------------------------------------------------- 329 | static void buram_deinterleave_rs8(int block, uint8_t *in, uint8_t *out) 330 | { 331 | for(int i=0; i<8; i++) 332 | { 333 | uint8_t tmp = in[block + i*8]; 334 | for(int j=0; j<8; j++) 335 | { 336 | out[j] = (out[j] << 1) | (tmp >> 7); 337 | tmp <<= 1; 338 | } 339 | } 340 | } 341 | 342 | //===================================================================== 343 | // Encode an ecc block 344 | //--------------------------------------------------------------------- 345 | static void buram_encode(struct buram *brm, uint8_t *buf, uint8_t *out) 346 | { 347 | uint8_t block_buf[8]; 348 | 349 | // Calculate CRC 350 | uint16_t crc = buram_crc(brm, buf + 2); 351 | buf[0] = crc >> 8; 352 | buf[1] = crc; 353 | buf[34] = ~buf[0]; 354 | buf[35] = ~buf[1]; 355 | 356 | // Interleave data 357 | buram_interleave_data(buf, out); 358 | // Encode with the 6-bit RS code 359 | for(int block = 0; block < 8; block++) 360 | { 361 | buram_deinterleave_rs6(block, out, block_buf); 362 | buram_rs_encode(&brm->rs6, block_buf); 363 | buram_interleave_rs6(block, block_buf, out); 364 | } 365 | // Encode with the 8-bit RS code 366 | for(int block = 0; block < 8; block++) 367 | { 368 | buram_deinterleave_rs8(block, out, block_buf); 369 | buram_rs_encode(&brm->rs8, block_buf); 370 | buram_interleave_rs8(block, block_buf, out); 371 | } 372 | } 373 | 374 | //===================================================================== 375 | // Decode an ecc block 376 | //--------------------------------------------------------------------- 377 | static int buram_decode(struct buram *brm, uint8_t *in, uint8_t *buf) 378 | { 379 | uint8_t block_buf[8]; 380 | uint16_t flags = 0; 381 | 382 | // Decode the 8-bit RS code 383 | for(int block = 0; block < 8; block++) 384 | { 385 | buram_deinterleave_rs8(block, in, block_buf); 386 | flags |= buram_rs_decode(&brm->rs8, block_buf); 387 | buram_interleave_rs8(block, block_buf, in); 388 | } 389 | // Decode the 6-bit RS code 390 | for(int block = 0; block < 8; block++) 391 | { 392 | buram_deinterleave_rs6(block, in, block_buf); 393 | flags |= buram_rs_decode(&brm->rs6, block_buf); 394 | buram_interleave_rs6(block, block_buf, in); 395 | } 396 | // Deinterleave data 397 | buram_deinterleave_data(in, buf); 398 | // Calculate CRC 399 | uint16_t crc = buram_crc(brm, buf + 2); 400 | uint16_t check_crc1 = (buf[0] << 8) | buf[1]; 401 | uint16_t check_crc2 = ~((buf[34] << 8) | buf[35]); 402 | 403 | if(crc != check_crc1 && crc != check_crc2) 404 | flags |= 8; 405 | return flags; 406 | } 407 | 408 | //===================================================================== 409 | // Get data from SRAM 410 | //--------------------------------------------------------------------- 411 | static int buram_get_data(struct buram *brm, uint32_t address, uint32_t size, uint8_t *buffer) 412 | { 413 | if(brm->size && brm->data) 414 | while(size--) 415 | *buffer++ = brm->data[(address++) % brm->size]; 416 | return size; 417 | } 418 | 419 | //===================================================================== 420 | // Get data from SRAM 421 | //--------------------------------------------------------------------- 422 | static int buram_set_data(struct buram *brm, uint32_t address, uint32_t size, uint8_t *buffer) 423 | { 424 | if(brm->size && brm->data) 425 | while(size--) 426 | brm->data[(address++) % brm->size] = *buffer++; 427 | return size; 428 | } 429 | 430 | //===================================================================== 431 | // Decode to the temp buffer 432 | //--------------------------------------------------------------------- 433 | static uint8_t* buram_decode_buffer(struct buram *brm, uint32_t address) 434 | { 435 | uint32_t buffer_address = address & -0x40; 436 | if(brm->rs_buffer_addr != buffer_address) 437 | { 438 | uint8_t sram_buf[64]; 439 | buram_get_data(brm, buffer_address, 64, sram_buf); 440 | brm->rs_status = buram_decode(brm, sram_buf, brm->rs_buffer); 441 | brm->rs_buffer_addr = buffer_address; 442 | } 443 | return brm->rs_buffer + 2 + ((address ^ buffer_address) >> 1); 444 | } 445 | 446 | //===================================================================== 447 | // Encode the temp buffer 448 | //--------------------------------------------------------------------- 449 | static void buram_encode_buffer(struct buram *brm, uint32_t address, uint8_t *data, uint32_t size) 450 | { 451 | uint8_t sram_buf[64]; 452 | uint32_t buffer_address = address & -0x40; 453 | if(size < 32 && brm->rs_buffer_addr != buffer_address) 454 | { 455 | buram_get_data(brm, buffer_address, 64, sram_buf); 456 | brm->rs_status = buram_decode(brm, sram_buf, brm->rs_buffer); 457 | } 458 | brm->rs_buffer_addr = buffer_address; 459 | uint8_t *write_ptr = brm->rs_buffer + 2 + ((address ^ buffer_address) >> 1); 460 | while(size--) 461 | *write_ptr++ = *data++; 462 | 463 | buram_encode(brm, brm->rs_buffer, sram_buf); 464 | buram_set_data(brm, buffer_address, 64, sram_buf); 465 | } 466 | 467 | //===================================================================== 468 | // Find a number that occurs at least 3 times 469 | //--------------------------------------------------------------------- 470 | static int16_t buram_read_repeat_code(uint8_t* data, int count) 471 | { 472 | if(data) 473 | { 474 | uint16_t buf[count]; 475 | for(int i = 0; i < count; i++) 476 | { 477 | buf[i] = (data[0] << 8) | (data[1]); 478 | data += 2; 479 | } 480 | for(int i = 0; i < (count/2); i++) 481 | { 482 | int repeats = 0; 483 | for(int j=i+1; j (count/2)) 489 | return buf[i]; 490 | } 491 | } 492 | return -1; 493 | } 494 | 495 | //===================================================================== 496 | // Write repeating code 497 | //--------------------------------------------------------------------- 498 | static void buram_write_repeat_code(uint8_t* data, int count, uint16_t val) 499 | { 500 | while(count--) 501 | { 502 | *data++ = val >> 8; 503 | *data++ = val; 504 | } 505 | } 506 | 507 | //===================================================================== 508 | // Get the number of free blocks 509 | //--------------------------------------------------------------------- 510 | static int16_t buram_get_free_blocks(struct buram *brm) 511 | { 512 | if(brm->size >= 64) 513 | return buram_read_repeat_code(brm->data + brm->top_of_dir + 0x10, 4); 514 | else 515 | return -1; 516 | } 517 | 518 | //===================================================================== 519 | // Get the number of files in the directory 520 | //--------------------------------------------------------------------- 521 | static int16_t buram_get_num_files(struct buram *brm) 522 | { 523 | if(brm->size >= 64) 524 | return buram_read_repeat_code(brm->data + brm->top_of_dir + 0x18, 4); 525 | else 526 | return -1; 527 | } 528 | 529 | //===================================================================== 530 | // Update the free file and block counters 531 | //--------------------------------------------------------------------- 532 | static int16_t buram_update_counters(struct buram *brm, int delete, int16_t block_delta) 533 | { 534 | int16_t num_files = buram_get_num_files(brm); 535 | int16_t free_blocks = buram_get_free_blocks(brm); 536 | if(delete) 537 | { 538 | num_files--; 539 | if(num_files & 1) 540 | free_blocks++; 541 | free_blocks += block_delta; 542 | } 543 | else 544 | { 545 | if(num_files & 1) 546 | free_blocks--; 547 | num_files++; 548 | free_blocks -= block_delta; 549 | } 550 | uint8_t *ptr = brm->data + brm->top_of_dir + 0x10; 551 | buram_write_repeat_code(brm->data + brm->top_of_dir + 0x10, 4, free_blocks); 552 | buram_write_repeat_code(brm->data + brm->top_of_dir + 0x18, 4, num_files); 553 | } 554 | 555 | //===================================================================== 556 | // Read a file descriptor 557 | //--------------------------------------------------------------------- 558 | static void buram_read_file_desc(uint8_t *buf, struct buram_file *file) 559 | { 560 | for(int i = 0; i < 11; i++) 561 | file->filename[i] = buf[i]; 562 | file->use_ecc = buf[11]; 563 | file->start_block = (buf[12] << 8) | buf[13]; 564 | file->size = (buf[14] << 8) | buf[15]; 565 | } 566 | 567 | //===================================================================== 568 | // Write a file descriptor 569 | //--------------------------------------------------------------------- 570 | static void buram_write_file_desc(struct buram_file *file, uint8_t *buf) 571 | { 572 | for(int i = 0; i < 11; i++) 573 | buf[i] = file->filename[i]; 574 | buf[11] = file->use_ecc; 575 | buf[12] = file->start_block >> 8; 576 | buf[13] = file->start_block; 577 | buf[14] = file->size >> 8; 578 | buf[15] = file->size; 579 | } 580 | 581 | //===================================================================== 582 | // Initialize backup RAM context 583 | //--------------------------------------------------------------------- 584 | int buram_init(struct buram* brm, uint8_t *data, uint32_t size, uint8_t **format_str) 585 | { 586 | static const uint8_t polygen_rs8[12] = {87, 166, 113, 75, 198, 25, 167, 114, 76, 199, 26, 1}; 587 | static const uint8_t polygen_rs6[12] = {20, 58, 56, 18, 26, 6, 59, 57, 19, 27, 7, 1}; 588 | buram_rs_init(&brm->rs8, 0x1D, 8, polygen_rs8); 589 | buram_rs_init(&brm->rs6, 3, 6, polygen_rs6); 590 | buram_crc_init(brm); 591 | 592 | brm->data = data; 593 | brm->size = 0; 594 | if(data) 595 | { 596 | // Read format 597 | brm->size = size; 598 | brm->top_of_dir = size - 0x40; 599 | // The original MCD BRAM code also checks the format here. We omit that check... 600 | if(format_str) 601 | { 602 | memcpy(*format_str, brm->data + brm->top_of_dir + 0x20, 11); 603 | *format_str[11] = 0; 604 | } 605 | return 0; 606 | } 607 | return 1; 608 | } 609 | 610 | //===================================================================== 611 | // Return format, number of free blocks and number of files 612 | //--------------------------------------------------------------------- 613 | int buram_stat(struct buram* brm, struct buram_stat* stat) 614 | { 615 | int return_val = 1; 616 | int16_t free_blocks = -1; 617 | int16_t num_files = -1; 618 | if(brm->size) 619 | { 620 | int16_t free_blocks = buram_get_free_blocks(brm); 621 | int16_t num_files = buram_get_num_files(brm); 622 | if(free_blocks >= 0 && num_files >= 0) 623 | { 624 | // Get the format... 625 | if(stat) 626 | { 627 | memcpy(stat->format, brm->data + brm->top_of_dir + 0x20, 11); 628 | stat->format[11] = 0; 629 | memcpy(stat->volume, brm->data + brm->top_of_dir, 11); 630 | stat->volume[11] = 0; 631 | stat->num_free_blocks = free_blocks; 632 | stat->num_files = num_files; 633 | } 634 | return_val = 0; 635 | } 636 | } 637 | return return_val; 638 | } 639 | 640 | //===================================================================== 641 | // Search for a file and return file information 642 | //--------------------------------------------------------------------- 643 | int buram_search(struct buram *brm, struct buram_file *file, char *filename) 644 | { 645 | int files_left = buram_get_num_files(brm); 646 | uint32_t pos = brm->top_of_dir - 0x20; 647 | while(files_left > 0) 648 | { 649 | uint8_t *buf = buram_decode_buffer(brm, pos); 650 | if(!memcmp(filename, buf, 11)) 651 | { 652 | buram_read_file_desc(buf, file); 653 | return 0; 654 | } 655 | pos -= 0x20; 656 | files_left--; 657 | } 658 | return -1; 659 | } 660 | 661 | //===================================================================== 662 | // Delete a file 663 | //--------------------------------------------------------------------- 664 | int buram_delete(struct buram *brm, char *filename) 665 | { 666 | struct buram_file file; 667 | int files_left = buram_get_num_files(brm); 668 | uint32_t pos = brm->top_of_dir - 0x20; 669 | while(files_left > 0) 670 | { 671 | uint8_t *buf = buram_decode_buffer(brm, pos); 672 | if(!memcmp(filename, buf, 11)) 673 | { 674 | buram_read_file_desc(buf, &file); 675 | uint16_t shift_size = file.size; 676 | while(files_left > 1) 677 | { 678 | uint8_t write_buf[16]; 679 | buf = buram_decode_buffer(brm, pos - 0x20); 680 | buram_read_file_desc(buf, &file); 681 | file.start_block -= shift_size; 682 | buram_write_file_desc(&file, write_buf); 683 | buram_encode_buffer(brm, pos, write_buf, 0x10); 684 | memmove(brm->data + file.start_block * 64, 685 | brm->data + (file.start_block + shift_size) * 64, 686 | file.size * 64); 687 | pos -= 0x20; 688 | files_left--; 689 | } 690 | buram_update_counters(brm, 1, shift_size); 691 | return 0; 692 | } 693 | pos -= 0x20; 694 | files_left--; 695 | } 696 | return -1; // No files deleted. 697 | } 698 | 699 | //===================================================================== 700 | // Format 701 | //--------------------------------------------------------------------- 702 | int buram_format(struct buram *brm, uint8_t *volume) 703 | { 704 | // The BIOS always sets this to spaces, I think this might be supposed to be the volume name 705 | // though. 706 | static const uint8_t initial_volume[11] = "___________"; 707 | // Perhaps this is supposed to indicate the block size, however the BIOS does not use it. 708 | static const uint8_t initial_unknown[5] = {0x00,0x00,0x00,0x00,0x40}; 709 | // Format and media type 710 | static const uint8_t initial_format[32] = "SEGA_CD_ROM\x00\x01\x00\x00\x00RAM_CARTRIDGE___"; 711 | if(brm->data) 712 | { 713 | // Last block contains the footer, first block is reserved. The remaining block is reserved 714 | // for the first file in the directory, hence the # of free blocks is the total minus 3. 715 | uint16_t free_blocks = (brm->size / 64) - 3; 716 | memcpy(brm->data + brm->top_of_dir, volume ? volume : initial_volume, 11); 717 | memcpy(brm->data + brm->top_of_dir + 0x0b, initial_unknown, 5); 718 | buram_write_repeat_code(brm->data + brm->top_of_dir + 0x10, 4, free_blocks); 719 | buram_write_repeat_code(brm->data + brm->top_of_dir + 0x18, 4, 0); 720 | memcpy(brm->data + brm->top_of_dir + 0x20, initial_format, 32); 721 | return 0; 722 | } 723 | return -1; 724 | } 725 | 726 | //===================================================================== 727 | // Read a file 728 | //--------------------------------------------------------------------- 729 | int buram_read(struct buram *brm, uint8_t *output, char *filename, long pos, long size) 730 | { 731 | struct buram_file file; 732 | int status = buram_search(brm, &file, filename); 733 | if(!status) 734 | { 735 | if(size == 0) 736 | size = file.size; 737 | if(pos + size > file.size) 738 | size = file.size - pos; 739 | 740 | pos += file.start_block; 741 | if((pos + size) * 64 > brm->size) 742 | return -1; 743 | 744 | if(file.use_ecc) 745 | { 746 | while(size--) 747 | { 748 | uint8_t *buffer = buram_decode_buffer(brm, pos * 64); 749 | status |= brm->rs_status & 12; 750 | memcpy(output, buffer, 32); 751 | output += 32; 752 | pos++; 753 | } 754 | } 755 | else if(size) 756 | { 757 | memcpy(output, brm->data + pos * 64, size * 64); 758 | } 759 | } 760 | return status; 761 | } 762 | 763 | //===================================================================== 764 | // Write a file 765 | //--------------------------------------------------------------------- 766 | int buram_write(struct buram *brm, struct buram_file *file, uint8_t *input) 767 | { 768 | // Doing buram_search before buram_delete seems pointless, but it's done 769 | // so we can check if we have enough blocks before deleting the existing 770 | // file. 771 | int free_blocks = buram_get_free_blocks(brm); 772 | 773 | struct buram_file last_file; 774 | int result = buram_search(brm, &last_file, file->filename); 775 | if(!result) 776 | { 777 | if(free_blocks + last_file.size >= file->size) 778 | buram_delete(brm, last_file.filename); 779 | else 780 | return -1; 781 | } 782 | else 783 | { 784 | if(free_blocks < file->size) 785 | return -1; 786 | } 787 | 788 | int num_files = buram_get_num_files(brm); 789 | uint32_t pos = brm->top_of_dir - 0x20 - (num_files * 0x20); 790 | file->start_block = 1; // we start writing from block 1 791 | 792 | if(num_files) 793 | { 794 | uint8_t *buf = buram_decode_buffer(brm, pos + 0x20); 795 | buram_read_file_desc(buf, &last_file); 796 | file->start_block = last_file.start_block + last_file.size; 797 | } 798 | 799 | uint8_t write_buf[16]; 800 | buram_write_file_desc(file, write_buf); 801 | buram_encode_buffer(brm, pos, write_buf, 0x10); 802 | 803 | if(file->use_ecc) 804 | { 805 | for(int i = 0; i < file->size; i++) 806 | buram_encode_buffer(brm, (file->start_block + i) * 64, input + i * 32, 32); 807 | } 808 | else 809 | { 810 | memcpy(brm->data + file->start_block * 64, input, file->size * 64); 811 | } 812 | 813 | buram_update_counters(brm, 0, file->size); 814 | return 0; 815 | } 816 | 817 | //===================================================================== 818 | // List directory. Supports wildcard patterns using '*'. 819 | //--------------------------------------------------------------------- 820 | int buram_dir(struct buram *brm, struct buram_file *file, char *pattern, long skip, long size) 821 | { 822 | int size_left = size; 823 | int files_left = buram_get_num_files(brm); 824 | uint32_t pos = brm->top_of_dir - 0x20; 825 | while(files_left > 0) 826 | { 827 | uint8_t *buf = buram_decode_buffer(brm, pos); 828 | int match = 0; 829 | while(match < 11) 830 | { 831 | if(pattern[match] == '*') 832 | match = 11; 833 | else if(pattern[match] == buf[match]) 834 | match++; 835 | else 836 | break; 837 | } 838 | if(match == 11) 839 | { 840 | if(skip) 841 | { 842 | skip--; 843 | } 844 | else if(size_left) 845 | { 846 | for(int i = 0; i < 11; i++) 847 | file->filename[i] = buf[i]; 848 | file->use_ecc = buf[11]; 849 | file->start_block = (buf[12] << 8) | buf[13]; 850 | file->size = (buf[14] << 8) | buf[15]; 851 | file++; 852 | size_left--; 853 | } 854 | } 855 | pos -= 0x20; 856 | files_left--; 857 | } 858 | return size - size_left; 859 | } 860 | 861 | //===================================================================== 862 | // Convert filename from ASCII to BURAM format 863 | //--------------------------------------------------------------------- 864 | void buram_ascii_to_text(char* in, uint8_t* out, int size) 865 | { 866 | for(int i = 0; i < size; i++) 867 | { 868 | char c = *in; 869 | if(c && c != '.') 870 | in++; 871 | if((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) 872 | *out++ = c; 873 | else if(c >= 'a' && c <= 'z') 874 | *out++ = c - 'a' + 'A'; 875 | else if(c == '*' || c == '?') // Can use '?' to prevent automatic globbing 876 | *out++ = '*'; 877 | else 878 | *out++ = '_'; 879 | } 880 | *out++ = 0; 881 | } 882 | 883 | //===================================================================== 884 | // Convert filename from BURAM format to ASCII 885 | //--------------------------------------------------------------------- 886 | void buram_text_to_ascii(char* in, uint8_t* out, int size) 887 | { 888 | // Convert (actually since it's ASCII nothing needs to be done here apart from sanity checking) 889 | for(int i = 0; i < size; i++) 890 | { 891 | char c = *in; 892 | if(c) 893 | in++; 894 | if((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '*') 895 | *out++ = c; 896 | else 897 | *out++ = '_'; 898 | } 899 | *out = 0; 900 | // Remove trailing _ 901 | for(int i = size - 1; i > 0; i--) 902 | { 903 | if(*(--out) == '_') 904 | *out = 0; 905 | else 906 | break; 907 | } 908 | } 909 | 910 | //===================================================================== 911 | // Open sram image 912 | //--------------------------------------------------------------------- 913 | int open(struct buram *brm, const char *file_name, long *file_size, int *file_init, char **file_data) 914 | { 915 | int status = -1; 916 | if(*file_init) 917 | { 918 | FILE *file = NULL; 919 | uint8_t *data; 920 | 921 | if(file_name) 922 | file = fopen(file_name, "rb"); 923 | 924 | if(file) 925 | { 926 | fseek(file,0,SEEK_END); 927 | *file_size = ftell(file); 928 | rewind(file); 929 | *file_data = malloc(*file_size); 930 | if(*file_data) 931 | { 932 | fread(*file_data, 1, *file_size, file); 933 | fclose(file); 934 | *file_init = buram_init(brm, *file_data, *file_size, NULL); 935 | DEBUG("buram_init() returned %d - loading from '%s'\n", *file_init, file_name); 936 | status = 0; 937 | } 938 | } 939 | else 940 | { 941 | *file_data = calloc(1, *file_size); 942 | if(*file_data) 943 | { 944 | *file_init = buram_init(brm, *file_data, *file_size, NULL); 945 | DEBUG("buram_init() returned %d - initialized empty file\n", *file_init); 946 | status = 1; 947 | } 948 | } 949 | } 950 | return status; 951 | } 952 | 953 | //===================================================================== 954 | // Save sram image 955 | //--------------------------------------------------------------------- 956 | int save(struct buram *brm, const char *file_name, long file_size, int file_init, char *file_data) 957 | { 958 | int status = -1; 959 | if(!file_init && file_data) 960 | { 961 | FILE *fd; 962 | uint8_t *data; 963 | 964 | DEBUG("saving to '%s'...\n", file_name); 965 | fd = fopen(file_name, "wb"); // Temp. 966 | if(fd) 967 | { 968 | fwrite(file_data, file_size, 1, fd); 969 | fclose(fd); 970 | status = 0; 971 | } 972 | else 973 | { 974 | fprintf(stderr, "failed to open file '%s' for writing\n", file_name); 975 | } 976 | } 977 | return status; 978 | } 979 | 980 | //===================================================================== 981 | // Filesystem statistics 982 | //--------------------------------------------------------------------- 983 | int stats(struct buram *brm, int verbose) 984 | { 985 | struct buram_stat stat; 986 | int status = buram_stat(brm, &stat); 987 | 988 | DEBUG("buram_stat() returned %d\n", status); 989 | if(verbose) 990 | { 991 | char volume[12] = ""; 992 | char format[12] = ""; 993 | char media_id[17] = ""; 994 | 995 | buram_text_to_ascii(stat.format, format, 11); 996 | buram_text_to_ascii(stat.volume, volume, 11); 997 | 998 | if(brm->data && brm->size > 16) 999 | buram_text_to_ascii(brm->data + brm->size - 16, media_id, 16); 1000 | 1001 | printf("volume: %s\n",volume); 1002 | printf("format: %s\n",format); 1003 | printf("media_id: %s\n",media_id); 1004 | } 1005 | printf("\nfiles: %d, free blocks: %d\n", stat.num_files, stat.num_free_blocks); 1006 | return status; 1007 | } 1008 | 1009 | 1010 | //===================================================================== 1011 | // Run callback function for files matching a pattern 1012 | //--------------------------------------------------------------------- 1013 | int list(struct buram *brm, char* pattern, int fail_if_empty, int verbose, 1014 | int (*callback)(struct buram *brm, struct buram_file *file, int verbose, void *param), void *param) 1015 | { 1016 | static const int buf_size = 4; 1017 | struct buram_file files[buf_size]; 1018 | int start_pos = 0; 1019 | int num_files; 1020 | int status = 0; 1021 | do 1022 | { 1023 | num_files = buram_dir(brm, files, pattern, start_pos, buf_size); 1024 | if(fail_if_empty && !start_pos && !num_files) 1025 | { 1026 | fprintf(stderr, "no files found\n"); 1027 | status |= -1; 1028 | } 1029 | for(int i = 0; i < num_files; i++) 1030 | { 1031 | status |= callback(brm, &files[i], verbose, param); 1032 | } 1033 | start_pos += buf_size; 1034 | } 1035 | while(num_files == buf_size); 1036 | return status; 1037 | } 1038 | 1039 | // Callback function to print the directory listing 1040 | int cb_list(struct buram *brm, struct buram_file *file, int verbose, void *param) 1041 | { 1042 | char ascii_buf[12]; 1043 | buram_text_to_ascii(file->filename, ascii_buf, 11); 1044 | if(verbose) 1045 | printf("%c %5d %5d %s\n", file->use_ecc ? 'P' : ' ', 1046 | file->start_block, file->size, ascii_buf); 1047 | else 1048 | printf("%s\n", ascii_buf); 1049 | return 0; 1050 | } 1051 | 1052 | // Callback function to extract files 1053 | int cb_extract(struct buram *brm, struct buram_file *file, int verbose, void *param) 1054 | { 1055 | int status = -1; 1056 | char ascii_buf[12]; 1057 | buram_text_to_ascii(file->filename, ascii_buf, 11); 1058 | if(verbose) 1059 | printf("%s\n", ascii_buf); 1060 | 1061 | uint32_t block_size = file->use_ecc ? 32 : 64; 1062 | uint8_t *buffer = malloc(block_size * file->size); 1063 | if(buffer) 1064 | { 1065 | char filename[PATH_MAX]; 1066 | snprintf(filename, PATH_MAX, "%s.bin", ascii_buf); 1067 | int read_status = buram_read(brm, buffer, file->filename, 0, 0); 1068 | if(!read_status) 1069 | { 1070 | printf("opening file '%s' for writing ...\n", filename); 1071 | FILE *fd = fopen(filename, "wb"); 1072 | if(fd) 1073 | { 1074 | fwrite(buffer, block_size, file->size, fd); 1075 | fclose(fd); 1076 | status = 0; 1077 | } 1078 | else 1079 | { 1080 | fprintf(stderr, "failed to open file '%s' for writing\n", filename); 1081 | } 1082 | free(buffer); 1083 | } 1084 | else 1085 | { 1086 | fprintf(stderr, "buram_read() failed\n"); 1087 | } 1088 | } 1089 | else 1090 | { 1091 | fprintf(stderr, "failed to allocate buffer of size %d\n", block_size * file->size); 1092 | } 1093 | return status; 1094 | } 1095 | 1096 | // Callback function to delete files 1097 | int cb_delete(struct buram *brm, struct buram_file *file, int verbose, void *param) 1098 | { 1099 | char ascii_buf[12]; 1100 | buram_text_to_ascii(file->filename, ascii_buf, 11); 1101 | if(verbose) 1102 | printf("%s\n", ascii_buf); 1103 | return buram_delete(brm, file->filename); 1104 | } 1105 | 1106 | //===================================================================== 1107 | // Add file 1108 | //--------------------------------------------------------------------- 1109 | int add(struct buram *brm, char *path, uint8_t *name, int verbose, int protect) 1110 | { 1111 | FILE *fd; 1112 | long size; 1113 | uint8_t *data; 1114 | int status = -1; 1115 | 1116 | fd = fopen(path, "rb"); 1117 | if(fd) 1118 | { 1119 | fseek(fd,0,SEEK_END); 1120 | size = ftell(fd); 1121 | rewind(fd); 1122 | data = calloc(1, size + 63); 1123 | if(data) 1124 | { 1125 | int block_size = protect ? 32 : 64; 1126 | struct buram_file file; 1127 | fread(data, 1, size, fd); 1128 | memcpy(&file.filename, name, 11); 1129 | file.use_ecc = protect ? 0xff : 0; 1130 | file.size = ((size + block_size - 1) & -block_size) / block_size; 1131 | file.start_block = 0; 1132 | status = buram_write(brm, &file, data); 1133 | if (status) 1134 | { 1135 | fprintf(stderr, "%s: buram_write() failed. (%d blocks required, %d available)\n", path, file.size, buram_get_free_blocks(brm)); 1136 | } 1137 | 1138 | free(data); 1139 | } 1140 | else 1141 | { 1142 | fprintf(stderr, "failed to allocate buffer\n"); 1143 | } 1144 | fclose(fd); 1145 | } 1146 | else 1147 | { 1148 | fprintf(stderr, "failed to open file '%s' for reading\n", path); 1149 | } 1150 | return status; 1151 | } 1152 | 1153 | int main(int argc, char **argv) 1154 | { 1155 | struct buram brm; 1156 | 1157 | int status = 0; 1158 | int dirty = 0; 1159 | 1160 | int statistics = 0; 1161 | int verbose = 0; 1162 | int protect = 0; 1163 | const char *file_name = "test.brm"; // File used for reading and writing (if write_file is null) 1164 | const char *write_file = 0; // File used for writing 1165 | 1166 | long file_size = 0x2000; 1167 | int file_init = -1; 1168 | char *file_data; 1169 | 1170 | char name_buf[12]; 1171 | char ascii_buf[12]; 1172 | 1173 | int param, operation; 1174 | 1175 | for(param = 1, operation = 0; param < argc; param++) 1176 | { 1177 | if(argv[param][0] == '-') 1178 | { 1179 | int this_param, sub_param; 1180 | for(this_param = param, sub_param = 1; argv[this_param][sub_param]; sub_param++) 1181 | { 1182 | switch(argv[this_param][sub_param]) 1183 | { 1184 | case 'f': // Set filename 1185 | if(++param < argc) 1186 | file_name = argv[param]; 1187 | else 1188 | ERROR("missing parameter for '-f'\n"); 1189 | break; 1190 | case 'o': // Set output filename 1191 | if(++param < argc) 1192 | write_file = argv[param]; 1193 | else 1194 | ERROR("missing parameter for '-o'\n"); 1195 | break; 1196 | case 's': // Set size (untested) 1197 | if(++param < argc) 1198 | file_size = strtoul(argv[param], NULL, 0); 1199 | else 1200 | ERROR("missing parameter for '-s'\n"); 1201 | break; 1202 | case 'v': // Verbose mode 1203 | verbose = 1; 1204 | break; 1205 | case 'i': // Print statistics 1206 | statistics = 1; 1207 | break; 1208 | case 'p': // Use protection 1209 | protect = 1; 1210 | break; 1211 | case 'n': // Don't use protection 1212 | protect = 0; 1213 | break; 1214 | default: 1215 | operation = tolower(argv[this_param][sub_param]); 1216 | break; 1217 | } 1218 | } 1219 | } 1220 | else 1221 | { 1222 | if(file_init) 1223 | { 1224 | int open_result = open(&brm, operation == 'c' ? NULL : file_name, &file_size, &file_init, &file_data); 1225 | if((operation == 'c' && open_result < 0) || (operation != 'c' && open_result != 0)) 1226 | ERROR("failed to open '%s' for reading (operation=%c, result=%d)\n", file_name, operation, open_result); 1227 | } 1228 | 1229 | char *fn_start = strrchr(argv[param], '/'); 1230 | if(!fn_start) 1231 | strrchr(argv[param], '\\'); 1232 | fn_start = fn_start ? fn_start + 1 : argv[param]; 1233 | buram_ascii_to_text(fn_start, name_buf, 11); 1234 | buram_text_to_ascii(name_buf, ascii_buf, 11); 1235 | 1236 | operation = toupper(operation); 1237 | switch(operation) 1238 | { 1239 | case 'C': 1240 | DEBUG("formatting SRAM image\n"); 1241 | buram_format(&brm, NULL); 1242 | operation = 'A'; //fall through... 1243 | case 'A': 1244 | DEBUG("adding file '%s' as '%s'...\n",argv[param],ascii_buf); 1245 | status |= add(&brm, argv[param], name_buf, verbose, protect); 1246 | dirty = 1; 1247 | break; 1248 | case 'D': 1249 | DEBUG("deleting file '%s'...\n",ascii_buf); 1250 | status |= list(&brm, name_buf, 0, verbose, cb_delete, NULL); 1251 | dirty = 1; 1252 | break; 1253 | case 'T': // Directory listing 1254 | DEBUG("listing '%s'...\n",ascii_buf); 1255 | status |= list(&brm, name_buf, 0, verbose, cb_list, NULL); 1256 | break; 1257 | case 'X': 1258 | DEBUG("extracting '%s' as '%s.bin'...\n",ascii_buf,ascii_buf); 1259 | status |= list(&brm, name_buf, 1, verbose, cb_extract, NULL); 1260 | break; 1261 | } 1262 | } 1263 | } 1264 | switch(operation) 1265 | { 1266 | case 0: 1267 | if(statistics) 1268 | break; 1269 | // Fall through. 1270 | case 'h': 1271 | printf("buram - Mega CD backup RAM management tool\n"); 1272 | printf("version 1.0 (C) 2022 Ian Karlsson\n\n"); 1273 | printf("operations:\n"); 1274 | printf("\t-c: format new BRAM image and add files\n"); 1275 | printf("\t-a [files...]: add files\n"); 1276 | printf("\t-d [pattern]: delete files\n"); 1277 | printf("\t-t : list files\n"); 1278 | printf("\t-x : extract files\n"); 1279 | printf("options:\n"); 1280 | printf("\t-f [filename]: set filename for reading and writing\n"); 1281 | printf("\t-o [filename]: set filename for writing only\n"); 1282 | printf("\t-v: verbose output\n"); 1283 | printf("\t-i: display volume information\n"); 1284 | printf("\t-s [size]: set size (in bytes) when creating new BRAM image\n"); 1285 | printf("\t-p: add files with ECC protection\n"); 1286 | printf("\t-n: don't add files with protection\n"); 1287 | break; 1288 | case 'c': // Create empty SRAM image 1289 | printf("formatting empty SRAM image\n"); 1290 | if(open(&brm, file_name, &file_size, &file_init, &file_data) < 0) 1291 | ERROR("failed to open '%s' for reading\n", file_name); 1292 | buram_format(&brm, NULL); 1293 | dirty = 1; 1294 | break; 1295 | case 't': // List all files 1296 | DEBUG("listing all files...\n"); 1297 | if(open(&brm, file_name, &file_size, &file_init, &file_data) != 0) 1298 | ERROR("failed to open '%s' for reading\n", file_name); 1299 | buram_ascii_to_text("*", name_buf, 11); 1300 | list(&brm, name_buf, 0, verbose, cb_list, NULL); 1301 | break; 1302 | case 'x': // Extract all files 1303 | DEBUG("extracting all files...\n"); 1304 | if(open(&brm, file_name, &file_size, &file_init, &file_data) != 0) 1305 | ERROR("failed to open '%s' for reading\n", file_name); 1306 | buram_ascii_to_text("*", name_buf, 11); 1307 | list(&brm, name_buf, 1, verbose, cb_extract, NULL); 1308 | break; 1309 | case 'd': // Delete 1310 | case 'a': // Add files 1311 | fprintf(stderr, "no files specified\n"); 1312 | break; 1313 | } 1314 | 1315 | if(statistics) 1316 | { 1317 | if(!open(&brm, file_name, &file_size, &file_init, &file_data) < 0) 1318 | ERROR("failed to open '%s' for reading\n", file_name); 1319 | status |= stats(&brm, verbose); 1320 | } 1321 | 1322 | if(dirty) 1323 | { 1324 | status |= save(&brm, write_file ? write_file : file_name, file_size, file_init, file_data); 1325 | } 1326 | 1327 | return status; 1328 | } 1329 | --------------------------------------------------------------------------------