Fix infinite loop detection when placing players randomly on the newer random map...
[0ad.git] / source / ps / FileIo.cpp
blobf8c358e76606a242d7216d6bda6da3a0f8e617a8
1 /* Copyright (C) 2013 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * endian-safe binary file IO helpers.
22 #include "precompiled.h"
24 #include "FileIo.h"
25 #include "ps/CLogger.h"
26 #include "ps/Filesystem.h"
27 #include "lib/byte_order.h"
29 #pragma pack(push, 1)
31 struct FileHeader
33 char magic[4];
34 u32 version_le;
35 u32 payloadSize_le; // = file size - sizeof(FileHeader)
37 cassert(sizeof(FileHeader) == 12);
39 #pragma pack(pop)
42 //-----------------------------------------------------------------------------
43 // CFilePacker
45 CFilePacker::CFilePacker(u32 version, const char magic[4])
47 // put header in our data array.
48 // (its payloadSize_le will be updated on every Pack*() call)
49 FileHeader header;
50 strncpy(header.magic, magic, 4); // not 0-terminated => no _s
51 write_le32(&header.version_le, version);
52 write_le32(&header.payloadSize_le, 0);
53 m_writeBuffer.Append(&header, sizeof(FileHeader));
57 CFilePacker::~CFilePacker()
62 void CFilePacker::Write(const VfsPath& filename)
64 const size_t payloadSize = m_writeBuffer.Size() - sizeof(FileHeader);
65 const u32 payloadSize_le = to_le32(u32_from_larger(payloadSize));
66 m_writeBuffer.Overwrite(&payloadSize_le, sizeof(payloadSize_le), 0+offsetof(FileHeader, payloadSize_le));
68 // write out all data (including header)
69 const Status st = g_VFS->CreateFile(filename, m_writeBuffer.Data(), m_writeBuffer.Size());
70 if (st < 0)
72 LOGERROR("Failed to write file '%s' with status '%lld'", filename.string8(), (long long)st);
73 throw PSERROR_File_WriteFailed();
78 void CFilePacker::PackRaw(const void* rawData, size_t rawSize)
80 m_writeBuffer.Append(rawData, rawSize);
83 void CFilePacker::PackSize(size_t value)
85 const u32 value_le32 = to_le32(u32_from_larger(value));
86 PackRaw(&value_le32, sizeof(value_le32));
89 void CFilePacker::PackString(const CStr& str)
91 const size_t length = str.length();
92 PackSize(length);
93 PackRaw(str.c_str(), length);
97 //-----------------------------------------------------------------------------
98 // CFileUnpacker
100 CFileUnpacker::CFileUnpacker()
101 : m_bufSize(0), m_unpackPos(0), m_version(0)
106 CFileUnpacker::~CFileUnpacker()
111 void CFileUnpacker::Read(const VfsPath& filename, const char magic[4])
113 // if the file doesn't exist, LoadFile would raise an annoying error dialog.
114 // since this (unfortunately) happens so often, we perform a separate
115 // check and "just" raise an exception for the caller to handle.
116 // (this is nicer than somehow squelching internal VFS error reporting)
117 if(!VfsFileExists(filename))
118 throw PSERROR_File_OpenFailed();
120 // load the whole thing into memory
121 if(g_VFS->LoadFile(filename, m_buf, m_bufSize) < 0)
122 throw PSERROR_File_OpenFailed();
124 // make sure we read enough for the header
125 if(m_bufSize < sizeof(FileHeader))
127 m_buf.reset();
128 m_bufSize = 0;
129 throw PSERROR_File_ReadFailed();
132 // extract data from header
133 FileHeader* header = (FileHeader*)m_buf.get();
134 m_version = read_le32(&header->version_le);
135 const size_t payloadSize = (size_t)read_le32(&header->payloadSize_le);
137 // check we've got the right kind of file
138 // .. and that we read exactly headerSize+payloadSize
139 if(strncmp(header->magic, magic, 4) != 0 || m_bufSize != sizeof(FileHeader)+payloadSize)
141 m_buf.reset();
142 m_bufSize = 0;
143 throw PSERROR_File_InvalidType();
146 m_unpackPos = sizeof(FileHeader);
150 void CFileUnpacker::UnpackRaw(void* rawData, size_t rawDataSize)
152 // fail if reading past end of stream
153 if (m_unpackPos+rawDataSize > m_bufSize)
154 throw PSERROR_File_UnexpectedEOF();
156 void* src = m_buf.get() + m_unpackPos;
157 memcpy(rawData, src, rawDataSize);
158 m_unpackPos += rawDataSize;
162 size_t CFileUnpacker::UnpackSize()
164 u32 value_le32;
165 UnpackRaw(&value_le32, sizeof(value_le32));
166 return (size_t)to_le32(value_le32);
170 void CFileUnpacker::UnpackString(CStr8& result)
172 const size_t length = UnpackSize();
174 // fail if reading past end of stream
175 if (m_unpackPos+length > m_bufSize)
176 throw PSERROR_File_UnexpectedEOF();
178 result = CStr((char*)m_buf.get()+m_unpackPos, length);
179 m_unpackPos += length;