1 /** @file importdeh.cpp DeHackEd patch reader plugin for Doomsday Engine.
2 *
3 * @authors Copyright © 2013-2014 Daniel Swanson <danij@dengine.net>
4 * @authors Copyright © 2012-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA</small>
19 */
20
21 #include "importdeh.h"
22
23 #include <QDir>
24 #include <QFile>
25 #include <doomsday/filesys/lumpindex.h>
26 #include <doomsday/resource/bundles.h>
27 #include <doomsday/DoomsdayApp>
28 #include <de/App>
29 #include <de/CommandLine>
30 #include <de/Block>
31 #include <de/Log>
32 #include <de/String>
33
34 #include "dehreader.h"
35
36 using namespace de;
37
38 // Global handle on the engine's definition databases.
39 ded_t *ded;
40
41 // This is the original data before it gets replaced by any patches.
42 ded_sprid_t origSpriteNames[NUMSPRITES];
43 String origActionNames[NUMSTATES];
44
backupData()45 static void backupData()
46 {
47 for (int i = 0; i < NUMSPRITES && i < ded->sprites.size(); i++)
48 {
49 qstrncpy(origSpriteNames[i].id, ded->sprites[i].id, DED_SPRITEID_LEN + 1);
50 }
51
52 for (int i = 0; i < NUMSTATES && i < ded->states.size(); i++)
53 {
54 origActionNames[i] = ded->states[i].gets("action");
55 }
56 }
57
readLump(LumpIndex const & lumpIndex,lumpnum_t lumpNum)58 static void readLump(LumpIndex const &lumpIndex, lumpnum_t lumpNum)
59 {
60 if (0 > lumpNum || lumpNum >= lumpIndex.size())
61 {
62 LOG_AS("DehRead::readLump");
63 LOG_WARNING("Invalid lump index #%i, ignoring.") << lumpNum;
64 return;
65 }
66
67 File1 &lump = lumpIndex[lumpNum];
68 size_t len = lump.size();
69 Block deh = Block::fromRawData(reinterpret_cast<char const *>(lump.cache()), len);
70 /// @attention Results in a deep-copy of the lump data into the Block
71 /// thus the cached lump can be released after this call.
72 ///
73 /// @todo Do not use a local buffer - read using QTextStream.
74 lump.unlock();
75
76 /// @todo Custom status for contained files is not inherited from the container?
77 bool lumpIsCustom = (lump.isContained()? lump.container().hasCustom() : lump.hasCustom());
78
79 LOG_RES_MSG("Applying DeHackEd patch lump #%i \"%s:%s\"%s")
80 << lumpNum
81 << NativePath(lump.container().composePath()).pretty()
82 << lump.name()
83 << (lumpIsCustom? " (custom)" : "");
84
85 readDehPatch(deh, lumpIsCustom, NoInclude | IgnoreEOF);
86 }
87
88 #if 0
89 static void readFile(String const &sourcePath, bool sourceIsCustom = true)
90 {
91 LOG_AS("DehRead::readFile");
92
93 QFile file(sourcePath);
94 if (!file.open(QFile::ReadOnly | QFile::Text))
95 {
96 LOG_WARNING("Failed opening \"%s\" for read, aborting...") << QDir::toNativeSeparators(sourcePath);
97 return;
98 }
99
100 LOG_RES_MSG("Applying DeHackEd patch file \"%s\"%s")
101 << NativePath(sourcePath).pretty()
102 << (sourceIsCustom? " (custom)" : "");
103
104 readDehPatch(file.readAll(), sourceIsCustom, IgnoreEOF);
105 }
106 #endif
107
readFile2(String const & path,bool sourceIsCustom=true)108 static void readFile2(String const &path, bool sourceIsCustom = true)
109 {
110 LOG_AS("DehRead::readFile2");
111
112 if (File const *file = App::rootFolder().tryLocate<File const>(path))
113 {
114 LOG_RES_MSG("Applying %s%s")
115 << file->description()
116 << (sourceIsCustom? " (custom)" : "");
117
118 Block deh;
119 *file >> deh;
120 readDehPatch(deh, sourceIsCustom, IgnoreEOF);
121 }
122 else
123 {
124 LOG_RES_WARNING("\"%s\" not found") << path;
125 }
126 }
127
readPatchLumps(LumpIndex const & lumpIndex)128 static void readPatchLumps(LumpIndex const &lumpIndex)
129 {
130 bool const readAll = DENG2_APP->commandLine().check("-alldehs");
131 for (int i = lumpIndex.size() - 1; i >= 0; i--)
132 {
133 if (lumpIndex[i].name().fileNameExtension().toLower() == ".deh")
134 {
135 readLump(lumpIndex, i);
136 if (!readAll) return;
137 }
138 }
139 }
140
readPatchFiles()141 static void readPatchFiles()
142 {
143 // Patches may be loaded as data bundles.
144 for (DataBundle const *bundle : DataBundle::loadedBundles())
145 {
146 if (bundle->format() == DataBundle::Dehacked)
147 {
148 String const bundleRoot = bundle->rootPath();
149 for (Value const *path : bundle->packageMetadata().geta("dataFiles").elements())
150 {
151 readFile2(bundleRoot / path->asText());
152 }
153 }
154 }
155 }
156
157 /**
158 * This will be called after the engine has loaded all definitions but before
159 * the data they contain has been initialized.
160 */
DefsHook(int,int,void * data)161 int DefsHook(int /*hook_type*/, int /*parm*/, void *data)
162 {
163 // Grab the DED definition handle supplied by the engine.
164 ded = reinterpret_cast<ded_t *>(data);
165
166 backupData();
167
168 // Check for DEHACKED lumps.
169 readPatchLumps(*reinterpret_cast<de::LumpIndex const *>(F_LumpIndex()));
170
171 // Process all patch files specified with -deh options on the command line.
172 readPatchFiles();
173
174 return true;
175 }
176
177 /**
178 * This function is called automatically when the plugin is loaded.
179 * We let the engine know what we'd like to do.
180 */
DP_Initialize()181 DENG_ENTRYPOINT void DP_Initialize()
182 {
183 Plug_AddHook(HOOK_DEFS, DefsHook);
184 }
185
186 /**
187 * Declares the type of the plugin so the engine knows how to treat it. Called
188 * automatically when the plugin is loaded.
189 */
deng_LibraryType()190 DENG_ENTRYPOINT char const *deng_LibraryType()
191 {
192 return "deng-plugin/generic";
193 }
194
195 #if defined (DENG_STATIC_LINK)
196
staticlib_importdeh_symbol(char const * name)197 DENG_EXTERN_C void *staticlib_importdeh_symbol(char const *name)
198 {
199 DENG_SYMBOL_PTR(name, deng_LibraryType)
200 DENG_SYMBOL_PTR(name, DP_Initialize);
201 qWarning() << name << "not found in importdeh";
202 return nullptr;
203 }
204
205 #else
206
207 DENG_DECLARE_API(Base);
208 DENG_DECLARE_API(Con);
209 DENG_DECLARE_API(Def);
210 DENG_DECLARE_API(F);
211
212 DENG_API_EXCHANGE(
213 DENG_GET_API(DE_API_BASE, Base);
214 DENG_GET_API(DE_API_CONSOLE, Con);
215 DENG_GET_API(DE_API_DEFINITIONS, Def);
216 DENG_GET_API(DE_API_FILE_SYSTEM, F);
217 )
218
219 #endif
220