Files
o3de/Code/CryEngine/Cry3DEngine/CGF/ChunkFileWriters.cpp
T
2021-03-08 14:30:57 -08:00

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