/** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011 Bart Trzynadlowski, Nik Henson ** ** This file is part of Supermodel. ** ** Supermodel is free software: you can redistribute it and/or modify it under ** the terms of the GNU General Public License as published by the Free ** Software Foundation, either version 3 of the License, or (at your option) ** any later version. ** ** Supermodel is distributed in the hope that it will be useful, but WITHOUT ** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for ** more details. ** ** You should have received a copy of the GNU General Public License along ** with Supermodel. If not, see . **/ /* * BlockFile.cpp * * Block format container file management. Implementation of the CBlockFile * class. */ #include "BlockFile.h" #include #include #include #include "Supermodel.h" /****************************************************************************** Output Functions ******************************************************************************/ void CBlockFile::ReadString(std::string *str, uint32_t length) { if (NULL == fp) return; str->clear(); //TODO: use fstream to get rid of this ugly hack bool keep_loading = true; for (uint32_t i = 0; i < length; i++) { char c; fread(&c, sizeof(char), 1, fp); if (keep_loading) { if (!c) keep_loading = false; else *str += c; } } } unsigned CBlockFile::ReadBytes(void *data, uint32_t numBytes) { if (NULL == fp) return 0; return fread(data, sizeof(uint8_t), numBytes, fp); } unsigned CBlockFile::ReadDWord(uint32_t *data) { if (NULL == fp) return 0; fread(data, sizeof(uint32_t), 1, fp); return 4; } void CBlockFile::UpdateBlockSize(void) { long int curPos; unsigned newBlockSize; if (NULL == fp) return; curPos = ftell(fp); // save current file position fseek(fp, blockStartPos, SEEK_SET); newBlockSize = curPos - blockStartPos; fwrite(&newBlockSize, sizeof(uint32_t), 1, fp); fseek(fp, curPos, SEEK_SET); // go back } void CBlockFile::WriteByte(uint8_t data) { if (NULL == fp) return; fwrite(&data, sizeof(uint8_t), 1, fp); UpdateBlockSize(); } void CBlockFile::WriteDWord(uint32_t data) { if (NULL == fp) return; fwrite(&data, sizeof(uint32_t), 1, fp); UpdateBlockSize(); } void CBlockFile::WriteBytes(const void *data, uint32_t numBytes) { if (NULL == fp) return; fwrite(data, sizeof(uint8_t), numBytes, fp); UpdateBlockSize(); } void CBlockFile::WriteBlockHeader(const std::string &name, const std::string &comment) { if (NULL == fp) return; // Record current block starting position blockStartPos = ftell(fp); // Write the total block length field WriteDWord(0); // will be automatically updated as we write the file // Write name and comment lengths WriteDWord(name.size() + 1); WriteDWord(comment.size() + 1); Write(name); Write(comment); // Record the start of the current data section dataStartPos = ftell(fp); } /****************************************************************************** Block Format Container File Implementation Files are just a consecutive array of blocks that must be searched. Block Format ------------ blockLength (uint32_t) Total length of block in bytes. nameLength (uint32_t) Length of name field including terminating 0 (up to 1025). commentLength (uint32_t) Same as above, but for comment string. name ... Name string (null-terminated, up to 1025 bytes). comment ... Comment string (same as above). data ... Raw data (blockLength - total header size). ******************************************************************************/ unsigned CBlockFile::Read(void *data, uint32_t numBytes) { if (mode == 'r') return ReadBytes(data, numBytes); return 0; } unsigned CBlockFile::Read(bool *value) { uint8_t byte; unsigned numBytes = Read(&byte, sizeof(byte)); if (numBytes > 0) *value = byte != 0; return numBytes; } void CBlockFile::Write(const void *data, uint32_t numBytes) { if (mode == 'w') WriteBytes(data, numBytes); } void CBlockFile::Write(bool value) { uint8_t byte = value ? 1 : 0; Write(&byte, sizeof(byte)); } void CBlockFile::Write(const std::string &str) { if (mode == 'w') WriteBytes(str.c_str(), str.length() + 1); } void CBlockFile::NewBlock(const std::string &name, const std::string &comment) { if (mode == 'w') WriteBlockHeader(name, comment); } bool CBlockFile::FindBlock(const std::string &name) { if (mode != 'r') return FAIL; fseek(fp, 0, SEEK_SET); long int curPos = 0; while (curPos < fileSize) { blockStartPos = curPos; // Read header uint32_t block_length; uint32_t name_length; uint32_t comment_length; curPos += ReadDWord(&block_length); curPos += ReadDWord(&name_length); curPos += ReadDWord(&comment_length); std::string block_name; ReadString(&block_name, name_length); // Is this the block we want? if (block_name == name) { fseek(fp, blockStartPos + 12 + name_length + comment_length, SEEK_SET); // move to beginning of data dataStartPos = ftell(fp); return OKAY; } // Move to next block fseek(fp, blockStartPos + block_length, SEEK_SET); curPos = blockStartPos + block_length; if (block_length == 0) // this would never advance break; } return FAIL; } bool CBlockFile::Create(const std::string &file, const std::string &headerName, const std::string &comment) { fp = fopen(file.c_str(), "wb"); if (NULL == fp) return FAIL; mode = 'w'; WriteBlockHeader(headerName, comment); return OKAY; } bool CBlockFile::Load(const std::string &file) { fp = fopen(file.c_str(), "rb"); if (NULL == fp) return FAIL; mode = 'r'; // TODO: is this a valid block file? // Get the file size fseek(fp, 0, SEEK_END); fileSize = ftell(fp); fseek(fp, 0, SEEK_SET); return OKAY; } void CBlockFile::Close(void) { if (fp != NULL) fclose(fp); fp = NULL; mode = 0; } CBlockFile::CBlockFile(void) { fp = NULL; mode = 0; // neither reading nor writing (do nothing) } CBlockFile::~CBlockFile(void) { if (fp != NULL) // in case user forgot fclose(fp); fp = NULL; mode = 0; }