forked from wghost/UPKUtils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CustomTFC.cpp
259 lines (251 loc) · 8.71 KB
/
CustomTFC.cpp
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#include <iostream>
#include "CustomTFC.h"
CustomTFC::CustomTFC(const char* filename)
{
CustomTFC::Read(filename);
}
bool CustomTFC::SaveOnDisk()
{
/*std::ofstream file(TFCFileName, std::ios::binary);
if (!file)
return false;
//file.write(TFCFile.str().data(), TFCFile.str().size());
file << TFCFile.rdbuf();
file.close();*/
return true;
}
bool CustomTFC::Read(const char* filename)
{
TFCFileName = filename;
//TFCFile.str("");
/*std::ifstream file(TFCFileName, std::ios::binary);
if (file)
{
file.seekg(0, std::ios::end);
if (file.tellg() != 0)
{
file.seekg(0);
TFCFile << file.rdbuf();
}
file.close();
}*/
if (TFCFile.is_open())
TFCFile.close();
TFCFile.open(TFCFileName.c_str(), std::ios::binary | std::ios::in | std::ios::out);
if (!TFCFile)
{
std::ofstream tmpout(TFCFileName.c_str(), std::ios::binary);
tmpout.close();
TFCFile.open(TFCFileName.c_str(), std::ios::binary | std::ios::in | std::ios::out);
if (!TFCFile)
{
std::cerr << "Cannot open custom texture file: " << TFCFileName << std::endl;
return false;
}
}
return Reload();
}
bool CustomTFC::Reload()
{
if (!IsLoaded())
{
std::cerr << "Custom tfc is not loaded!\n";
return false;
}
TFCFile.clear();
TFCFile.seekg(0);
TFCInventory.clear();
BlockHeaders.clear();
///test if the file is empty
TFCFile.seekg(0, std::ios::end);
if (TFCFile.tellg() == 0)
return true;
else
TFCFile.seekg(0);
///attempt to read the info
bool stop = false;
while (!stop)
{
TFCBlockHeader header;
TFCFile.read(reinterpret_cast<char*>(&header), sizeof(header));
if (header.Magic != CUSTOM_TFC_MAGIC || !TFCFile.good())
{
std::cerr << "Missing custom tfc magic!\n";
return false;
}
BlockHeaders.push_back(header);
size_t maxOffset = header.BlockOffset + header.BlockSize;
while (TFCFile.tellg() < maxOffset)
{
TFCInventoryEntry entry;
TFCFile.read(reinterpret_cast<char*>(&entry.SavedBulkDataSizeOnDisk), 4);
TFCFile.read(reinterpret_cast<char*>(&entry.SavedBulkDataOffsetInFile), 4);
TFCFile.read(reinterpret_cast<char*>(&entry.ObjectNameLength), 4);
if (!TFCFile.good())
{
std::cerr << "Error reading custom tfc entry!\n";
return false;
}
if ((uint32_t)TFCFile.tellg() + (uint32_t)entry.ObjectNameLength > (uint32_t)maxOffset)
{
std::cerr << "Error reading custom tfc entry!\n";
return false;
}
std::vector<char> tmpVec(entry.ObjectNameLength);
TFCFile.read(tmpVec.data(), tmpVec.size());
if (!TFCFile.good())
{
std::cerr << "Error reading custom tfc entry!\n";
return false;
}
entry.ObjectName = std::string(tmpVec.data(), tmpVec.size());
std::string id = entry.ObjectName + "_" + std::to_string(entry.SavedBulkDataSizeOnDisk);
TFCInventory[id] = entry;
}
LastEntryEndOffset = TFCFile.tellg();
if (header.NextBlockOffset != 0xFFFFFFFF)
TFCFile.seekg(header.NextBlockOffset);
else
stop = true;
}
return true;
}
bool CustomTFC::WriteData(TFCInventoryEntry& DataDescr, std::vector<char> DataToWrite)
{
if (!IsLoaded())
{
std::cerr << "Custom tfc is not loaded!\n";
return false;
}
if (DataToWrite.size() == 0 || DataDescr.SavedBulkDataSizeOnDisk != DataToWrite.size())
{
std::cerr << "Custom tfc: wrong data size!\n";
return false;
}
std::string id = DataDescr.ObjectName + "_" + std::to_string(DataDescr.SavedBulkDataSizeOnDisk);
if (TFCInventory.count(id) > 0)
{
DataDescr.SavedBulkDataOffsetInFile = TFCInventory[id].SavedBulkDataOffsetInFile;
return true;
}
return InternalWriteData(DataDescr, DataToWrite);
}
bool CustomTFC::InternalWriteData(TFCInventoryEntry& DataDescr, std::vector<char> DataToWrite)
{
///new file -> make the first header
if (BlockHeaders.size() == 0 && !InternalWriteNewBlock())
{
std::cerr << "Error writing custom tfc header!\n";
return false;
}
if (DataToWrite.size() == 0)
{
std::cerr << "Error writing custom tfc data: data array is empty!\n";
return false;
}
///write the data
TFCFile.seekg(0, std::ios::end);
size_t newDataOffset = TFCFile.tellg();
DataDescr.SavedBulkDataOffsetInFile = newDataOffset;
TFCFile.seekp(newDataOffset);
TFCFile.write(DataToWrite.data(), DataToWrite.size());
if (!TFCFile.good())
{
std::cerr << "Error writing custom tfc data!\n";
std::cerr << "Stream flags: EOF = " << TFCFile.eof() << " FAIL = " << TFCFile.fail() << " BAD = " << TFCFile.bad() << " RDSTATE = " << TFCFile.rdstate() << std::endl;
return false;
}
///zero-padding
if (TFCFile.tellp() % ALIGN_TO_BLOCK_SIZE)
{
std::vector<char> alignmentZeros(ALIGN_TO_BLOCK_SIZE - TFCFile.tellp() % ALIGN_TO_BLOCK_SIZE, 0x00);
TFCFile.write(reinterpret_cast<char*>(alignmentZeros.data()), alignmentZeros.size());
if (!TFCFile.good())
{
std::cerr << "Custom tfc: zero-padding failed!\n";
return false;
}
}
///add new descriptor into the tfc
size_t newEntryOffset = LastEntryEndOffset;
DataDescr.ObjectNameLength = DataDescr.ObjectName.size();
size_t newEntrySize = 12 + DataDescr.ObjectNameLength;
TFCBlockHeader LastHeader = BlockHeaders[BlockHeaders.size() - 1];
///not enough space inside the current block -> add a new one
if (newEntryOffset + newEntrySize >= LastHeader.BlockOffset + ALIGN_TO_BLOCK_SIZE)
{
if (!InternalWriteNewBlock())
{
std::cerr << "Error writing new custom tfc block!\n";
return false;
}
LastHeader = BlockHeaders[BlockHeaders.size() - 1];
}
///write a new entry
TFCFile.seekp(LastEntryEndOffset);
TFCFile.write(reinterpret_cast<char*>(&DataDescr.SavedBulkDataSizeOnDisk), 4);
TFCFile.write(reinterpret_cast<char*>(&DataDescr.SavedBulkDataOffsetInFile), 4);
TFCFile.write(reinterpret_cast<char*>(&DataDescr.ObjectNameLength), 4);
TFCFile.write(DataDescr.ObjectName.data(), DataDescr.ObjectName.size());
if (!TFCFile.good())
{
std::cerr << "Error writing new custom tfc entry!\n";
return false;
}
LastEntryEndOffset = TFCFile.tellp();
///add the entry to the inventory
std::string id = DataDescr.ObjectName + "_" + std::to_string(DataDescr.SavedBulkDataSizeOnDisk);
TFCInventory[id] = DataDescr;
///update current block size
LastHeader.BlockSize += newEntrySize;
TFCFile.seekp(LastHeader.BlockOffset + 8);
TFCFile.write(reinterpret_cast<char*>(&LastHeader.BlockSize), 4);
BlockHeaders[BlockHeaders.size() - 1] = LastHeader;
return TFCFile.good();
}
bool CustomTFC::InternalWriteNewBlock()
{
TFCFile.seekg(0, std::ios::end);
size_t newBlockOffset = TFCFile.tellg();
///no headers and file is not empty -> not a custom tfc, won't do anything!
if (BlockHeaders.size() == 0 && newBlockOffset != 0)
{
std::cerr << "The existing custom tfc file has no header!\n";
return false;
}
///write a new block of zeros
TFCFile.seekp(newBlockOffset);
std::vector<char> newBlock(ALIGN_TO_BLOCK_SIZE, 0x00);
TFCFile.write(newBlock.data(), newBlock.size());
if (!TFCFile.good())
{
std::cerr << "Custom tfc file: failed to make a new block!\n";
return false;
}
///set NextBlockOffset for the previous block
if (BlockHeaders.size() > 0)
{
TFCFile.seekp(BlockHeaders[BlockHeaders.size() - 1].BlockOffset + 12);
TFCFile.write(reinterpret_cast<char*>(&newBlockOffset), 4);
if (!TFCFile.good())
{
std::cerr << "Custom tfc file: failed to update previous block link!\n";
return false;
}
}
///make and write the new block header
TFCBlockHeader NewHeader;
NewHeader.BlockOffset = newBlockOffset;
TFCFile.seekp(newBlockOffset);
TFCFile.write(reinterpret_cast<char*>(&NewHeader), sizeof(NewHeader));
if (!TFCFile.good())
{
std::cerr << "Custom tfc file: failed to write new block header!\n";
return false;
}
///set the offset for the new entry
LastEntryEndOffset = TFCFile.tellp();
///add the new block to the list
BlockHeaders.push_back(NewHeader);
return true;
}