TrinityCore
Loading...
Searching...
No Matches
mpq_libmpq.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "mpq_libmpq.h"
19#include "StringFormat.h"
20#include "Util.h"
21#include <boost/filesystem/directory.hpp>
22#include <boost/filesystem/operations.hpp>
23#include <boost/filesystem/path.hpp>
24#include <algorithm>
25#include <array>
26#include <stdexcept>
27#include <cstdio>
28
30
31MPQArchive::MPQArchive(char const* filename)
32{
33 int result = libmpq__archive_open(&mpq_a, filename, -1);
34 printf("Opening %s\n", filename);
35 switch(result)
36 {
37 case LIBMPQ_ERROR_OPEN:
38 throw std::runtime_error(Trinity::StringFormat("Error opening archive '{}': Does file really exist?", filename));
39 case LIBMPQ_ERROR_FORMAT:
40 throw std::runtime_error(Trinity::StringFormat("Error opening archive '{}': Bad file format", filename));
41 case LIBMPQ_ERROR_SEEK:
42 throw std::runtime_error(Trinity::StringFormat("Error opening archive '{}': Seeking in file failed", filename));
43 case LIBMPQ_ERROR_READ:
44 throw std::runtime_error(Trinity::StringFormat("Error opening archive '{}': Read error in archive", filename));
45 case LIBMPQ_ERROR_MALLOC:
46 throw std::runtime_error(Trinity::StringFormat("Error opening archive '{}': Maybe not enough memory", filename));
47 default:
48 throw std::runtime_error(Trinity::StringFormat("Error opening archive '{}': Unknown error", filename));
49 case 0:
50 // success
51 break;
52 }
53}
54
56{
57 if (mpq_a)
58 {
59 libmpq__archive_close(mpq_a);
60 mpq_a = nullptr;
61 }
62}
63
64MPQFile::MPQFile(char const* filename):
65 eof(false),
66 buffer(nullptr),
67 pointer(0),
68 size(0)
69{
70 for (ArchiveSet::iterator i = gOpenArchives.begin(); i != gOpenArchives.end(); ++i)
71 {
72 mpq_archive *mpq_a = i->mpq_a;
73 if (!mpq_a)
74 continue;
75
76 uint32_t filenum;
77 if(libmpq__file_number(mpq_a, filename, &filenum)) continue;
78 libmpq__off_t transferred;
79 libmpq__file_size_unpacked(mpq_a, filenum, &size);
80
81 // HACK: in patch.mpq some files don't want to open and give 1 for filesize
82 if (size<=1) {
83// printf("warning: file %s has size %d; cannot read.\n", filename, size);
84 eof = true;
85 buffer = 0;
86 return;
87 }
88 buffer = new char[size];
89
90 //libmpq_file_getdata
91 libmpq__file_read(mpq_a, filenum, (unsigned char*)buffer, size, &transferred);
92 /*libmpq_file_getdata(&mpq_a, hash, fileno, (unsigned char*)buffer);*/
93 return;
94
95 }
96 eof = true;
97 buffer = nullptr;
98}
99
100size_t MPQFile::read(void* dest, size_t bytes)
101{
102 if (eof) return 0;
103
104 size_t rpos = pointer + bytes;
105 if (rpos > size_t(size)) {
106 bytes = size - pointer;
107 eof = true;
108 }
109
110 memcpy(dest, &(buffer[pointer]), bytes);
111
112 pointer = rpos;
113
114 return bytes;
115}
116
117void MPQFile::seek(int offset)
118{
119 pointer = offset;
120 eof = (pointer >= size);
121}
122
123void MPQFile::seekRelative(int offset)
124{
125 pointer += offset;
126 eof = (pointer >= size);
127}
128
130{
131 delete[] buffer;
132 buffer = nullptr;
133 eof = true;
134}
135
136namespace MPQ
137{
139{
140 std::string_view NamePattern;
142};
143
144constexpr std::array<ArchiveData, 11> Archives =
145{{
146 { .NamePattern = "{installDir}/Data/patch-?.MPQ", .Required = true },
147 { .NamePattern = "{installDir}/Data/{locale}/patch-{locale}-?.MPQ", .Required = true },
148 { .NamePattern = "{installDir}/Data/patch.MPQ", .Required = true },
149 { .NamePattern = "{installDir}/Data/{locale}/patch-{locale}.MPQ", .Required = true },
150 { .NamePattern = "{installDir}/Data/expansion.MPQ", .Required = true },
151 { .NamePattern = "{installDir}/Data/lichking.MPQ", .Required = true },
152 { .NamePattern = "{installDir}/Data/common.MPQ", .Required = true },
153 { .NamePattern = "{installDir}/Data/common-2.MPQ", .Required = true },
154 { .NamePattern = "{installDir}/Data/{locale}/locale-{locale}.MPQ", .Required = true },
155 //{ .NamePattern = "{installDir}/Data/{locale}/speech-{locale}.MPQ", .Required = false },
156 { .NamePattern = "{installDir}/Data/{locale}/expansion-locale-{locale}.MPQ", .Required = true },
157 { .NamePattern = "{installDir}/Data/{locale}/lichking-locale-{locale}.MPQ", .Required = true },
158 //{ .NamePattern = "{installDir}/Data/{locale}/expansion-speech-{locale}.MPQ", .Required = false },
159 //{ .NamePattern = "{installDir}/Data/{locale}/lichking-speech-{locale}.MPQ", .Required = false },
160}};
161
162bool FileNameMatchesArchivePattern(std::string_view const& fileName, std::string_view const& pattern)
163{
164 return std::ranges::equal(fileName, pattern, [](char fileChar, char patternChar)
165 {
166 return patternChar == '?' || charToLower(fileChar) == charToLower(patternChar);
167 });
168}
169
170std::vector<boost::filesystem::path> ResolveArchiveFileList(std::string_view inputPath, std::string_view localeName, std::string_view namePattern)
171{
172 boost::filesystem::path path = Trinity::StringFormat(fmt::runtime(namePattern), fmt::arg("installDir", inputPath), fmt::arg("locale", localeName));
173 std::vector<boost::filesystem::path> absolutePaths;
174
175 std::string fileNamePattern = path.filename().string();
176 if (fileNamePattern.find('?') != std::string::npos)
177 {
178 // scan directory
179 boost::system::error_code ec;
180 for (boost::filesystem::directory_entry const& dirEntry : boost::filesystem::directory_iterator(path.parent_path(), ec))
181 {
182 if (!FileNameMatchesArchivePattern(dirEntry.path().filename().string(), fileNamePattern))
183 continue;
184
185 absolutePaths.push_back(dirEntry.path().lexically_normal().make_preferred());
186 }
187
188 std::ranges::sort(absolutePaths, [](boost::filesystem::path const& left, boost::filesystem::path const& right)
189 {
190 return std::ranges::lexicographical_compare(left.string(), right.string(), std::ranges::greater(), charToLower, charToLower);
191 });
192 }
193 else
194 absolutePaths.push_back(path.lexically_normal().make_preferred());
195
196 return absolutePaths;
197}
198
199bool OpenArchive(std::string_view inputPath, std::string_view localeName, ArchiveData const& archive)
200{
201 std::vector<boost::filesystem::path> fileNames = ResolveArchiveFileList(inputPath, localeName, archive.NamePattern);
202 for (boost::filesystem::path const& fileName : fileNames)
203 {
204 if (!exists(fileName))
205 {
206 if (archive.Required)
207 return false;
208
209 continue;
210 }
211
212 try
213 {
214 gOpenArchives.emplace_back(fileName.string().c_str());
215 }
216 catch (std::exception const& e)
217 {
218 printf("%s\n", e.what());
219 if (archive.Required)
220 return false;
221 }
222 }
223
224 return true;
225}
226
227bool OpenArchives(std::string_view inputPath, std::string_view localeName)
228{
229 for (ArchiveData const& archive : Archives)
230 if (!OpenArchive(inputPath, localeName, archive))
231 if (archive.Required)
232 return false;
233
234 return true;
235}
236
238{
239 gOpenArchives.clear();
240}
241}
struct CharToLower charToLower
MPQArchive(char const *filename)
mpq_archive_s * mpq_a
Definition mpq_libmpq.h:31
void close()
size_t read(void *dest, size_t bytes)
MPQFile(MPQFile const &)=delete
char * buffer
Definition mpq_libmpq.h:76
void close()
libmpq__off_t size
Definition mpq_libmpq.h:77
void seek(int offset)
void seekRelative(int offset)
libmpq__off_t pointer
Definition mpq_libmpq.h:77
bool eof
Definition mpq_libmpq.h:75
ArchiveSet gOpenArchives
std::vector< MPQArchive > ArchiveSet
Definition mpq_libmpq.h:69
ArchiveSet gOpenArchives
constexpr std::array< ArchiveData, 11 > Archives
void CloseArchives()
bool FileNameMatchesArchivePattern(std::string_view const &fileName, std::string_view const &pattern)
std::vector< boost::filesystem::path > ResolveArchiveFileList(std::string_view inputPath, std::string_view localeName, std::string_view namePattern)
bool OpenArchives(std::string_view inputPath, std::string_view localeName)
bool OpenArchive(std::string_view inputPath, std::string_view localeName, ArchiveData const &archive)
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
std::string_view NamePattern