-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathdata.cpp
More file actions
206 lines (175 loc) · 7.35 KB
/
data.cpp
File metadata and controls
206 lines (175 loc) · 7.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/*
* Standard Streamer Plugin for Mod Loader
* Copyright (C) 2014 LINK/2012 <dma_2012@hotmail.com>
* Licensed under the MIT License, see LICENSE at top level directory.
*
*/
#include <stdinc.hpp>
#include "streaming.hpp"
using namespace std::placeholders;
extern "C"
{
void* (*ColModelPool_new)(int) = nullptr;
extern void HOOK_LoadColFileFix(int);
};
static void FixColFile();
static void* LoadNonStreamedRes(std::function<void*(const char*)> load, const char* filepath, NonStreamedType type);
/*
* gta.dat / default.dat related stuff
*/
void CAbstractStreaming::DataPatch()
{
// Fix broken COLFILE in GTA SA
FixColFile();
// Detouring of non streamed resources loaded by gta.dat/default.dat
if(true)
{
if(true)
{
using hcolfile = function_hooker<0x5B9188, void*(const char*, int)>;
using hcolfile2 = function_hooker<xIII(0x476589), void*(const char*, int)>;
using hclump = function_hooker<0x5B91DB, void*(const char*)>;
auto fn_hcolfile = [](hcolfile::func_type load, const char* filepath, int id)
{
// NOTE: on III `id` is not a parameter, but since this is __cdecl, we don't have a problem.
return LoadNonStreamedRes(std::bind(load, _1, id), filepath, NonStreamedType::ColFile);
};
make_static_hook<hcolfile>(fn_hcolfile);
if(gvm.IsIII()) make_static_hook<hcolfile2>(fn_hcolfile);
make_static_hook<hclump>(std::bind(LoadNonStreamedRes, _1, _2, NonStreamedType::ClumpFile));
}
if(gvm.IsIII() || gvm.IsSA())
{
using hatomic = function_hooker<0x5B91B0, void*(const char*)>;
using htexdict = function_hooker<0x5B910A, void*(const char*)>;
make_static_hook<hatomic>(std::bind(LoadNonStreamedRes, _1, _2, NonStreamedType::AtomicFile));
make_static_hook<htexdict>(std::bind(LoadNonStreamedRes, _1, _2, NonStreamedType::TexDictionary));
}
else if(gvm.IsVC())
{
using hatomic = function_hooker<xVc(0x48DB7B), void*(int, int, const char*)>;
using htexdict = function_hooker<xVc(0x48DA46), void*(int, int, const char*)>;
make_static_hook<hatomic>([](hatomic::func_type load, int a, int b, const char* filepath)
{
return LoadNonStreamedRes(std::bind(load, a, b, _1), filepath, NonStreamedType::AtomicFile);
});
make_static_hook<htexdict>([](htexdict::func_type load, int a, int b, const char* filepath)
{
return LoadNonStreamedRes(std::bind(load, a, b, _1), filepath, NonStreamedType::TexDictionary);
});
}
}
}
/*
* CAbstractStreaming::TryLoadNonStreamedResource
* Checks if the file 'filepath' is under our ownership, and if it is register as non-streamed.
*/
std::string CAbstractStreaming::TryLoadNonStreamedResource(std::string filepath, NonStreamedType type)
{
auto filename = NormalizePath(filepath.substr(GetLastPathComponent(filepath)));
if(!this->bHasInitializedStreaming)
{
auto it = std::find_if(this->raw_models.begin(), this->raw_models.end(),
[&filename](const std::pair<std::string, const modloader::file *>& pair) {
return std::string(pair.second->filename()) == filename;
});
if(it != this->raw_models.end())
{
// Log about non streamed resource and make sure it's unique
plugin_ptr->Log("Using non-streamed resource \"%s\"", it->second->filepath());
if(this->non_stream.count(it->second->hash))
throw std::runtime_error("std.stream: TryLoadNonStreamedResource: Repeated resource!");
// Register into non_stream and unregister from raw_models
this->non_stream.emplace(it->second->hash, std::make_pair(it->second, type));
std::string fullpath = it->second->fullpath();
this->raw_models.erase(it);
return fullpath;
}
}
if(true)
{
size_t filename_hash = modloader::hash(filename);
auto it = this->non_stream.find(filename_hash);
if(it != this->non_stream.end())
{
return it->second.first->fullpath();
}
}
return std::string();
}
void CAbstractStreaming::RemoveNonStreamedFromRawModels()
{
// This is only needed for III because of non-streamed .col/.ifp
if(!gvm.IsIII())
return;
for(auto it = this->raw_models.begin(); it != this->raw_models.end(); )
{
if(IsNonStreamed(it->second))
it = this->raw_models.erase(it);
else
++it;
}
}
/*
* LoadNonStreamedRes
* Direct wrapper between the game calls and CAbstractStreaming::TryLoadNonStreamedResource
*/
void* LoadNonStreamedRes(std::function<void*(const char*)> load, const char* filepath, NonStreamedType type)
{
auto newfilepath = streaming->TryLoadNonStreamedResource(filepath, type);
return load(newfilepath.size()? newfilepath.data() : filepath);
}
/*
* FixColFile
* Fixes the COLFILE from gta.dat not working properly, crashing the game (GTA SA only).
*/
void FixColFile()
{
if(!gvm.IsSA())
return;
static bool using_colbuf; // Is using colbuf or original buffer?
static bool empty_colmodel; // Is this a empty colmodel?
static std::vector<char> colbuf; // Buffer for reading col file into
// Prototype for patches
using rcolinfo_f = int(void*, uint32_t*, size_t);
using rcolmodel_f = int(void*, char*, size_t);
using lcol_f = int(char*, int, void*, char*);
using rel_f = int(void*);
// Fixes the crash caused by using COLFILE for a building etc
ColModelPool_new = MakeCALL(0x5B4F2E, HOOK_LoadColFileFix).get();
// Reads collision info and check if we need to use our own collision buffer
auto ReadColInfo = [](std::function<rcolinfo_f> Read, void*& f, uint32_t*& buffer, size_t& size)
{
auto r = Read(f, buffer, size);
empty_colmodel = (buffer[1] <= 0x18);
if(using_colbuf = !empty_colmodel)
colbuf.resize(buffer[1]);
return r;
};
// Replace buffer if necessary
auto ReadColModel = [](std::function<rcolmodel_f> Read, void*& f, char*& buffer, size_t& size)
{
if(!empty_colmodel)
return Read(f, using_colbuf? colbuf.data() : buffer, size);
return 0;
};
// Replace buffer if necessary
auto LoadCollisionModel = [](std::function<lcol_f> LoadColModel, char*& buf, int& size, void*& colmodel, char*& modelname)
{
return LoadColModel(using_colbuf? colbuf.data() : buf, size, colmodel, modelname);
};
// Frees our buffer
auto ReleaseBuffer = [](std::function<rel_f> fclose, void*& f)
{
colbuf.clear(); colbuf.shrink_to_fit();
return fclose(f);
};
// Patches
make_static_hook<function_hooker<0x5B4EF4, rcolmodel_f>>(ReadColModel);
make_static_hook<function_hooker<0x5B4E92, rcolinfo_f>>(ReadColInfo);
make_static_hook<function_hooker<0x5B4FCC, rcolinfo_f>>(ReadColInfo);
make_static_hook<function_hooker<0x5B4FA0, lcol_f>>(LoadCollisionModel);
make_static_hook<function_hooker<0x5B4FB5, lcol_f>>(LoadCollisionModel);
make_static_hook<function_hooker<0x5B4F83, lcol_f>>(LoadCollisionModel);
make_static_hook<function_hooker<0x5B92F9, rel_f>>(ReleaseBuffer);
}