Line data Source code
1 : /* Copyright (C) 2019 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/CStr.h"
27 : #include "ps/Filesystem.h"
28 : #include "lib/byte_order.h"
29 :
30 : #pragma pack(push, 1)
31 :
32 : struct FileHeader
33 : {
34 : char magic[4];
35 : u32 version_le;
36 : u32 payloadSize_le; // = file size - sizeof(FileHeader)
37 : };
38 : cassert(sizeof(FileHeader) == 12);
39 :
40 : #pragma pack(pop)
41 :
42 :
43 : //-----------------------------------------------------------------------------
44 : // CFilePacker
45 :
46 0 : CFilePacker::CFilePacker(u32 version, const char magic[4])
47 : {
48 : // put header in our data array.
49 : // (its payloadSize_le will be updated on every Pack*() call)
50 : FileHeader header;
51 0 : std::copy(magic, magic + 4, header.magic);
52 0 : write_le32(&header.version_le, version);
53 0 : write_le32(&header.payloadSize_le, 0);
54 0 : m_writeBuffer.Append(&header, sizeof(FileHeader));
55 0 : }
56 :
57 :
58 0 : CFilePacker::~CFilePacker()
59 : {
60 0 : }
61 :
62 :
63 0 : void CFilePacker::Write(const VfsPath& filename)
64 : {
65 0 : const size_t payloadSize = m_writeBuffer.Size() - sizeof(FileHeader);
66 0 : const u32 payloadSize_le = to_le32(u32_from_larger(payloadSize));
67 0 : m_writeBuffer.Overwrite(&payloadSize_le, sizeof(payloadSize_le), 0+offsetof(FileHeader, payloadSize_le));
68 :
69 : // write out all data (including header)
70 0 : const Status st = g_VFS->CreateFile(filename, m_writeBuffer.Data(), m_writeBuffer.Size());
71 0 : if (st < 0)
72 : {
73 0 : LOGERROR("Failed to write file '%s' with status '%lld'", filename.string8(), (long long)st);
74 0 : throw PSERROR_File_WriteFailed();
75 : }
76 0 : }
77 :
78 :
79 0 : void CFilePacker::PackRaw(const void* rawData, size_t rawSize)
80 : {
81 0 : m_writeBuffer.Append(rawData, rawSize);
82 0 : }
83 :
84 0 : void CFilePacker::PackSize(size_t value)
85 : {
86 0 : const u32 value_le32 = to_le32(u32_from_larger(value));
87 0 : PackRaw(&value_le32, sizeof(value_le32));
88 0 : }
89 :
90 0 : void CFilePacker::PackString(const CStr& str)
91 : {
92 0 : const size_t length = str.length();
93 0 : PackSize(length);
94 0 : PackRaw(str.c_str(), length);
95 0 : }
96 :
97 :
98 : //-----------------------------------------------------------------------------
99 : // CFileUnpacker
100 :
101 6 : CFileUnpacker::CFileUnpacker()
102 6 : : m_bufSize(0), m_unpackPos(0), m_version(0)
103 : {
104 6 : }
105 :
106 :
107 6 : CFileUnpacker::~CFileUnpacker()
108 : {
109 6 : }
110 :
111 :
112 6 : void CFileUnpacker::Read(const VfsPath& filename, const char magic[4])
113 : {
114 : // if the file doesn't exist, LoadFile would raise an annoying error dialog.
115 : // since this (unfortunately) happens so often, we perform a separate
116 : // check and "just" raise an exception for the caller to handle.
117 : // (this is nicer than somehow squelching internal VFS error reporting)
118 6 : if(!VfsFileExists(filename))
119 0 : throw PSERROR_File_OpenFailed();
120 :
121 : // load the whole thing into memory
122 6 : if(g_VFS->LoadFile(filename, m_buf, m_bufSize) < 0)
123 0 : throw PSERROR_File_OpenFailed();
124 :
125 : // make sure we read enough for the header
126 6 : if(m_bufSize < sizeof(FileHeader))
127 : {
128 0 : m_buf.reset();
129 0 : m_bufSize = 0;
130 0 : throw PSERROR_File_ReadFailed();
131 : }
132 :
133 : // extract data from header
134 6 : FileHeader* header = (FileHeader*)m_buf.get();
135 6 : m_version = read_le32(&header->version_le);
136 6 : const size_t payloadSize = (size_t)read_le32(&header->payloadSize_le);
137 :
138 : // check we've got the right kind of file
139 : // .. and that we read exactly headerSize+payloadSize
140 6 : if(strncmp(header->magic, magic, 4) != 0 || m_bufSize != sizeof(FileHeader)+payloadSize)
141 : {
142 0 : m_buf.reset();
143 0 : m_bufSize = 0;
144 0 : throw PSERROR_File_InvalidType();
145 : }
146 :
147 6 : m_unpackPos = sizeof(FileHeader);
148 6 : }
149 :
150 :
151 3029 : void CFileUnpacker::UnpackRaw(void* rawData, size_t rawDataSize)
152 : {
153 : // fail if reading past end of stream
154 3029 : if (m_unpackPos+rawDataSize > m_bufSize)
155 0 : throw PSERROR_File_UnexpectedEOF();
156 :
157 3029 : void* src = m_buf.get() + m_unpackPos;
158 3029 : memcpy(rawData, src, rawDataSize);
159 3029 : m_unpackPos += rawDataSize;
160 3029 : }
161 :
162 :
163 30 : size_t CFileUnpacker::UnpackSize()
164 : {
165 : u32 value_le32;
166 30 : UnpackRaw(&value_le32, sizeof(value_le32));
167 30 : return (size_t)to_le32(value_le32);
168 : }
169 :
170 :
171 3 : void CFileUnpacker::UnpackString(CStr8& result)
172 : {
173 3 : const size_t length = UnpackSize();
174 :
175 : // fail if reading past end of stream
176 3 : if (m_unpackPos+length > m_bufSize)
177 0 : throw PSERROR_File_UnexpectedEOF();
178 :
179 3 : result = CStr((char*)m_buf.get()+m_unpackPos, length);
180 3 : m_unpackPos += length;
181 6 : }
|