mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
317 lines
8.3 KiB
C++
317 lines
8.3 KiB
C++
/*
|
|
* sfall
|
|
* Copyright (C) 2008, 2009, 2010, 2012 The sfall team
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
|
|
#include "..\main.h"
|
|
#include "..\FalloutEngine\Fallout2.h"
|
|
#include "..\Utils.h"
|
|
//#include "FileSystem.h"
|
|
|
|
#include "Tiles.h"
|
|
|
|
namespace sfall
|
|
{
|
|
|
|
typedef int (__stdcall *functype)();
|
|
|
|
static const DWORD Tiles_0E[] = {
|
|
0x484255, 0x48429D, 0x484377, 0x484385, 0x48A897, 0x48A89A, 0x4B2231,
|
|
0x4B2374, 0x4B2381, 0x4B2480, 0x4B248D, 0x4B2A7C, 0x4B2BDA,
|
|
};
|
|
|
|
static const DWORD Tiles_3F[] = {
|
|
0x41875D, 0x4839E6, 0x483A2F, 0x484380, 0x48A803, 0x48A9F2, 0x48C96C,
|
|
0x48C99F, 0x48C9D2, 0x4B2247, 0x4B2334, 0x4B2440, 0x4B2AB9, 0x4B2B8F,
|
|
0x4B2BF4,
|
|
};
|
|
|
|
static const DWORD Tiles_40[] = {
|
|
0x48C941, 0x48C955, 0x48CA5F, 0x48CA71, 0x48CA9B, 0x48CAAD, 0x48CADB,
|
|
0x48CAEB, 0x48CAF7, 0x48CB20, 0x48CB32, 0x48CB61,
|
|
};
|
|
|
|
static const DWORD Tiles_C0[] = {
|
|
0x48424E, 0x484296, 0x484372, 0x48A88D, 0x48A892, 0x4B222C, 0x4B236F,
|
|
0x4B247B, 0x4B2A77, 0x4B2BD5,
|
|
};
|
|
|
|
#pragma pack(push, 1)
|
|
struct TilesData {
|
|
short tile[2];
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
struct OverrideEntry {
|
|
DWORD xTiles;
|
|
DWORD yTiles;
|
|
DWORD replacementID;
|
|
|
|
OverrideEntry(DWORD xtiles, DWORD ytiles, DWORD repid)
|
|
: xTiles(xtiles), yTiles(ytiles), replacementID(repid) {
|
|
}
|
|
};
|
|
|
|
static OverrideEntry** overrides = nullptr;
|
|
static DWORD origTileCount = 0;
|
|
static DWORD tileMode;
|
|
static BYTE* mask;
|
|
|
|
static bool LoadMask() {
|
|
fo::DbFile* file = fo::func::db_fopen("art\\tiles\\gridmask.frm", "rb"); // same as grid000.frm from HRP
|
|
if (!file) {
|
|
dlogr("AllowLargeTiles: Failed to open gridmask.frm file.", DL_INIT);
|
|
return false;
|
|
}
|
|
mask = new BYTE[80 * 36];
|
|
|
|
fo::func::db_fseek(file, 0x4A, SEEK_SET);
|
|
fo::func::db_freadByteCount(file, mask, 80 * 36);
|
|
fo::func::db_fclose(file);
|
|
return true;
|
|
}
|
|
|
|
static int ProcessTile(fo::Art* tiles, int tile, int listPos) {
|
|
char buf[32] = "art\\tiles\\";
|
|
strncpy_s(&buf[10], 22, &tiles->names[13 * tile], _TRUNCATE);
|
|
|
|
fo::DbFile* artFile = fo::func::db_fopen(buf, "rb");
|
|
if (!artFile) return 0;
|
|
|
|
fo::func::db_fseek(artFile, 0x3E, SEEK_SET); // frameData
|
|
|
|
WORD width;
|
|
fo::func::db_freadShort(artFile, &width);
|
|
if (width <= 80) goto exit;
|
|
|
|
WORD height;
|
|
fo::func::db_freadShort(artFile, &height);
|
|
if (height < 36) goto exit;
|
|
|
|
float newWidth = (float)(width - (width % 8));
|
|
float newHeight = (float)(height - (height % 12));
|
|
|
|
// Number of tiles horizontally and vertically
|
|
// the calculation is unstable if the large tile is not proportional to the size of 80x36 (aspect ratio)
|
|
int xSize = std::lroundf(((newWidth / 32.0f) - (newHeight / 24.0f)) - 0.01f);
|
|
int ySize = std::lroundf(((newHeight / 16.0f) - (newWidth / 64.0f)) - 0.01f);
|
|
if (xSize <= 0 || ySize <= 0) goto exit;
|
|
|
|
long bytes = width * height;
|
|
BYTE* pixelData = new BYTE[bytes];
|
|
|
|
// Read pixels data
|
|
if (fo::func::db_fseek(artFile, 0x4A, SEEK_SET) ||
|
|
fo::func::db_fread(pixelData, 1, bytes, artFile) != bytes)
|
|
{
|
|
delete[] pixelData;
|
|
exit:
|
|
fo::func::db_fclose(artFile);
|
|
return 0;
|
|
}
|
|
long listID = listPos - tiles->total;
|
|
|
|
fo::FrmFile frame;
|
|
fo::func::db_fseek(artFile, 0, SEEK_SET);
|
|
fo::func::db_freadByteCount(artFile, (BYTE*)&frame, 74);
|
|
|
|
frame.height = ByteSwapW(36);
|
|
frame.width = ByteSwapW(80);
|
|
frame.frameSize = ByteSwapD(80 * 36);
|
|
frame.frameAreaSize = ByteSwapD(80 * 36 + 12);
|
|
|
|
for (int y = 0; y < ySize; y++) {
|
|
for (int x = 0; x < xSize; x++) {
|
|
// offset relative to the top-left corner
|
|
int xOffset = x * 48 + (ySize - (y + 1)) * 32;
|
|
int yOffset = height - (36 + x * 12 + y * 24);
|
|
|
|
for (int pY = 0; pY < 36; pY++) {
|
|
for (int pX = 0; pX < 80; pX++) {
|
|
int point = (80 * pY) + pX;
|
|
if (mask[point]) {
|
|
frame.pixels[point] = pixelData[(width * (yOffset + pY)) + xOffset + pX];
|
|
} else {
|
|
frame.pixels[point] = 0;
|
|
}
|
|
}
|
|
}
|
|
sprintf_s(&buf[10], 22, "zzz%04d.frm", listID++);
|
|
//FScreateFromData(buf, &frame, sizeof(frame));
|
|
fo::DbFile* file = fo::func::db_fopen(buf, "wb");
|
|
fo::func::db_fwriteByteCount(file, (BYTE*)&frame, sizeof(frame));
|
|
fo::func::db_fclose(file);
|
|
}
|
|
}
|
|
overrides[tile] = new OverrideEntry(xSize, ySize, listPos);
|
|
|
|
fo::func::db_fclose(artFile);
|
|
delete[] pixelData;
|
|
|
|
return xSize * ySize; // number of tiles added
|
|
}
|
|
|
|
static const functype art_init = (functype)fo::funcoffs::art_init_;
|
|
|
|
static int __stdcall ArtInitHook() {
|
|
if (art_init()) return -1;
|
|
if (!LoadMask()) return 0;
|
|
|
|
fo::Art* tiles = &fo::var::art[4];
|
|
|
|
long listpos = origTileCount = tiles->total;
|
|
overrides = new OverrideEntry*[listpos]();
|
|
|
|
if (tileMode == 2) {
|
|
fo::DbFile* file = fo::func::db_fopen("art\\tiles\\xltiles.lst", "rt");
|
|
if (!file) return 0;
|
|
|
|
char buf[32];
|
|
DWORD id;
|
|
char* comment;
|
|
|
|
while (fo::func::db_fgets(buf, 31, file) > 0) {
|
|
if (comment = strchr(buf, ';')) *comment = 0;
|
|
id = atoi(buf);
|
|
if (id > 1) listpos += ProcessTile(tiles, id, listpos);
|
|
}
|
|
fo::func::db_fclose(file);
|
|
} else {
|
|
for (int i = 2; i < tiles->total; i++) listpos += ProcessTile(tiles, i, listpos);
|
|
}
|
|
if (listpos != tiles->total) {
|
|
tiles->names = (char*)fo::func::mem_realloc(tiles->names, listpos * 13);
|
|
for (long i = tiles->total; i < listpos; i++) {
|
|
sprintf_s(&tiles->names[i * 13], 12, "zzz%04d.frm", i - tiles->total);
|
|
}
|
|
tiles->total = listpos;
|
|
}
|
|
|
|
delete[] mask;
|
|
return 0;
|
|
}
|
|
|
|
static void __declspec(naked) iso_init_hook() {
|
|
__asm {
|
|
mov ebx, dword ptr ds:[FO_VAR_read_callback];
|
|
xor eax, eax;
|
|
mov dword ptr ds:[FO_VAR_read_callback], eax;
|
|
call ArtInitHook;
|
|
mov dword ptr ds:[FO_VAR_read_callback], ebx;
|
|
retn;
|
|
}
|
|
}
|
|
|
|
static long __fastcall SquareLoadCheck(TilesData* data) {
|
|
if (!overrides) return 0;
|
|
for (DWORD y = 0; y < 100; y++) {
|
|
for (DWORD x = 0; x < 100; x++) {
|
|
for (DWORD z = 0; z < 2; z++) {
|
|
DWORD tile = data[y * 100 + x].tile[z];
|
|
if (tile > 1 && tile < origTileCount && overrides[tile]) {
|
|
DWORD newtile = overrides[tile]->replacementID - 1;
|
|
for (DWORD y2 = 0; y2 < overrides[tile]->yTiles; y2++) {
|
|
for (DWORD x2 = 0; x2 < overrides[tile]->xTiles; x2++) {
|
|
newtile++;
|
|
if (x - x2 < 0 || y - y2 < 0) continue;
|
|
data[(y - y2) * 100 + x - x2].tile[z] = (short)newtile;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0; // don't delete
|
|
}
|
|
|
|
static void __declspec(naked) square_load_hook() {
|
|
__asm {
|
|
mov ecx, edx;
|
|
call fo::funcoffs::db_freadIntCount_;
|
|
test eax, eax;
|
|
jnz end;
|
|
jmp SquareLoadCheck; // ecx - data
|
|
end:
|
|
retn;
|
|
}
|
|
}
|
|
|
|
static void __declspec(naked) art_id_hack() {
|
|
using namespace fo;
|
|
__asm {
|
|
cmp esi, (OBJ_TYPE_TILE << 24); // 0x4000000
|
|
jne end;
|
|
and eax, 0x3FFF;
|
|
retn;
|
|
end:
|
|
and eax, 0x0FFF;
|
|
retn;
|
|
}
|
|
}
|
|
|
|
static void __declspec(naked) art_get_name_hack() {
|
|
using namespace fo;
|
|
__asm {
|
|
sar eax, 24;
|
|
cmp eax, OBJ_TYPE_TILE;
|
|
jne end;
|
|
mov esi, edx;
|
|
mov ebp, edx;
|
|
and esi, 0x3FFF;
|
|
and ebp, 0xC000;
|
|
sar ebp, 0x0E;
|
|
end:
|
|
test esi, esi;
|
|
retn;
|
|
}
|
|
}
|
|
|
|
void Tiles::init() {
|
|
if (tileMode = IniReader::GetConfigInt("Misc", "AllowLargeTiles", 0)) {
|
|
dlog("Applying allow large tiles patch.", DL_INIT);
|
|
HookCall(0x481D72, iso_init_hook);
|
|
HookCall(0x48434C, square_load_hook);
|
|
dlogr(" Done", DL_INIT);
|
|
}
|
|
|
|
if (IniReader::GetConfigInt("Misc", "MoreTiles", 0)) {
|
|
dlog("Applying tile FRM limit patch.", DL_INIT);
|
|
MakeCall(0x419D46, art_id_hack);
|
|
MakeCall(0x419479, art_get_name_hack);
|
|
SafeWriteBatch<BYTE>(0x0E, Tiles_0E);
|
|
SafeWriteBatch<BYTE>(0x3F, Tiles_3F);
|
|
SafeWriteBatch<BYTE>(0x40, Tiles_40);
|
|
SafeWriteBatch<BYTE>(0xC0, Tiles_C0);
|
|
if (hrpVersionValid) { // Check HRP 4.1.8
|
|
SafeWrite8(HRPAddress(0x1000E1C0), 0x40);
|
|
SafeWrite8(HRPAddress(0x1000E1DA), 0x3F);
|
|
}
|
|
dlogr(" Done", DL_INIT);
|
|
}
|
|
}
|
|
|
|
void Tiles::exit() {
|
|
if (overrides) {
|
|
for (size_t i = 0; i < origTileCount; i++) delete overrides[i]; // free OverrideEntry
|
|
delete[] overrides;
|
|
}
|
|
}
|
|
|
|
}
|