/* * 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 "StdAfx.h" #include "ChunkFileWriters.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(ICryPak* 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