Created
January 17, 2017 17:11
-
-
Save feliwir/196e72f862aa9c62fec535c4e717d715 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//(C) Stephan Vedder 2014 | |
#include "BigFile.hpp" | |
#include <stdexcept> | |
BigFile::BigEntry::BigEntry() : | |
name(""), data(""), size(0), offset(0) | |
{} | |
BigFile::BigEntry::BigEntry(std::string name, uint32_t size, std::string data) : | |
name(name), data(data), size(size), offset(0), new_offset(0) | |
{} | |
BigFile::BigEntry::BigEntry(const char* name, uint32_t size, const char* data) : | |
name(name), data(data), size(size), offset(0), new_offset(0) | |
{} | |
BigFile::BigEntry::BigEntry(const BigEntry& copy) : | |
size(copy.size), offset(copy.offset), data(copy.data), name(copy.name), new_offset(0) | |
{} | |
BigFile::BigFile() : m_numEntries(0) | |
{ | |
} | |
void BigFile::BigEntry::Release() | |
{ | |
size = 0; | |
offset = 0; | |
} | |
BigFile::BigFile(const char* filename) : m_numEntries(0), m_filename(filename) | |
{ | |
m_fin.open(filename, std::ios::binary|std::ios::in); | |
if (m_fin.fail()) | |
throw std::runtime_error("Failed to open BigFile"); | |
//Read in the first 4 bytes | |
char magic[4]; | |
for (auto i = 0; i < 4; ++i) | |
{ | |
magic[i] = m_fin.get(); | |
} | |
//Check if this a valid BIG4 File | |
const char check[] = { "BIG4" }; | |
for (auto i = 0; i < 4; ++i) | |
{ | |
if (magic[i] != check[i]) | |
throw std::runtime_error("Not a valid BigFile"); | |
} | |
m_archiveSize = Read<uint32_t>(); | |
m_numEntries = Reverse(Read<uint32_t>()); | |
m_offsetFirst = Reverse(Read<uint32_t>()); | |
//Read every entry | |
for (auto i = 0; i < m_numEntries; i++) | |
{ | |
BigEntry entry; | |
entry.offset = Reverse(Read<uint32_t>()); | |
entry.size = Reverse(Read<uint32_t>()); | |
entry.name = ReadCString(); | |
m_entries.push_back(entry); | |
} | |
} | |
const uint32_t BigFile::GetSize() | |
{ | |
return m_archiveSize; | |
} | |
const char* BigFile::GetEntry(const uint32_t entryid) | |
{ | |
if (entryid>m_entries.size()) | |
return false; | |
BigEntry& entry = m_entries[entryid]; | |
if (entry.data.size()>0) | |
return entry.data.c_str(); | |
else | |
return ReadFixedString(entry.offset, entry.size); | |
} | |
const char* BigFile::GetEntryByName(const char* entryname) | |
{ | |
auto index = 0; | |
std::string name(entryname); | |
std::replace(name.begin(), name.end(), '/', '\\'); | |
for (auto& i : m_entries) | |
{ | |
if (Equal(i.name,name)) | |
break; | |
++index; | |
} | |
if (index == m_entries.size()) | |
return nullptr; | |
BigEntry& entry = m_entries[index]; | |
if (entry.data.size()>0) | |
return entry.data.c_str(); | |
else | |
return ReadFixedString(entry.offset, entry.size); | |
} | |
const uint32_t BigFile::GetEntrySize(const uint32_t entryid) | |
{ | |
if (entryid>m_entries.size()) | |
return 0; | |
BigEntry& entry = m_entries[entryid]; | |
return entry.size; | |
} | |
const uint32_t BigFile::GetEntrySizeByName(const char* entryname) | |
{ | |
auto index = 0; | |
std::string name(entryname); | |
std::replace(name.begin(), name.end(), '/', '\\'); | |
for (auto& i : m_entries) | |
{ | |
if (Equal(i.name, name)) | |
break; | |
++index; | |
} | |
if (index == m_entries.size()) | |
return 0; | |
BigEntry& entry = m_entries[index]; | |
return entry.size; | |
} | |
const char** BigFile::GetFileNames() | |
{ | |
const char** result = new const char*[m_entries.size()]; | |
auto index = 0; | |
for (auto& i : m_entries) | |
{ | |
result[index] = i.name.c_str(); | |
++index; | |
} | |
return result; | |
} | |
const uint32_t BigFile::GetNumEntries() | |
{ | |
return m_entries.size(); | |
} | |
const uint32_t BigFile::GetEntryIndex(const char* entryname) | |
{ | |
auto index = 0; | |
std::string name(entryname); | |
std::replace(name.begin(), name.end(), '/', '\\'); | |
for (auto& i : m_entries) | |
{ | |
if (Equal(i.name, name)) | |
break; | |
++index; | |
} | |
return index; | |
} | |
bool BigFile::RemoveEntry(const uint32_t entryid) | |
{ | |
if (entryid > m_entries.size()) | |
return false; | |
m_entries.erase(m_entries.begin() + entryid); | |
--m_numEntries; | |
return true; | |
} | |
bool BigFile::RemoveEntryByName(const char* entryname) | |
{ | |
auto index = 0; | |
std::string name(entryname); | |
std::replace(name.begin(), name.end(), '/', '\\'); | |
for (auto& i : m_entries) | |
{ | |
if (Equal(i.name, name)) | |
break; | |
++index; | |
} | |
if (index == m_entries.size()) | |
return false; | |
m_entries.erase(m_entries.begin() + index); | |
--m_numEntries; | |
return true; | |
} | |
bool BigFile::AddEntry(const char* entryname, const char* data, uint32_t size, bool overwrite) | |
{ | |
auto index = 0; | |
std::string str(entryname); | |
std::replace(str.begin(), str.end(), '/', '\\'); | |
for (auto& i : m_entries) | |
{ | |
if (Equal(i.name, str)) | |
break; | |
++index; | |
} | |
if ((index != m_entries.size()) && !overwrite) | |
return false; | |
BigEntry entry(str, size, data); | |
m_entries.push_back(entry); | |
++m_numEntries; | |
return true; | |
} | |
bool BigFile::Write() | |
{ | |
if (m_filename.size() > 0) | |
return BigFile::Write(m_filename.c_str()); | |
else | |
return false; | |
} | |
bool BigFile::Write(const char* filename) | |
{ | |
//calculate new filesize: | |
auto size = 24; | |
auto offsetFirst = 24; | |
for (auto& i : m_entries) | |
{ | |
size += i.size; | |
size += i.name.size() + 1; | |
size += 8; | |
offsetFirst += i.name.size() + 1; | |
offsetFirst += 8; | |
} | |
//recalculate offsets | |
auto new_offset = offsetFirst; | |
for (auto& i : m_entries) | |
{ | |
i.new_offset = new_offset; | |
new_offset += i.size; | |
} | |
//create new buffer | |
char* data = new char[size+10]; | |
char* iter = data; | |
memset(data, '\0', size); | |
Write("BIG4", iter); | |
Write(size, iter); | |
Write(m_numEntries, iter); | |
Write(offsetFirst, iter); | |
for (auto& i : m_entries) | |
{ | |
Write(i.new_offset, iter); | |
Write(i.size, iter); | |
WriteCString(i.name, iter); | |
auto cpos = iter - data; | |
//file is already cached | |
if (i.data.size()>0) | |
{ | |
WriteFixedString(i.new_offset - cpos, i.size, i.data, iter); | |
i.data.clear(); | |
} | |
//read it from disk first | |
else | |
{ | |
std::string filedata = ReadFixedString(i.offset, i.size); | |
WriteFixedString(i.new_offset - cpos, i.size, filedata, iter); | |
int a = 0; | |
} | |
i.offset = i.new_offset; | |
} | |
std::ofstream fof(filename, std::ios::binary | std::ios::out); | |
if (fof.fail()) | |
{ | |
delete[] data; | |
return false; | |
} | |
fof.write(data, size); | |
fof.close(); | |
if (!Equal(filename, m_filename)) | |
{ | |
if (m_fin.is_open()) | |
{ | |
m_fin.close(); | |
} | |
m_fin.open(filename, std::ios::binary | std::ios::in); | |
if (m_fin.fail()) | |
{ | |
return false; | |
} | |
m_filename = filename; | |
} | |
//finally we made it succesfully | |
m_offsetFirst = offsetFirst; | |
m_archiveSize = size; | |
return true; | |
} | |
BigFile::~BigFile() | |
{ | |
if (m_fin.is_open()) | |
m_fin.close(); | |
for (auto& i : m_entries) | |
{ | |
i.Release(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment