commit x64 compilation from lulu cause the other branch dont seems to compile properly at home
This commit is contained in:
440
include/modules/archive/archive.cpp
Normal file
440
include/modules/archive/archive.cpp
Normal file
@ -0,0 +1,440 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <cstring>
|
||||
#include "zlib.h"
|
||||
#include "archive/archive.h"
|
||||
#include "metafile/nml.h"
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "thread/mutex.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::NML;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#define __CorrectOffsetPadding \
|
||||
{\
|
||||
if (offset_padding)\
|
||||
{\
|
||||
size_t error = handle->Tell() % offset_padding;\
|
||||
if (error)\
|
||||
handle->Seek((long)(offset_padding - error));\
|
||||
}\
|
||||
}
|
||||
bool ArchiveIndex::Load(const char *uri)
|
||||
{
|
||||
index.Clear();
|
||||
|
||||
__LOG_V__ << "Loading archive index '" << uri << "'...\n";
|
||||
|
||||
File file;
|
||||
if (!Parser::Load(uri, file))
|
||||
return false;
|
||||
|
||||
// Grab index tag.
|
||||
Tag *index_tag = file.GetTag("Index;");
|
||||
if (!index_tag)
|
||||
__ERR__(__LOG_E__ << "No archive index tag found in '" << uri << "'.\n", false)
|
||||
|
||||
// Pool for entries.
|
||||
NMLTagForeach(t, *index_tag)
|
||||
if (t->name == "Entry")
|
||||
{
|
||||
Tag *id_tag = t->GetTypedTag("ID;", Variant::VariantString),
|
||||
*compressed_tag = t->GetTypedTag("CompLen", Variant::VariantInteger),
|
||||
*length_tag = t->GetTypedTag("Len", Variant::VariantInteger),
|
||||
*method_tag = t->GetTypedTag("Method", Variant::VariantString),
|
||||
*offset_tag = t->GetTypedTag("Offset", Variant::VariantInteger);
|
||||
|
||||
if (id_tag && compressed_tag && length_tag && method_tag)
|
||||
{
|
||||
// Import the entry.
|
||||
if (ArchiveEntry *entry = new ArchiveEntry)
|
||||
{
|
||||
entry->path = id_tag->GetString();
|
||||
entry->length = length_tag->GetInteger();
|
||||
entry->compressed_length = compressed_tag->GetInteger();
|
||||
entry->offset = offset_tag->GetInteger();
|
||||
|
||||
String method(method_tag->GetString());
|
||||
|
||||
if (method == "Raw")
|
||||
entry->method = ArchiveEntry::MethodRaw;
|
||||
else if (method == "Zlib")
|
||||
entry->method = ArchiveEntry::MethodZLibCompress;
|
||||
|
||||
index.Add(entry);
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Failed to allocate new archive index entry.\n";
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Incomplete index entry.\n";
|
||||
}
|
||||
else
|
||||
__LOG_W__ << "Unexpected tag <" << t->name << "> in <Index>.\n";
|
||||
|
||||
__LOG_V__ << "Done, found " << index.GetCount() << " entries.\n";
|
||||
return true;
|
||||
}
|
||||
bool ArchiveIndex::Save(const char *uri)
|
||||
{
|
||||
File file;
|
||||
Tag *index_tag = file.AddRoot("Index");
|
||||
|
||||
ListForeachPtr(ArchiveEntry *, entry, index)
|
||||
{
|
||||
// Create entry.
|
||||
Tag *entry_tag = index_tag->AddChild("Entry");
|
||||
|
||||
entry_tag->AddChild("ID", entry->path.c_str());
|
||||
entry_tag->AddChild("CompLen", (int)entry->compressed_length);
|
||||
entry_tag->AddChild("Len", (int)entry->length);
|
||||
entry_tag->AddChild("Offset", (int)entry->offset);
|
||||
|
||||
switch (entry->method)
|
||||
{
|
||||
case ArchiveEntry::MethodRaw:
|
||||
entry_tag->AddChild("Method", "Raw");
|
||||
break;
|
||||
|
||||
case ArchiveEntry::MethodZLibCompress:
|
||||
entry_tag->AddChild("Method", "Zlib");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Parser::Save(uri, file);
|
||||
}
|
||||
ArchiveEntry *ArchiveIndex::FindEntry(const char *alias) const
|
||||
{
|
||||
ListForeachPtr(ArchiveEntry *, e, index)
|
||||
if (e->path == alias)
|
||||
return e;
|
||||
return NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Archive::LoadIndex(const char *uri)
|
||||
{ return index.Load(uri); }
|
||||
bool Archive::SaveIndex(const char *uri)
|
||||
{ return index.Save(uri); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Archive::OpenRead(const char *uri, const char *idx)
|
||||
{
|
||||
Close();
|
||||
if (!uri)
|
||||
__ERR__(__LOG_E__ << "No archive to open.\n", false)
|
||||
|
||||
if (!(handle = Platform::Get().io->Open(uri)))
|
||||
__ERR__(__LOG_E__ << "Failed to open archive '" << uri << "'.\n", false)
|
||||
|
||||
// Check archive header.
|
||||
__CorrectOffsetPadding
|
||||
|
||||
uint magic_word = handle->Read <uint> ();
|
||||
|
||||
if (magic_word == 0x4E415244) // 'NARD' (padding support).
|
||||
{
|
||||
__CorrectOffsetPadding
|
||||
offset_padding = handle->Read <int> ();
|
||||
__CorrectOffsetPadding
|
||||
size_padding = handle->Read <int> ();
|
||||
revision = EnhancedLegacy;
|
||||
}
|
||||
else if (magic_word == 0x4E415243) // 'NARC' backward compatibility.
|
||||
{
|
||||
offset_padding = 0;
|
||||
size_padding = 0;
|
||||
revision = Legacy;
|
||||
}
|
||||
else
|
||||
{
|
||||
__LOG_E__ << "Invalid archive type '" << uri << "'.\n";
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open or create index if none provided.
|
||||
if (!idx)
|
||||
{
|
||||
size_t alen = handle->GetSize();
|
||||
|
||||
if (verbose)
|
||||
__LOG__ << "No index provided, please wait while scanning archive...\n";
|
||||
|
||||
while (handle->Tell() < alen)
|
||||
{
|
||||
// Fetch alias.
|
||||
char tmp[512];
|
||||
__CorrectOffsetPadding
|
||||
uint tsz = handle->Read <uint> ();
|
||||
|
||||
if (tsz > 511)
|
||||
break;
|
||||
|
||||
else
|
||||
{
|
||||
handle->Read((void *)tmp, tsz);
|
||||
tmp[tsz] = 0;
|
||||
}
|
||||
|
||||
// Attributes.
|
||||
__CorrectOffsetPadding
|
||||
char cmp = handle->Read <uchar> () & 1;
|
||||
__CorrectOffsetPadding
|
||||
uint len = handle->Read <uint> (), clen = len;
|
||||
if (cmp)
|
||||
{
|
||||
__CorrectOffsetPadding
|
||||
clen = handle->Read <uint> ();
|
||||
}
|
||||
|
||||
// Store in index.
|
||||
if (ArchiveEntry *entry = new ArchiveEntry)
|
||||
{
|
||||
entry->path = tmp;
|
||||
entry->method = cmp;
|
||||
entry->length = len;
|
||||
entry->compressed_length = clen;
|
||||
entry->offset = handle->Tell();
|
||||
index.index.Add(entry);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
__LOG__ << "New entry: '" << entry->path << "'\n";
|
||||
__LOG__ << "[method = " << entry->method << ", len = " << (uint)entry->length << ", clen = " << (uint)entry->compressed_length << ", offset = " << (uint)entry->offset << "].\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
__LOG_E__ << "Could not allocate new index entry.\n";
|
||||
|
||||
handle->Seek(clen);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
__LOG__ << "Done, index table built.\n\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadIndex(idx);
|
||||
|
||||
if (verbose)
|
||||
ListForeachPtr(ArchiveEntry *, entry, index.index)
|
||||
{
|
||||
__LOG__ << "New entry: '" << entry->path << "'\n";
|
||||
__LOG__ << "[method = " << entry->method << ", len = " << (uint)entry->length << ", clen = " << (uint)entry->compressed_length << ", offset = " << (uint)entry->offset << "].\n";
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Archive::CreateNew(const char *uri)
|
||||
{
|
||||
Close();
|
||||
|
||||
if (!(handle = Platform::Get().io->Open(uri, IO::ModeWrite)))
|
||||
__ERR__(__LOG_E__ << "Failed to open '" << uri << "' as output archive.\n", false)
|
||||
|
||||
// Output header.
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <uint> (0x4E415244); // 'NARD'
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <int> (offset_padding);
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <int> (size_padding);
|
||||
|
||||
append_mode = true;
|
||||
return true;
|
||||
}
|
||||
void Archive::Close()
|
||||
{
|
||||
if (append_mode)
|
||||
{
|
||||
// EOF marker.
|
||||
handle->Write <uint> (0xffffffff);
|
||||
|
||||
// Size padding.
|
||||
size_t size = handle->Tell();
|
||||
size_t pad_count = size_padding ? size_padding - size % size_padding : 0;
|
||||
|
||||
for (size_t n = 0; n < pad_count; ++n)
|
||||
handle->Write <uchar> (0xff); // End of archive marker (entry id length > 512).
|
||||
}
|
||||
|
||||
// Close archive.
|
||||
handle = NULL;
|
||||
index.index.Clear();
|
||||
|
||||
if (append_mode)
|
||||
__LOG__ << "Archive complete, closing.\n";
|
||||
append_mode = false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Archive::FileRead(const char *path, void *out)
|
||||
{
|
||||
if (append_mode)
|
||||
__ERR__(__LOG_E__ << "Cannot load '" << path << "' (r/w mode error).\n", false)
|
||||
|
||||
ArchiveEntry *entry = Exists(path);
|
||||
if (!entry)
|
||||
__ERR__(__LOG_E__ << "Could not find '" << path << "'.\n", false)
|
||||
|
||||
Threading::MutexLock lock(access_mutex);
|
||||
|
||||
size_t h_cursor = handle->Tell();
|
||||
|
||||
switch (entry->method)
|
||||
{
|
||||
case ArchiveEntry::MethodRaw:
|
||||
handle->Seek(entry->offset, IO::Base::SeekStart); // Note: Should be padded to the right offset already.
|
||||
if (handle->Read(out, entry->length) != entry->length)
|
||||
__LOG_E__ << "Failed to load raw source.\n";
|
||||
break;
|
||||
|
||||
case ArchiveEntry::MethodZLibCompress:
|
||||
{
|
||||
handle->Seek(entry->offset, IO::Base::SeekStart); // Note: Should be padded to the right offset already.
|
||||
|
||||
Array <char> cp((uint)(entry->compressed_length + 16));
|
||||
if (!cp)
|
||||
__ERR__(__LOG_E__ << "Failed to allocated compressed memory support.\n", false)
|
||||
|
||||
if (handle->Read((void *)cp, entry->compressed_length) != entry->compressed_length)
|
||||
__ERR__(__LOG_E__ << "Failed to load compressed source.\n", false)
|
||||
|
||||
uLong out_len = (uLong)entry->length;
|
||||
uncompress((Bytef *)out, &out_len, (Bytef *)&cp[0], (uLong)entry->compressed_length);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
handle->Seek(h_cursor, IO::Base::SeekStart);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ArchiveEntry *Archive::MemoryBlockWrite(const char *alias, const void *in, size_t len, int level)
|
||||
{
|
||||
if (!append_mode || !len)
|
||||
__ERR__(__LOG_E__ << "Cannot write '" << alias << "' (r/w mode error).\n", NULL)
|
||||
|
||||
Threading::MutexLock lock(access_mutex);
|
||||
|
||||
// Ensure alias uniqueness.
|
||||
if (ArchiveEntry *e = index.FindEntry(alias))
|
||||
return e;
|
||||
|
||||
// Sync index.
|
||||
ArchiveEntry *entry = new ArchiveEntry;
|
||||
if (!entry)
|
||||
__ERR__(__LOG_E__ << "Failed to allocate new index entry.\n", NULL)
|
||||
entry->path = alias;
|
||||
index.index.Add(entry);
|
||||
|
||||
// Compress and add to archive.
|
||||
Array <char> out;
|
||||
size_t clen;
|
||||
|
||||
// Output alias.
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <uint> (std::strlen(alias));
|
||||
__CorrectOffsetPadding
|
||||
handle->Write((const void *)alias, std::strlen(alias));
|
||||
if (verbose)
|
||||
__LOG_H__ << "Adding '" << alias << "' to archive...\n";
|
||||
|
||||
// Output data, ZLIB needs destination to be at least source * 100.1% + 12 bytes.
|
||||
bool add_raw = true;
|
||||
|
||||
if (level >= 0)
|
||||
{
|
||||
clen = len + (len / 1000 + 1) + 12;
|
||||
|
||||
if (out.Allocate((uint)clen) && (compress2((Bytef *)&out[0], (uLongf *)&clen, (const Bytef *)in, (uLong)len, level) == Z_OK))
|
||||
{
|
||||
// @TODO compression method as a bit flag here.
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <uchar> (1);
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <uint> (len);
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <uint> (clen);
|
||||
|
||||
// Write compressed block.
|
||||
__CorrectOffsetPadding
|
||||
entry->method = ArchiveEntry::MethodZLibCompress;
|
||||
entry->offset = handle->Tell();
|
||||
entry->length = len;
|
||||
entry->compressed_length = clen;
|
||||
|
||||
if (verbose)
|
||||
__LOG__ << "Was " << (uint)(len / 1000) << " KB, is now " << (uint)(clen / 1000) << " KB (" << (uint)(clen * 100 / len) <<"%).\n";
|
||||
|
||||
handle->Write((const void *)out, clen);
|
||||
add_raw = false;
|
||||
}
|
||||
else
|
||||
if (verbose)
|
||||
__LOG__ << "No compression achieved for '" << alias << "', adding uncompressed.\n";
|
||||
}
|
||||
|
||||
if (add_raw)
|
||||
{
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <uchar> (0);
|
||||
__CorrectOffsetPadding
|
||||
handle->Write <uint> (len);
|
||||
|
||||
// Write raw block.
|
||||
__CorrectOffsetPadding
|
||||
|
||||
entry->method = ArchiveEntry::MethodRaw;
|
||||
entry->offset = handle->Tell();
|
||||
entry->length = len;
|
||||
entry->compressed_length = 0;
|
||||
|
||||
handle->Write((const void *)in, len);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
ArchiveEntry *Archive::FileWrite(const char *path, const char *alias, int level)
|
||||
{
|
||||
Array <char> data;
|
||||
if (!Platform::Get().io->FileLoad(path, data))
|
||||
return NULL;
|
||||
return MemoryBlockWrite(alias ? alias : path, &data[0], data.GetSize(), level);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Archive::Archive()
|
||||
{
|
||||
append_mode = false;
|
||||
offset_padding = 0;
|
||||
size_padding = 0;
|
||||
|
||||
access_mutex = new Threading::Mutex;
|
||||
}
|
||||
Archive::~Archive()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user