Skip to content

Instantly share code, notes, and snippets.

@phrohdoh
Forked from feliwir/bigfile.cpp
Created January 17, 2017 17:13
Show Gist options
  • Save phrohdoh/dbc1438e427ccc680b8c88ffd903f1ac to your computer and use it in GitHub Desktop.
Save phrohdoh/dbc1438e427ccc680b8c88ffd903f1ac to your computer and use it in GitHub Desktop.
//(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