PrimeWorldEditor/Resource/CPakFile.cpp

175 lines
4.4 KiB
C++

#include "CPakFile.h"
#include <Common/types.h>
#include <FileIO/CMemoryInStream.h>
#include <FileIO/FileIO.h>
#include <zlib.h>
#include <lzo/lzo1x.h>
#include <iostream>
#include <iomanip>
CPakFile::CPakFile()
{
pak = nullptr;
}
CPakFile::CPakFile(CInputStream* pakfile)
{
pak = pakfile;
if (!pak->IsValid()) return;
version = pak->ReadLong();
pak->Seek(0x4, SEEK_CUR);
u32 namedResCount = pak->ReadLong();
NamedResTable.resize(namedResCount);
for (u32 n = 0; n < namedResCount; n++)
{
SNamedResource *res = &NamedResTable[n];
res->resType = CFourCC(*pak);
res->resID = (u64) pak->ReadLong();
u32 resNameLength = pak->ReadLong();
res->resName = pak->ReadString(resNameLength);
}
u32 resCount = pak->ReadLong();
ResInfoTable.resize(resCount);
for (u32 r = 0; r < resCount; r++)
{
SResInfo *res = &ResInfoTable[r];
res->compressed = (pak->ReadLong() != 0);
res->resType = CFourCC(*pak);
res->resID = (u64) pak->ReadLong();
res->size = pak->ReadLong();
res->offset = pak->ReadLong();
}
}
CPakFile::~CPakFile()
{
if (pak) delete pak;
}
std::vector<SNamedResource> CPakFile::getNamedResources()
{
return NamedResTable;
}
SResInfo CPakFile::getResourceInfo(u64 assetID, CFourCC assetType)
{
// TODO: figure out how the game finds assets in paks, implement similar system to speed things up
if (ResInfoTable.empty())
return SResInfo();
for (u32 r = 0; r < ResInfoTable.size(); r++)
{
if (((u64) (ResInfoTable[r].resID & 0xFFFFFFFF) == (u64) (assetID & 0xFFFFFFFF)) && (ResInfoTable[r].resType == assetType))
return ResInfoTable[r];
}
return SResInfo();
}
std::vector<u8>* CPakFile::getResource(u64 assetID, CFourCC assetType)
{
SResInfo info = getResourceInfo(assetID, assetType);
// make sure SResInfo is valid
if ((u64) (info.resID & 0xFFFFFFFF) != (u64) (assetID & 0xFFFFFFFF)) return nullptr;
else return getResource(info);
}
std::vector<u8>* CPakFile::getResource(SResInfo& info)
{
pak->Seek(info.offset, SEEK_SET);
std::vector<u8> *res_buf = new std::vector<u8>;
if (info.compressed)
{
u32 decmp_size = pak->ReadLong();
res_buf->resize(decmp_size);
std::vector<u8> cmp_buf(info.size - 4);
pak->ReadBytes(&cmp_buf[0], info.size - 4);
bool dcmp = decompress(cmp_buf.data(), cmp_buf.size(), res_buf->data(), res_buf->size());
if (!dcmp) {
std::cout << "Error: Unable to decompress " << info.resType.ToString() << " 0x" << std::hex << std::setw(8) << std::setfill('0') << info.resID << std::dec << "\n";
delete res_buf;
return nullptr;
}
}
else {
res_buf->resize(info.size);
pak->ReadBytes(res_buf->data(), info.size);
}
return res_buf;
}
bool CPakFile::decompress(u8 *src, u32 src_len, u8 *dst, u32 dst_len)
{
if ((src[0] == 0x78) && (src[1] == 0xda))
{
// zlib
z_stream z;
z.zalloc = Z_NULL;
z.zfree = Z_NULL;
z.opaque = Z_NULL;
z.avail_in = src_len;
z.next_in = src;
z.avail_out = dst_len;
z.next_out = dst;
s32 ret = inflateInit(&z);
if (ret == Z_OK)
{
ret = inflate(&z, Z_NO_FLUSH);
if ((ret == Z_OK) || (ret == Z_STREAM_END))
ret = inflateEnd(&z);
}
if ((ret != Z_OK) && (ret != Z_STREAM_END)) {
std::cout << "zlib error: " << std::dec << ret << "\n";
return false;
}
else return true;
}
else {
// LZO
lzo_uint decmp;
s32 ret;
u8 *src_end = src + src_len;
u8 *dst_end = dst + dst_len;
lzo_init();
while ((src < src_end) && (dst < dst_end)) {
short block_size;
memcpy(&block_size, src, 2);
if (IOUtil::SystemEndianness == IOUtil::LittleEndian) IOUtil::SwapBytes(block_size);
src += 2;
ret = lzo1x_decompress(src, block_size, dst, &decmp, LZO1X_MEM_DECOMPRESS);
if (ret != LZO_E_OK) break;
src += block_size;
dst += decmp;
}
if (ret != LZO_E_OK) {
std::cout << "LZO error: " << std::dec << ret << "\n";
return false;
}
else return true;
}
return false;
}