Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
FileIo.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
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.
8  *
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.
13  *
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/>.
16  */
17 
18 /*
19  * endian-safe binary file IO helpers.
20  */
21 
22 #include "precompiled.h"
23 
24 #include "FileIo.h"
25 #include "ps/CLogger.h"
26 #include "ps/Filesystem.h"
27 #include "lib/byte_order.h"
28 
29 #pragma pack(push, 1)
30 
31 struct FileHeader
32 {
33  char magic[4];
35  u32 payloadSize_le; // = file size - sizeof(FileHeader)
36 };
37 cassert(sizeof(FileHeader) == 12);
38 
39 #pragma pack(pop)
40 
41 
42 //-----------------------------------------------------------------------------
43 // CFilePacker
44 
45 CFilePacker::CFilePacker(u32 version, const char magic[4])
46 {
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));
54 }
55 
56 
58 {
59 }
60 
61 
62 void CFilePacker::Write(const VfsPath& filename)
63 {
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));
67 
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)
71  {
72  LOGERROR(L"Failed to write file '%ls' with status '%lld'", filename.string().c_str(), (long long)st);
74  }
75 }
76 
77 
78 void CFilePacker::PackRaw(const void* rawData, size_t rawSize)
79 {
80  m_writeBuffer.Append(rawData, rawSize);
81 }
82 
83 void CFilePacker::PackSize(size_t value)
84 {
85  const u32 value_le32 = to_le32(u32_from_larger(value));
86  PackRaw(&value_le32, sizeof(value_le32));
87 }
88 
89 void CFilePacker::PackString(const CStr& str)
90 {
91  const size_t length = str.length();
92  PackSize(length);
93  PackRaw(str.c_str(), length);
94 }
95 
96 
97 //-----------------------------------------------------------------------------
98 // CFileUnpacker
99 
101  : m_bufSize(0), m_unpackPos(0), m_version(0)
102 {
103 }
104 
105 
107 {
108 }
109 
110 
111 void CFileUnpacker::Read(const VfsPath& filename, const char magic[4])
112 {
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();
119 
120  // load the whole thing into memory
121  if(g_VFS->LoadFile(filename, m_buf, m_bufSize) < 0)
122  throw PSERROR_File_OpenFailed();
123 
124  // make sure we read enough for the header
125  if(m_bufSize < sizeof(FileHeader))
126  {
127  m_buf.reset();
128  m_bufSize = 0;
129  throw PSERROR_File_ReadFailed();
130  }
131 
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);
136 
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)
140  {
141  m_buf.reset();
142  m_bufSize = 0;
143  throw PSERROR_File_InvalidType();
144  }
145 
146  m_unpackPos = sizeof(FileHeader);
147 }
148 
149 
150 void CFileUnpacker::UnpackRaw(void* rawData, size_t rawDataSize)
151 {
152  // fail if reading past end of stream
153  if (m_unpackPos+rawDataSize > m_bufSize)
155 
156  void* src = m_buf.get() + m_unpackPos;
157  memcpy(rawData, src, rawDataSize);
158  m_unpackPos += rawDataSize;
159 }
160 
161 
163 {
164  u32 value_le32;
165  UnpackRaw(&value_le32, sizeof(value_le32));
166  return (size_t)to_le32(value_le32);
167 }
168 
169 
170 void CFileUnpacker::UnpackString(CStr8& result)
171 {
172  const size_t length = UnpackSize();
173 
174  // fail if reading past end of stream
175  if (m_unpackPos+length > m_bufSize)
177 
178  result = CStr((char*)m_buf.get()+m_unpackPos, length);
179  m_unpackPos += length;
180 }
void UnpackRaw(void *rawData, size_t rawDataSize)
unpack given number of bytes from the input into the given array.
Definition: FileIo.cpp:150
size_t Size() const
Definition: write_buffer.h:42
u32 read_le32(const void *p)
Definition: byte_order.cpp:73
void Append(const void *data, size_t size)
#define LOGERROR
Definition: CLogger.h:35
void PackRaw(const void *rawData, size_t rawDataSize)
pack given number of bytes onto the end of the data stream
Definition: FileIo.cpp:78
WriteBuffer m_writeBuffer
the output data stream built during pack operations.
Definition: FileIo.h:93
void Overwrite(const void *data, size_t size, size_t offset)
u32 payloadSize_le
Definition: FileIo.cpp:35
Definition: path.h:75
#define to_le32(x)
Definition: byte_order.h:78
shared_ptr< u8 > Data() const
Definition: write_buffer.h:37
const String & string() const
Definition: path.h:123
void Read(const VfsPath &filename, const char magic[4])
open and read in given file, check magic bits against those given; throw variety of exceptions if ope...
Definition: FileIo.cpp:111
CFilePacker(u32 version, const char magic[4])
adds version and signature (i.e.
Definition: FileIo.cpp:45
size_t UnpackSize()
use UnpackRaw to retrieve 32-bits; returns their value as size_t after converting from little endian ...
Definition: FileIo.cpp:162
void UnpackString(CStr8 &result)
unpack a string from the raw data stream.
Definition: FileIo.cpp:170
i64 Status
Error handling system.
Definition: status.h:171
size_t m_bufSize
Definition: FileIo.h:143
char magic[4]
Definition: FileIo.cpp:33
u32 u32_from_larger(T x)
Definition: lib.h:154
~CFilePacker()
Definition: FileIo.cpp:57
#define u32
Definition: types.h:41
bool VfsFileExists(const VfsPath &pathname)
Definition: Filesystem.cpp:34
void Write(const VfsPath &filename)
write out to file all packed data added so far.
Definition: FileIo.cpp:62
u32 m_version
current unpack position in stream
Definition: FileIo.h:146
void PackSize(size_t value)
convenience: convert a number (almost always a size type) to little-endian u32 and pack that...
Definition: FileIo.cpp:83
#define cassert(expr)
Compile-time assertion.
u32 version_le
Definition: FileIo.cpp:34
PIVFS g_VFS
Definition: Filesystem.cpp:30
void PackString(const CStr &str)
pack a string onto the end of the data stream (encoded as a 32-bit length followed by the characters)...
Definition: FileIo.cpp:89
shared_ptr< u8 > m_buf
Definition: FileIo.h:142
void write_le32(void *p, u32 x)
Definition: byte_order.cpp:116
size_t m_unpackPos
Definition: FileIo.h:145