/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program 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 2 of the License, or (at your
* option) any later version.
*
* This program 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 this program. If not, see .
*/
#define _CRT_SECURE_NO_DEPRECATE
#include "loadlib.h"
#include
u_map_fcc MverMagic = { { 'R','E','V','M' } };
ChunkedFile::ChunkedFile()
{
data = nullptr;
data_size = 0;
}
ChunkedFile::~ChunkedFile()
{
free();
}
bool ChunkedFile::loadFile(std::shared_ptr mpq, std::string const& fileName, bool log)
{
free();
std::unique_ptr file(mpq->OpenFile(fileName.c_str(), CASC_LOCALE_ALL_WOW, log));
if (!file)
return false;
int64 fileSize = file->GetSize();
if (fileSize == -1)
return false;
data_size = uint32(fileSize);
data = new uint8[data_size];
uint32 bytesRead = 0;
if (!file->ReadFile(data, data_size, &bytesRead) || bytesRead != data_size)
return false;
parseChunks();
if (prepareLoadedData())
return true;
printf("Error loading %s\n", fileName.c_str());
free();
return false;
}
bool ChunkedFile::loadFile(std::shared_ptr mpq, uint32 fileDataId, std::string const& description, bool log)
{
free();
std::unique_ptr file(mpq->OpenFile(fileDataId, CASC_LOCALE_ALL_WOW, log));
if (!file)
return false;
int64 fileSize = file->GetSize();
if (fileSize == -1)
return false;
data_size = fileSize;
data = new uint8[data_size];
uint32 bytesRead = 0;
if (!file->ReadFile(data, data_size, &bytesRead) || bytesRead != data_size)
return false;
parseChunks();
if (prepareLoadedData())
return true;
printf("Error loading %s\n", description.c_str());
free();
return false;
}
bool ChunkedFile::prepareLoadedData()
{
FileChunk* chunk = GetChunk("MVER");
if (!chunk)
return false;
// Check version
file_MVER* version = chunk->As();
if (version->fcc != MverMagic.fcc)
return false;
if (version->ver != FILE_FORMAT_VERSION)
return false;
return true;
}
void ChunkedFile::free()
{
for (auto& chunk : chunks)
delete chunk.second;
chunks.clear();
delete[] data;
data = nullptr;
data_size = 0;
}
u_map_fcc InterestingChunks[] =
{
{ { 'R', 'E', 'V', 'M' } },
{ { 'N', 'I', 'A', 'M' } },
{ { 'O', '2', 'H', 'M' } },
{ { 'K', 'N', 'C', 'M' } },
{ { 'T', 'V', 'C', 'M' } },
{ { 'O', 'M', 'W', 'M' } },
{ { 'Q', 'L', 'C', 'M' } },
{ { 'O', 'B', 'F', 'M' } },
{ { 'D', 'H', 'P', 'M' } },
{ { 'D', 'I', 'A', 'M' } }
};
bool IsInterestingChunk(u_map_fcc const& fcc)
{
for (u_map_fcc const& f : InterestingChunks)
if (f.fcc == fcc.fcc)
return true;
return false;
}
void ChunkedFile::parseChunks()
{
uint8* ptr = GetData();
// Make sure there's enough data to read u_map_fcc struct and the uint32 size after it
while (ptr <= GetData() + GetDataSize() - 8)
{
u_map_fcc header = *(u_map_fcc*)ptr;
if (IsInterestingChunk(header))
{
uint32 size = *(uint32*)(ptr + 4);
if (size <= data_size)
{
std::swap(header.fcc_txt[0], header.fcc_txt[3]);
std::swap(header.fcc_txt[1], header.fcc_txt[2]);
FileChunk* chunk = new FileChunk(ptr, size);
chunk->parseSubChunks();
chunks.insert({ std::string(header.fcc_txt, 4), chunk });
}
// move to next chunk
ptr += size + 8;
}
else
++ptr;
}
}
FileChunk* ChunkedFile::GetChunk(std::string const& name)
{
auto range = chunks.equal_range(name);
if (std::distance(range.first, range.second) == 1)
return range.first->second;
return nullptr;
}
FileChunk::~FileChunk()
{
for (auto& subchunk : subchunks)
delete subchunk.second;
subchunks.clear();
}
void FileChunk::parseSubChunks()
{
uint8* ptr = data + 8; // skip self
while (ptr < data + size)
{
u_map_fcc header = *(u_map_fcc*)ptr;
if (IsInterestingChunk(header))
{
uint32 subsize = *(uint32*)(ptr + 4);
if (subsize < size)
{
std::swap(header.fcc_txt[0], header.fcc_txt[3]);
std::swap(header.fcc_txt[1], header.fcc_txt[2]);
FileChunk* chunk = new FileChunk(ptr, subsize);
chunk->parseSubChunks();
subchunks.insert({ std::string(header.fcc_txt, 4), chunk });
}
// move to next chunk
ptr += subsize + 8;
}
else
++ptr;
}
}
FileChunk* FileChunk::GetSubChunk(std::string const& name)
{
auto range = subchunks.equal_range(name);
if (std::distance(range.first, range.second) == 1)
return range.first->second;
return nullptr;
}