1 /* Copyright (C) 2019 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"
25 #include "ps/CLogger.h"
27 #include "ps/Filesystem.h"
28 #include "lib/byte_order.h"
36 u32 payloadSize_le
; // = file size - sizeof(FileHeader)
38 cassert(sizeof(FileHeader
) == 12);
43 //-----------------------------------------------------------------------------
46 CFilePacker::CFilePacker(u32 version
, const char magic
[4])
48 // put header in our data array.
49 // (its payloadSize_le will be updated on every Pack*() call)
51 std::copy(magic
, magic
+ 4, header
.magic
);
52 write_le32(&header
.version_le
, version
);
53 write_le32(&header
.payloadSize_le
, 0);
54 m_writeBuffer
.Append(&header
, sizeof(FileHeader
));
58 CFilePacker::~CFilePacker()
63 void CFilePacker::Write(const VfsPath
& filename
)
65 const size_t payloadSize
= m_writeBuffer
.Size() - sizeof(FileHeader
);
66 const u32 payloadSize_le
= to_le32(u32_from_larger(payloadSize
));
67 m_writeBuffer
.Overwrite(&payloadSize_le
, sizeof(payloadSize_le
), 0+offsetof(FileHeader
, payloadSize_le
));
69 // write out all data (including header)
70 const Status st
= g_VFS
->CreateFile(filename
, m_writeBuffer
.Data(), m_writeBuffer
.Size());
73 LOGERROR("Failed to write file '%s' with status '%lld'", filename
.string8(), (long long)st
);
74 throw PSERROR_File_WriteFailed();
79 void CFilePacker::PackRaw(const void* rawData
, size_t rawSize
)
81 m_writeBuffer
.Append(rawData
, rawSize
);
84 void CFilePacker::PackSize(size_t value
)
86 const u32 value_le32
= to_le32(u32_from_larger(value
));
87 PackRaw(&value_le32
, sizeof(value_le32
));
90 void CFilePacker::PackString(const CStr
& str
)
92 const size_t length
= str
.length();
94 PackRaw(str
.c_str(), length
);
98 //-----------------------------------------------------------------------------
101 CFileUnpacker::CFileUnpacker()
102 : m_bufSize(0), m_unpackPos(0), m_version(0)
107 CFileUnpacker::~CFileUnpacker()
112 void CFileUnpacker::Read(const VfsPath
& filename
, const char magic
[4])
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 if(!VfsFileExists(filename
))
119 throw PSERROR_File_OpenFailed();
121 // load the whole thing into memory
122 if(g_VFS
->LoadFile(filename
, m_buf
, m_bufSize
) < 0)
123 throw PSERROR_File_OpenFailed();
125 // make sure we read enough for the header
126 if(m_bufSize
< sizeof(FileHeader
))
130 throw PSERROR_File_ReadFailed();
133 // extract data from header
134 FileHeader
* header
= (FileHeader
*)m_buf
.get();
135 m_version
= read_le32(&header
->version_le
);
136 const size_t payloadSize
= (size_t)read_le32(&header
->payloadSize_le
);
138 // check we've got the right kind of file
139 // .. and that we read exactly headerSize+payloadSize
140 if(strncmp(header
->magic
, magic
, 4) != 0 || m_bufSize
!= sizeof(FileHeader
)+payloadSize
)
144 throw PSERROR_File_InvalidType();
147 m_unpackPos
= sizeof(FileHeader
);
151 void CFileUnpacker::UnpackRaw(void* rawData
, size_t rawDataSize
)
153 // fail if reading past end of stream
154 if (m_unpackPos
+rawDataSize
> m_bufSize
)
155 throw PSERROR_File_UnexpectedEOF();
157 void* src
= m_buf
.get() + m_unpackPos
;
158 memcpy(rawData
, src
, rawDataSize
);
159 m_unpackPos
+= rawDataSize
;
163 size_t CFileUnpacker::UnpackSize()
166 UnpackRaw(&value_le32
, sizeof(value_le32
));
167 return (size_t)to_le32(value_le32
);
171 void CFileUnpacker::UnpackString(CStr8
& result
)
173 const size_t length
= UnpackSize();
175 // fail if reading past end of stream
176 if (m_unpackPos
+length
> m_bufSize
)
177 throw PSERROR_File_UnexpectedEOF();
179 result
= CStr((char*)m_buf
.get()+m_unpackPos
, length
);
180 m_unpackPos
+= length
;