unsigned stats_get_version(char *type, uint8_t *data){ /* I could've used iff_parse_chunk instead to determine chunk versions, but this would be unnecessarily slow and would require all chunk parsers to be written, defeating the purpose of this tool. */ if(!strcmp(type, "STR#") || !strcmp(type, "CTSS") || !strcmp(type, "FAMs") || !strcmp(type, "TTAs") || !strcmp(type, "CST") || !strcmp(type, "BHAV") || !strcmp(type, "DGRP") || !strcmp(type, "POSI")) return read_uint16le(data); if(!strcmp(type, "FCNS") || !strcmp(type, "OBJf") || !strcmp(type, "Optn") || !strcmp(type, "Rcon") || !strcmp(type, "TPRP") || !strcmp(type, "SLOT") || !strcmp(type, "TRCN") || !strcmp(type, "rsmp")) return read_uint32le(data+4); if(!strcmp(type, "OBJD") || !strcmp(type, "PALT") || !strcmp(type, "SPR2")) return read_uint32le(data); if(!strcmp(type, "TTAB")) return read_uint16le(data+2); if(!strcmp(type, "SPR#")){ if(data[0] == 0) return read_uint32be(data); else return read_uint32le(data); } return -1; }
bool get_sgc_metadata(int fd, struct mp3entry* id3) { uint32_t sgc_type; if ((lseek(fd, 0, SEEK_SET) < 0) || read_uint32be(fd, &sgc_type) != (int)sizeof(sgc_type)) return false; id3->vbr = false; id3->filesize = filesize(fd); /* we only render 16 bits, 44.1KHz, Stereo */ id3->bitrate = 706; id3->frequency = 44100; /* Make sure this is an SGC file */ if (sgc_type != FOURCC('S','G','C',0x1A)) return false; return parse_sgc_header(fd, id3); }
bool get_nsf_metadata(int fd, struct mp3entry* id3) { uint32_t nsf_type; if (lseek(fd, 0, SEEK_SET) < 0 || read_uint32be(fd, &nsf_type) != (int)sizeof(nsf_type)) return false; id3->vbr = false; id3->filesize = filesize(fd); /* we only render 16 bits, 44.1KHz, Mono */ id3->bitrate = 706; id3->frequency = 44100; if (nsf_type == CHAR4_CONST('N', 'S', 'F', 'E')) return parse_nsfe(fd, id3); else if (nsf_type == CHAR4_CONST('N', 'E', 'S', 'M')) return parse_nesm(fd, id3); /* not a valid format*/ return false; }
int ordered_map_file_open(const char *path, OrderedMapFile **out_omf) { *out_omf = nullptr; OrderedMapFile *omf = create_zero<OrderedMapFile>(); if (!omf) { ordered_map_file_close(omf); return GenesisErrorNoMem; } if (omf->queue.error() || omf->cond.error() || omf->mutex.error()) { ordered_map_file_close(omf); return omf->queue.error() || omf->cond.error() || omf->mutex.error(); } omf->list = create_zero<List<OrderedMapFileEntry *>>(); if (!omf->list) { ordered_map_file_close(omf); return GenesisErrorNoMem; } omf->map = create_zero<HashMap<ByteBuffer, OrderedMapFileEntry *, ByteBuffer::hash>>(); if (!omf->map) { ordered_map_file_close(omf); return GenesisErrorNoMem; } omf->running = true; int err = omf->write_thread.start(run_write, omf); if (err) { ordered_map_file_close(omf); return err; } bool open_for_writing = false; omf->file = fopen(path, "rb+"); if (omf->file) { int err = read_header(omf); if (err == GenesisErrorEmptyFile) { open_for_writing = true; } else if (err) { ordered_map_file_close(omf); return err; } } else { open_for_writing = true; } if (open_for_writing) { omf->file = fopen(path, "wb+"); if (!omf->file) { ordered_map_file_close(omf); return GenesisErrorFileAccess; } int err = write_header(omf); if (err) { ordered_map_file_close(omf); return err; } } // read everything into list omf->write_buffer.resize(TRANSACTION_METADATA_SIZE); omf->transaction_offset = UUID_SIZE; for (;;) { size_t amt_read = fread(omf->write_buffer.raw(), 1, TRANSACTION_METADATA_SIZE, omf->file); if (amt_read != TRANSACTION_METADATA_SIZE) { // partial transaction. ignore it and we're done. break; } uint8_t *transaction_ptr = (uint8_t*)omf->write_buffer.raw(); int transaction_size = read_uint32be(&transaction_ptr[4]); omf->write_buffer.resize(transaction_size); transaction_ptr = (uint8_t*)omf->write_buffer.raw(); size_t amt_to_read = transaction_size - TRANSACTION_METADATA_SIZE; amt_read = fread(&transaction_ptr[TRANSACTION_METADATA_SIZE], 1, amt_to_read, omf->file); if (amt_read != amt_to_read) { // partial transaction. ignore it and we're done. break; } uint32_t computed_crc = crc32(0, &transaction_ptr[4], transaction_size - 4); uint32_t crc_from_file = read_uint32be(&transaction_ptr[0]); if (computed_crc != crc_from_file) { // crc check failed. ignore this transaction and we're done. break; } int put_count = read_uint32be(&transaction_ptr[8]); int del_count = read_uint32be(&transaction_ptr[12]); int offset = TRANSACTION_METADATA_SIZE; for (int i = 0; i < put_count; i += 1) { int key_size = read_uint32be(&transaction_ptr[offset]); offset += 4; int val_size = read_uint32be(&transaction_ptr[offset]); offset += 4; OrderedMapFileEntry *entry = create_zero<OrderedMapFileEntry>(); if (!entry) { ordered_map_file_close(omf); return GenesisErrorNoMem; } entry->key = ByteBuffer((char*)&transaction_ptr[offset], key_size); offset += key_size; entry->offset = omf->transaction_offset + offset; entry->size = val_size; offset += val_size; auto old_hash_entry = omf->map->maybe_get(entry->key); if (old_hash_entry) { OrderedMapFileEntry *old_entry = old_hash_entry->value; destroy(old_entry, 1); } omf->map->put(entry->key, entry); } for (int i = 0; i < del_count; i += 1) { int key_size = read_uint32be(&transaction_ptr[offset]); offset += 4; ByteBuffer key((char*)&transaction_ptr[offset], key_size); offset += key_size; auto hash_entry = omf->map->maybe_get(key); if (hash_entry) { OrderedMapFileEntry *entry = hash_entry->value; omf->map->remove(key); destroy(entry, 1); } } omf->transaction_offset += transaction_size; } // transfer map to list and sort auto it = omf->map->entry_iterator(); if (omf->list->ensure_capacity(omf->map->size())) { ordered_map_file_close(omf); return GenesisErrorNoMem; } for (;;) { auto *map_entry = it.next(); if (!map_entry) break; ok_or_panic(omf->list->append(map_entry->value)); } omf->map->clear(); destroy_map(omf); omf->list->sort<compare_entries>(); *out_omf = omf; return 0; }
static bool parse_nsfe(int fd, struct mp3entry *id3) { unsigned int chunks_found = 0; long track_count = 0; long playlist_count = 0; struct NSFE_INFOCHUNK info; memset(&info, 0, sizeof(struct NSFE_INFOCHUNK)); /* default values */ info.nTrackCount = 1; id3->length = 150 * 1000; /* begin reading chunks */ while (!(chunks_found & CHUNK_NEND)) { uint32_t chunk_size, chunk_type; if (read_uint32le(fd, &chunk_size) != (int)sizeof(uint32_t)) return false; if (read_uint32be(fd, &chunk_type) != (int)sizeof(uint32_t)) return false; switch (chunk_type) { /* first three types are mandatory (but don't worry about NEND anyway) */ case CHAR4_CONST('I', 'N', 'F', 'O'): { if (chunks_found & CHUNK_INFO) return false; /* only one info chunk permitted */ chunks_found |= CHUNK_INFO; /* minimum size */ if (chunk_size < 8) return false; ssize_t size = MIN(sizeof(struct NSFE_INFOCHUNK), chunk_size); if (read(fd, &info, size) != size) return false; if (size >= 9) track_count = info.nTrackCount; chunk_size -= size; break; } case CHAR4_CONST('D', 'A', 'T', 'A'): { if (!(chunks_found & CHUNK_INFO)) return false; if (chunks_found & CHUNK_DATA) return false; /* only one may exist */ if (chunk_size < 1) return false; chunks_found |= CHUNK_DATA; break; } case CHAR4_CONST('N', 'E', 'N', 'D'): { /* just end parsing regardless of whether or not this really is the last chunk/data (but it _should_ be) */ chunks_found |= CHUNK_NEND; continue; } /* remaining types are optional */ case CHAR4_CONST('a', 'u', 't', 'h'): { if (chunks_found & CHUNK_auth) return false; /* only one may exist */ chunks_found |= CHUNK_auth; /* szGameTitle, szArtist, szCopyright */ char ** const ar[] = { &id3->title, &id3->artist, &id3->album }; char *p = id3->id3v2buf; long buf_rem = sizeof (id3->id3v2buf); unsigned int i; for (i = 0; i < ARRAYLEN(ar) && chunk_size && buf_rem; i++) { long len = read_string(fd, p, buf_rem, '\0', chunk_size); if (len < 0) return false; *ar[i] = p; p += len; buf_rem -= len; if (chunk_size >= (uint32_t)len) chunk_size -= len; else chunk_size = 0; } break; } case CHAR4_CONST('p', 'l', 's', 't'): { if (chunks_found & CHUNK_plst) return false; /* only one may exist */ chunks_found |= CHUNK_plst; /* each byte is the index of one track */ playlist_count = chunk_size; break; } case CHAR4_CONST('t', 'i', 'm', 'e'): case CHAR4_CONST('f', 'a', 'd', 'e'): case CHAR4_CONST('t', 'l', 'b', 'l'): /* we unfortunately can't use these anyway */ { /* don't care how many of these there are even though there should be only one */ if (!(chunks_found & CHUNK_INFO)) return false; case CHAR4_CONST('B', 'A', 'N', 'K'): break; } default: /* unknown chunk */ { /* check the first byte */ chunk_type = (uint8_t)chunk_type; /* chunk is vital... don't continue */ if(chunk_type >= 'A' && chunk_type <= 'Z') return false; /* otherwise, just skip it */ break; } } /* end switch */ lseek(fd, chunk_size, SEEK_CUR); } /* end while */ if (track_count | playlist_count) id3->length = MAX(track_count, playlist_count)*1000; /* Single subtrack files will be treated differently by gme's nsf codec */ if (id3->length <= 1000) id3->length = 150 * 1000; /* * if we exited the while loop without a 'return', we must have hit an NEND * chunk if this is the case, the file was layed out as it was expected. * now.. make sure we found both an info chunk, AND a data chunk... since * these are minimum requirements for a valid NSFE file */ return (chunks_found & (CHUNK_INFO | CHUNK_DATA)) == (CHUNK_INFO | CHUNK_DATA); }