644 lines
15 KiB
C++
644 lines
15 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#include <platform.h>
|
|
#include "ChunkFileWriters.h"
|
|
#include <AzFramework/Archive/IArchive.h>
|
|
|
|
static inline size_t ComputeSizeOfAlignment(size_t pos, size_t alignment)
|
|
{
|
|
if (alignment <= 1 || (alignment & (alignment - 1)))
|
|
{
|
|
return 0;
|
|
}
|
|
const size_t mask = alignment - 1;
|
|
return (alignment - (pos & mask)) & mask;
|
|
}
|
|
|
|
|
|
namespace ChunkFile
|
|
{
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
bool IWriter::WriteZeros(size_t size)
|
|
{
|
|
if (size <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
char bf[1024];
|
|
memset(bf, 0, (sizeof(bf) < size ? sizeof(bf) : size));
|
|
|
|
while (size > 0)
|
|
{
|
|
const uint32 sz = (sizeof(bf) < size ? sizeof(bf) : size);
|
|
size -= sz;
|
|
if (!Write(bf, sz))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
OsFileWriter::OsFileWriter()
|
|
: m_f(0)
|
|
{
|
|
}
|
|
|
|
OsFileWriter::~OsFileWriter()
|
|
{
|
|
Erase();
|
|
}
|
|
|
|
bool OsFileWriter::Create(const char* filename)
|
|
{
|
|
Erase();
|
|
|
|
if (filename == 0 || filename[0] == 0)
|
|
{
|
|
// RCLogError("Filename is empty");
|
|
return false;
|
|
}
|
|
|
|
m_filename = filename;
|
|
|
|
m_f = nullptr;
|
|
azfopen(&m_f, filename, "wb");
|
|
if (!m_f)
|
|
{
|
|
// RCLogError("Failed to create file %s.", m_filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
m_offset = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void OsFileWriter::Erase()
|
|
{
|
|
if (m_f)
|
|
{
|
|
Close();
|
|
::remove(m_filename.c_str());
|
|
}
|
|
}
|
|
|
|
void OsFileWriter::Close()
|
|
{
|
|
if (m_f)
|
|
{
|
|
fclose(m_f);
|
|
m_f = 0;
|
|
}
|
|
}
|
|
|
|
int32 OsFileWriter::GetPos() const
|
|
{
|
|
return m_offset;
|
|
}
|
|
|
|
bool OsFileWriter::Write(const void* buffer, size_t size)
|
|
{
|
|
if (!m_f)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (size <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (fwrite(buffer, size, 1, m_f) != 1)
|
|
{
|
|
// RCLogError("Failed to write %u byte(s) to file %s.", (uint)size, m_filename.c_str());
|
|
Erase();
|
|
return false;
|
|
}
|
|
|
|
m_offset += size;
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
#if !defined(RESOURCE_COMPILER)
|
|
|
|
CryPakFileWriter::CryPakFileWriter()
|
|
: m_pPak(0)
|
|
, m_fileHandle(AZ::IO::InvalidHandle)
|
|
{
|
|
}
|
|
|
|
CryPakFileWriter::~CryPakFileWriter()
|
|
{
|
|
Erase();
|
|
}
|
|
|
|
bool CryPakFileWriter::Create(AZ::IO::IArchive* pPak, const char* filename)
|
|
{
|
|
Erase();
|
|
|
|
if (pPak == 0 || filename == 0 || filename[0] == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_pPak = pPak;
|
|
m_filename = filename;
|
|
|
|
m_fileHandle = m_pPak->FOpen(m_filename.c_str(), "w+b");
|
|
|
|
if (m_fileHandle == AZ::IO::InvalidHandle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_offset = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void CryPakFileWriter::Erase()
|
|
{
|
|
if (m_fileHandle != AZ::IO::InvalidHandle)
|
|
{
|
|
Close();
|
|
m_pPak->RemoveFile(m_filename.c_str());
|
|
}
|
|
}
|
|
|
|
void CryPakFileWriter::Close()
|
|
{
|
|
if (m_fileHandle != AZ::IO::InvalidHandle)
|
|
{
|
|
m_pPak->FClose(m_fileHandle);
|
|
m_fileHandle = AZ::IO::InvalidHandle;
|
|
}
|
|
}
|
|
|
|
int32 CryPakFileWriter::GetPos() const
|
|
{
|
|
return m_offset;
|
|
}
|
|
|
|
bool CryPakFileWriter::Write(const void* buffer, size_t size)
|
|
{
|
|
if (m_fileHandle == AZ::IO::InvalidHandle)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (size <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (m_pPak->FWrite(buffer, 1, size, m_fileHandle) != size)
|
|
{
|
|
Erase();
|
|
return false;
|
|
}
|
|
|
|
m_offset += size;
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
MemoryWriter::MemoryWriter()
|
|
: m_ptr(0)
|
|
, m_size(0)
|
|
{
|
|
}
|
|
|
|
MemoryWriter::~MemoryWriter()
|
|
{
|
|
}
|
|
|
|
bool MemoryWriter::Start(void* ptr, int32 size)
|
|
{
|
|
Erase();
|
|
|
|
if (ptr == 0 || size <= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
m_ptr = (char*)ptr;
|
|
m_size = size;
|
|
m_offset = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void MemoryWriter::Erase()
|
|
{
|
|
m_ptr = 0;
|
|
m_size = 0;
|
|
}
|
|
|
|
void MemoryWriter::Close()
|
|
{
|
|
m_ptr = 0;
|
|
m_size = 0;
|
|
}
|
|
|
|
int32 MemoryWriter::GetPos() const
|
|
{
|
|
return m_offset;
|
|
}
|
|
|
|
bool MemoryWriter::Write(const void* buffer, size_t size)
|
|
{
|
|
if (!m_ptr)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (size <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ((size_t)m_offset + size > (size_t)m_size)
|
|
{
|
|
Erase();
|
|
return false;
|
|
}
|
|
|
|
memcpy(&m_ptr[m_offset], buffer, size);
|
|
m_offset += size;
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
MemorylessChunkFileWriter::MemorylessChunkFileWriter(
|
|
EChunkFileFormat eFormat,
|
|
IWriter* pWriter)
|
|
: m_eChunkFileFormat(eFormat)
|
|
, m_pWriter(pWriter)
|
|
, m_alignment(4)
|
|
, m_chunkCount(0)
|
|
, m_eState(eState_Init)
|
|
{
|
|
if (!m_pWriter)
|
|
{
|
|
m_eState = eState_Fail;
|
|
}
|
|
}
|
|
|
|
MemorylessChunkFileWriter::~MemorylessChunkFileWriter()
|
|
{
|
|
if (m_eState != eState_Success)
|
|
{
|
|
Fail();
|
|
}
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::SetAlignment(size_t alignment)
|
|
{
|
|
m_alignment = (alignment < 1) ? 1 : alignment;
|
|
}
|
|
|
|
bool MemorylessChunkFileWriter::StartPass()
|
|
{
|
|
switch (m_eState)
|
|
{
|
|
case eState_Init:
|
|
m_eState = eState_CountingChunks;
|
|
m_chunkIndex = -1;
|
|
break;
|
|
case eState_CountingChunks:
|
|
WriteFileHeader(m_chunkIndex + 1, GetSizeOfHeader());
|
|
m_eState = eState_WritingChunkTable;
|
|
WriteChunkTableHeader(m_chunkIndex + 1);
|
|
m_dataOffsetInFile = GetSizeOfHeader() + GetSizeOfChunkTable(m_chunkIndex + 1);
|
|
m_chunkIndex = -1;
|
|
break;
|
|
case eState_WritingChunkTable:
|
|
if (m_chunkIndex >= 0)
|
|
{
|
|
WriteChunkEntry();
|
|
}
|
|
m_eState = eState_WritingData;
|
|
m_dataOffsetInFile = GetSizeOfHeader() + GetSizeOfChunkTable(m_chunkIndex + 1);
|
|
m_chunkIndex = -1;
|
|
break;
|
|
case eState_WritingData:
|
|
m_eState = eState_Success;
|
|
m_pWriter->Close();
|
|
return false;
|
|
case eState_Fail:
|
|
return false;
|
|
default:
|
|
assert(0);
|
|
Fail();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::StartChunk(EEndianness eEndianness, uint32 type, uint32 version, uint32 id)
|
|
{
|
|
if (type != 0)
|
|
{
|
|
type = ConvertChunkTypeTo0x746(type);
|
|
if (type == 0)
|
|
{
|
|
Fail();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (version >= ChunkFile::ChunkTableEntry_0x746::kBigEndianVersionFlag)
|
|
{
|
|
Fail();
|
|
return;
|
|
}
|
|
|
|
switch (m_eState)
|
|
{
|
|
case eState_CountingChunks:
|
|
++m_chunkIndex;
|
|
break;
|
|
case eState_WritingChunkTable:
|
|
if (m_chunkIndex >= 0)
|
|
{
|
|
WriteChunkEntry();
|
|
}
|
|
/* fall through */
|
|
case eState_WritingData:
|
|
{
|
|
size_t size = ComputeSizeOfAlignment(m_dataOffsetInFile, m_alignment);
|
|
|
|
// Make sure that zero-length chunks have distinct positions in file
|
|
if (size == 0 && m_chunkIndex > 0 && m_chunkSize == 0)
|
|
{
|
|
size = m_alignment;
|
|
}
|
|
|
|
m_dataOffsetInFile += size;
|
|
m_chunkOffsetInFile = m_dataOffsetInFile;
|
|
if (m_eState == eState_WritingData && !m_pWriter->WriteZeros(size))
|
|
{
|
|
Fail();
|
|
return;
|
|
}
|
|
}
|
|
|
|
++m_chunkIndex;
|
|
m_chunkEndianness = eEndianness;
|
|
m_chunkType = type;
|
|
m_chunkVersion = version;
|
|
m_chunkId = id;
|
|
m_chunkSize = 0;
|
|
|
|
if (m_eChunkFileFormat == eChunkFileFormat_0x745 &&
|
|
ChunkContainsHeader_0x744_0x745(m_chunkType, m_chunkVersion))
|
|
{
|
|
ChunkHeader_0x744_0x745 c;
|
|
|
|
c.type = ConvertChunkTypeTo0x745(m_chunkType);
|
|
c.version = m_chunkVersion | (m_chunkEndianness == eEndianness_Big ? ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag : 0);
|
|
c.id = m_chunkId;
|
|
c.offsetInFile = m_chunkOffsetInFile;
|
|
|
|
if (SYSTEM_IS_BIG_ENDIAN)
|
|
{
|
|
c.SwapEndianness();
|
|
}
|
|
|
|
AddChunkData(&c, sizeof(c));
|
|
}
|
|
break;
|
|
default:
|
|
Fail();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::AddChunkData(void* ptr, size_t size)
|
|
{
|
|
if (m_chunkIndex < 0)
|
|
{
|
|
Fail();
|
|
return;
|
|
}
|
|
|
|
switch (m_eState)
|
|
{
|
|
case eState_CountingChunks:
|
|
break;
|
|
case eState_WritingChunkTable:
|
|
case eState_WritingData:
|
|
m_chunkSize += size;
|
|
m_dataOffsetInFile += size;
|
|
if (m_eState == eState_WritingData && !m_pWriter->Write(ptr, size))
|
|
{
|
|
Fail();
|
|
}
|
|
break;
|
|
default:
|
|
Fail();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::AddChunkDataZeros(size_t size)
|
|
{
|
|
if (m_chunkIndex < 0)
|
|
{
|
|
Fail();
|
|
return;
|
|
}
|
|
|
|
switch (m_eState)
|
|
{
|
|
case eState_CountingChunks:
|
|
break;
|
|
case eState_WritingChunkTable:
|
|
case eState_WritingData:
|
|
m_chunkSize += size;
|
|
m_dataOffsetInFile += size;
|
|
if (m_eState == eState_WritingData && !m_pWriter->WriteZeros(size))
|
|
{
|
|
Fail();
|
|
}
|
|
break;
|
|
default:
|
|
Fail();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::AddChunkDataAlignment(size_t alignment)
|
|
{
|
|
const size_t size = ComputeSizeOfAlignment(m_chunkSize, alignment);
|
|
return AddChunkDataZeros(size);
|
|
}
|
|
|
|
bool MemorylessChunkFileWriter::HasWrittenSuccessfully() const
|
|
{
|
|
return m_eState == eState_Success;
|
|
}
|
|
|
|
IWriter* MemorylessChunkFileWriter::GetWriter() const
|
|
{
|
|
return m_pWriter;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void MemorylessChunkFileWriter::Fail()
|
|
{
|
|
m_eState = eState_Fail;
|
|
if (m_pWriter)
|
|
{
|
|
m_pWriter->Erase();
|
|
}
|
|
}
|
|
|
|
size_t MemorylessChunkFileWriter::GetSizeOfHeader() const
|
|
{
|
|
return (m_eChunkFileFormat == eChunkFileFormat_0x745)
|
|
? sizeof(FileHeader_0x744_0x745)
|
|
: sizeof(FileHeader_0x746);
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::WriteFileHeader(int32 chunkCount, uint32 chunkTableOffsetInFile)
|
|
{
|
|
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
|
|
{
|
|
FileHeader_0x744_0x745 h;
|
|
h.Set(chunkTableOffsetInFile);
|
|
|
|
if (SYSTEM_IS_BIG_ENDIAN)
|
|
{
|
|
h.SwapEndianness();
|
|
}
|
|
|
|
if (!m_pWriter->Write(&h, sizeof(h)))
|
|
{
|
|
Fail();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FileHeader_0x746 h;
|
|
h.Set(chunkCount, chunkTableOffsetInFile);
|
|
|
|
if (SYSTEM_IS_BIG_ENDIAN)
|
|
{
|
|
h.SwapEndianness();
|
|
}
|
|
|
|
if (!m_pWriter->Write(&h, sizeof(h)))
|
|
{
|
|
Fail();
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t MemorylessChunkFileWriter::GetSizeOfChunkTable(int32 chunkCount) const
|
|
{
|
|
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
|
|
{
|
|
return sizeof(uint32) + chunkCount * sizeof(ChunkTableEntry_0x745);
|
|
}
|
|
else
|
|
{
|
|
return chunkCount * sizeof(ChunkTableEntry_0x746);
|
|
}
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::WriteChunkTableHeader(int32 chunkCount)
|
|
{
|
|
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
|
|
{
|
|
if (SYSTEM_IS_BIG_ENDIAN)
|
|
{
|
|
SwapEndianBase(&chunkCount, 1);
|
|
}
|
|
|
|
if (!m_pWriter->Write(&chunkCount, sizeof(chunkCount)))
|
|
{
|
|
Fail();
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemorylessChunkFileWriter::WriteChunkEntry()
|
|
{
|
|
if (m_chunkIndex < 0)
|
|
{
|
|
assert(0);
|
|
Fail();
|
|
return;
|
|
}
|
|
|
|
if (m_eChunkFileFormat == eChunkFileFormat_0x745)
|
|
{
|
|
ChunkTableEntry_0x745 c;
|
|
|
|
c.type = ConvertChunkTypeTo0x745(m_chunkType);
|
|
c.version = m_chunkVersion | (m_chunkEndianness == eEndianness_Big ? ChunkFile::ChunkHeader_0x744_0x745::kBigEndianVersionFlag : 0);
|
|
c.id = m_chunkId;
|
|
c.size = m_chunkSize;
|
|
c.offsetInFile = m_chunkOffsetInFile;
|
|
|
|
if (SYSTEM_IS_BIG_ENDIAN)
|
|
{
|
|
c.SwapEndianness();
|
|
}
|
|
|
|
if (!m_pWriter->Write(&c, sizeof(c)))
|
|
{
|
|
Fail();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ChunkTableEntry_0x746 c;
|
|
|
|
c.type = m_chunkType;
|
|
c.version = m_chunkVersion | (m_chunkEndianness == eEndianness_Big ? ChunkFile::ChunkTableEntry_0x746::kBigEndianVersionFlag : 0);
|
|
c.id = m_chunkId;
|
|
c.size = m_chunkSize;
|
|
c.offsetInFile = m_chunkOffsetInFile;
|
|
|
|
if (SYSTEM_IS_BIG_ENDIAN)
|
|
{
|
|
c.SwapEndianness();
|
|
}
|
|
|
|
if (!m_pWriter->Write(&c, sizeof(c)))
|
|
{
|
|
Fail();
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
} // namespace ChunkFile
|