WoW Model Viewer
Your premiere tool for viewing, equipping and animating World of Warcraft models.
Loading...
Searching...
No Matches
WoWFolder.cpp
Go to the documentation of this file.
1#include "WoWFolder.h"
2#include <filesystem>
3#include <format>
4#include <fstream>
5#include <string>
6#include "CASCFile.h"
7#include "Game.h"
8#include "string_utils.h"
9#include "HardDriveFile.h"
10#include "Logger.h"
11
12wow::WoWFolder::WoWFolder(const std::string& path) : GameFolder(path)
13{
14}
15
17{
18 m_CASCFolder.init(path());
19}
20
21void wow::WoWFolder::initFromListfile(const std::string& filename)
22{
23 const std::string fullPath = core::Game::instance().configFolder() + filename;
24 std::ifstream file(fullPath);
25 if (!file.is_open())
26 {
27 LOG_ERROR << "Failed to open" << filename;
28 return;
29 }
30
31 // Get total file size for progress tracking
32 file.seekg(0, std::ios::end);
33 const auto totalSize = file.tellg();
34 file.seekg(0, std::ios::beg);
35 int lineCount = 0;
36
37 LOG_INFO << "WoWFolder - Starting to build object hierarchy";
38 std::string stdline;
39 while (std::getline(file, stdline))
40 {
41 std::string line = core::toLower(stdline);
42 auto lineData = core::split(line, ';');
43 if (lineData.size() < 2)
44 continue;
45 int id = std::stoi(lineData[0]);
46 std::string fileName = lineData[1];
47 // Add the file to the name-ID mappings even if it can't be found in CASC,
48 // as it could be a custom file added by the user:
49 m_idNameMap[id] = fileName;
50 m_nameIdMap[fileName] = id;
51 if (m_CASCFolder.fileExists(id))
52 {
53 CASCFile* File = new CASCFile(fileName, id);
54 auto lastSlash = line.rfind('/');
55 File->setName(lastSlash != std::string::npos ? line.substr(lastSlash + 1) : line);
56 addChild(File);
57 }
58 if (m_progressCallback && ++lineCount % 500 == 0 && totalSize > 0)
59 m_progressCallback(static_cast<int>(file.tellg()), static_cast<int>(totalSize));
60 }
61 LOG_INFO << "WoWFolder - Hierarchy creation done";
62}
63
64void wow::WoWFolder::addCustomFiles(const std::string& path, bool bypassOriginalFiles)
65{
66 LOG_INFO << "Add customFiles from folder" << path;
67 namespace fs = std::filesystem;
68
69 std::error_code ec;
70 for (const auto& entry : fs::recursive_directory_iterator(path, ec))
71 {
72 if (!entry.is_regular_file())
73 continue;
74
75 std::string filePath = core::toLower(entry.path().string());
76 std::string absPath = filePath;
77
78 std::string toRemove = path + "\\";
79 if (filePath.find(toRemove) == 0)
80 filePath.erase(0, toRemove.size());
81
82 GameFile* originalFile = GameFolder::getFile(filePath);
83 bool addnewfile = true;
84 int originalId = -1;
85 if (originalFile)
86 {
87 if (bypassOriginalFiles)
88 {
89 originalId = originalFile->fileDataId();
90 removeChild(originalFile);
91 delete originalFile;
92 originalFile = nullptr;
93 }
94 else
95 {
96 addnewfile = false;
97 }
98 }
99 else
100 {
101 // Even though the file wasn't found in the game database, it's possible to assign it
102 // a specific ID in the listfile (useful in some situations) :
103 auto it = m_nameIdMap.find(filePath);
104 if (it != m_nameIdMap.end())
105 originalId = it->second;
106 }
107 if (addnewfile)
108 {
109 LOG_INFO << "Add custom file" << filePath << "(ID:" << originalId << ")from hard drive location" <<
110 absPath;
111 HardDriveFile* file = new HardDriveFile(filePath, absPath, originalId);
112 auto lastSlash = filePath.rfind('/');
113 file->setName(lastSlash != std::string::npos ? filePath.substr(lastSlash + 1) : filePath);
114 addChild(file);
115 }
116 }
117}
118
120{
121 GameFile* result = nullptr;
122
123 if (id <= 0) // bad id given
124 return result;
125
126 const auto it = m_idMap.find(id);
127 if (it != m_idMap.end())
128 result = it->second;
129
130 if (!result) // if not found, try to force open by id
131 {
132 // Build File########.unk filename needed for CASC lib to open file based on id
133 const std::string filename = std::format("File{:08x}.unk", id);
134 LOG_INFO << "File with id" << id << "not found in listfile. Trying to open" << filename;
135
136 HANDLE newfile;
137 if (m_CASCFolder.openFile(id, &newfile))
138 {
139 LOG_INFO << "Succesfully opened";
140 m_CASCFolder.closeFile(newfile);
141 CASCFile* file = new CASCFile(filename, id);
142 file->setName(filename);
143 addChild(file);
144 result = file;
145 }
146 }
147
148 return result;
149}
150
151bool wow::WoWFolder::openFile(int id, HANDLE* result)
152{
153 return m_CASCFolder.openFile(id, result);
154}
155
156bool wow::WoWFolder::openFile(std::string file, HANDLE* result)
157{
158 const auto it = m_nameIdMap.find(file);
159 if (it == m_nameIdMap.end())
160 return false;
161 return m_CASCFolder.openFile(it->second, result);
162}
163
165{
166 return m_CASCFolder.version();
167}
168
170{
171 auto v = core::split(version(), '.');
172 return std::stoi(v[0]);
173}
174
176{
177 return m_CASCFolder.locale();
178}
179
181{
182 return m_CASCFolder.setConfig(config);
183}
184
185std::vector<core::GameConfig> wow::WoWFolder::configsFound()
186{
187 return m_CASCFolder.configsFound();
188}
189
191{
192 return m_CASCFolder.lastError();
193}
194
196{
197 GameFolder::onChildAdded(child);
198 m_idMap[child->fileDataId()] = child;
199}
200
202{
203 GameFolder::onChildRemoved(child);
204 m_idMap.erase(child->fileDataId());
205}
206
207std::string wow::WoWFolder::fileName(int id)
208{
209 const auto it = m_idNameMap.find(id);
210 if (it == m_idNameMap.end())
211 return std::string();
212 return it->second;
213}
214
215int wow::WoWFolder::fileID(const std::string& fileName)
216{
217 const auto it = m_nameIdMap.find(fileName);
218 if (it == m_nameIdMap.end())
219 return -1;
220 return it->second;
221}
#define LOG_ERROR
Definition Logger.h:11
#define LOG_INFO
Definition Logger.h:10
GameFile implementation that reads from a CASC storage archive.
Definition CASCFile.h:20
void setName(const std::string &name)
Definition Component.cpp:46
Abstract base class representing a file within the game data archive.
Definition GameFile.h:12
virtual bool openFile()=0
int fileDataId()
Definition GameFile.h:57
A CASCFile implementation that reads data from the local hard drive rather than from a CASC archive.
Describes a detected game installation (locale, version, product).
Definition GameFolder.h:16
static Game & instance()
Access the singleton instance (Meyers singleton).
Definition Game.h:22
std::string configFolder()
Definition Game.h:41
GameFile * getFile(int id) override
std::string fileName(int id)
int lastError() override
int majorVersion() override
void initFromListfile(const std::string &file) override
Definition WoWFolder.cpp:21
WoWFolder(const std::string &path)
Definition WoWFolder.cpp:12
int fileID(const std::string &fileName)
void init() override
Definition WoWFolder.cpp:16
void onChildAdded(GameFile *) override
void onChildRemoved(GameFile *) override
std::vector< core::GameConfig > configsFound() override
bool openFile(int id, HANDLE *result) override
void addCustomFiles(const std::string &path, bool bypassOriginalFiles) override
Definition WoWFolder.cpp:64
bool setConfig(core::GameConfig config) override
std::string version() override
std::string locale() override
std::vector< std::string > split(const std::string &s, char delimiter)
Split a string by a single-character delimiter.
std::string toLower(const std::string &s)