mirror of
https://github.com/XLabsProject/iw3x-port.git
synced 2023-08-02 15:02:11 +02:00
Generate missing specular probes from the loading image
This commit is contained in:
parent
17a80f8d09
commit
7d8012ba3c
248
deps/s3tc-dxt-decompression/s3tc.cpp
vendored
Normal file
248
deps/s3tc-dxt-decompression/s3tc.cpp
vendored
Normal file
@ -0,0 +1,248 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
// unsigned long PackRGBA(): Helper method that packs RGBA channels into a single 4 byte pixel.
|
||||
//
|
||||
// unsigned char r: red channel.
|
||||
// unsigned char g: green channel.
|
||||
// unsigned char b: blue channel.
|
||||
// unsigned char a: alpha channel.
|
||||
|
||||
unsigned long PackRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
|
||||
{
|
||||
return ((r << 24) | (g << 16) | (b << 8) | a);
|
||||
}
|
||||
|
||||
// void DecompressBlockDXT1(): Decompresses one block of a DXT1 texture and stores the resulting pixels at the appropriate offset in 'image'.
|
||||
//
|
||||
// unsigned long x: x-coordinate of the first pixel in the block.
|
||||
// unsigned long y: y-coordinate of the first pixel in the block.
|
||||
// unsigned long width: width of the texture being decompressed.
|
||||
// unsigned long height: height of the texture being decompressed.
|
||||
// const unsigned char *blockStorage: pointer to the block to decompress.
|
||||
// unsigned long *image: pointer to image where the decompressed pixel data should be stored.
|
||||
|
||||
void DecompressBlockDXT1(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image)
|
||||
{
|
||||
unsigned short color0 = *reinterpret_cast<const unsigned short *>(blockStorage);
|
||||
unsigned short color1 = *reinterpret_cast<const unsigned short *>(blockStorage + 2);
|
||||
|
||||
unsigned long temp;
|
||||
|
||||
temp = (color0 >> 11) * 255 + 16;
|
||||
unsigned char r0 = (unsigned char)((temp/32 + temp)/32);
|
||||
temp = ((color0 & 0x07E0) >> 5) * 255 + 32;
|
||||
unsigned char g0 = (unsigned char)((temp/64 + temp)/64);
|
||||
temp = (color0 & 0x001F) * 255 + 16;
|
||||
unsigned char b0 = (unsigned char)((temp/32 + temp)/32);
|
||||
|
||||
temp = (color1 >> 11) * 255 + 16;
|
||||
unsigned char r1 = (unsigned char)((temp/32 + temp)/32);
|
||||
temp = ((color1 & 0x07E0) >> 5) * 255 + 32;
|
||||
unsigned char g1 = (unsigned char)((temp/64 + temp)/64);
|
||||
temp = (color1 & 0x001F) * 255 + 16;
|
||||
unsigned char b1 = (unsigned char)((temp/32 + temp)/32);
|
||||
|
||||
unsigned long code = *reinterpret_cast<const unsigned long *>(blockStorage + 4);
|
||||
|
||||
for (int j=0; j < 4; j++)
|
||||
{
|
||||
for (int i=0; i < 4; i++)
|
||||
{
|
||||
unsigned long finalColor = 0;
|
||||
unsigned char positionCode = (code >> 2*(4*j+i)) & 0x03;
|
||||
|
||||
if (color0 > color1)
|
||||
{
|
||||
switch (positionCode)
|
||||
{
|
||||
case 0:
|
||||
finalColor = PackRGBA(r0, g0, b0, 255);
|
||||
break;
|
||||
case 1:
|
||||
finalColor = PackRGBA(r1, g1, b1, 255);
|
||||
break;
|
||||
case 2:
|
||||
finalColor = PackRGBA((2*r0+r1)/3, (2*g0+g1)/3, (2*b0+b1)/3, 255);
|
||||
break;
|
||||
case 3:
|
||||
finalColor = PackRGBA((r0+2*r1)/3, (g0+2*g1)/3, (b0+2*b1)/3, 255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (positionCode)
|
||||
{
|
||||
case 0:
|
||||
finalColor = PackRGBA(r0, g0, b0, 255);
|
||||
break;
|
||||
case 1:
|
||||
finalColor = PackRGBA(r1, g1, b1, 255);
|
||||
break;
|
||||
case 2:
|
||||
finalColor = PackRGBA((r0+r1)/2, (g0+g1)/2, (b0+b1)/2, 255);
|
||||
break;
|
||||
case 3:
|
||||
finalColor = PackRGBA(0, 0, 0, 255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (x + i < width)
|
||||
image[(y + j)*width + (x + i)] = finalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// void BlockDecompressImageDXT1(): Decompresses all the blocks of a DXT1 compressed texture and stores the resulting pixels in 'image'.
|
||||
//
|
||||
// unsigned long width: Texture width.
|
||||
// unsigned long height: Texture height.
|
||||
// const unsigned char *blockStorage: pointer to compressed DXT1 blocks.
|
||||
// unsigned long *image: pointer to the image where the decompressed pixels will be stored.
|
||||
|
||||
void BlockDecompressImageDXT1(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image)
|
||||
{
|
||||
unsigned long blockCountX = (width + 3) / 4;
|
||||
unsigned long blockCountY = (height + 3) / 4;
|
||||
unsigned long blockWidth = (width < 4) ? width : 4;
|
||||
unsigned long blockHeight = (height < 4) ? height : 4;
|
||||
|
||||
for (unsigned long j = 0; j < blockCountY; j++)
|
||||
{
|
||||
for (unsigned long i = 0; i < blockCountX; i++) DecompressBlockDXT1(i*4, j*4, width, blockStorage + i * 8, image);
|
||||
blockStorage += blockCountX * 8;
|
||||
}
|
||||
}
|
||||
|
||||
// void DecompressBlockDXT5(): Decompresses one block of a DXT5 texture and stores the resulting pixels at the appropriate offset in 'image'.
|
||||
//
|
||||
// unsigned long x: x-coordinate of the first pixel in the block.
|
||||
// unsigned long y: y-coordinate of the first pixel in the block.
|
||||
// unsigned long width: width of the texture being decompressed.
|
||||
// unsigned long height: height of the texture being decompressed.
|
||||
// const unsigned char *blockStorage: pointer to the block to decompress.
|
||||
// unsigned long *image: pointer to image where the decompressed pixel data should be stored.
|
||||
|
||||
void DecompressBlockDXT5(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image)
|
||||
{
|
||||
unsigned char alpha0 = *reinterpret_cast<const unsigned char *>(blockStorage);
|
||||
unsigned char alpha1 = *reinterpret_cast<const unsigned char *>(blockStorage + 1);
|
||||
|
||||
const unsigned char *bits = blockStorage + 2;
|
||||
unsigned long alphaCode1 = bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24);
|
||||
unsigned short alphaCode2 = bits[0] | (bits[1] << 8);
|
||||
|
||||
unsigned short color0 = *reinterpret_cast<const unsigned short *>(blockStorage + 8);
|
||||
unsigned short color1 = *reinterpret_cast<const unsigned short *>(blockStorage + 10);
|
||||
|
||||
unsigned long temp;
|
||||
|
||||
temp = (color0 >> 11) * 255 + 16;
|
||||
unsigned char r0 = (unsigned char)((temp/32 + temp)/32);
|
||||
temp = ((color0 & 0x07E0) >> 5) * 255 + 32;
|
||||
unsigned char g0 = (unsigned char)((temp/64 + temp)/64);
|
||||
temp = (color0 & 0x001F) * 255 + 16;
|
||||
unsigned char b0 = (unsigned char)((temp/32 + temp)/32);
|
||||
|
||||
temp = (color1 >> 11) * 255 + 16;
|
||||
unsigned char r1 = (unsigned char)((temp/32 + temp)/32);
|
||||
temp = ((color1 & 0x07E0) >> 5) * 255 + 32;
|
||||
unsigned char g1 = (unsigned char)((temp/64 + temp)/64);
|
||||
temp = (color1 & 0x001F) * 255 + 16;
|
||||
unsigned char b1 = (unsigned char)((temp/32 + temp)/32);
|
||||
|
||||
unsigned long code = *reinterpret_cast<const unsigned long *>(blockStorage + 12);
|
||||
|
||||
for (int j=0; j < 4; j++)
|
||||
{
|
||||
for (int i=0; i < 4; i++)
|
||||
{
|
||||
int alphaCodeIndex = 3*(4*j+i);
|
||||
int alphaCode;
|
||||
|
||||
if (alphaCodeIndex <= 12)
|
||||
{
|
||||
alphaCode = (alphaCode2 >> alphaCodeIndex) & 0x07;
|
||||
}
|
||||
else if (alphaCodeIndex == 15)
|
||||
{
|
||||
alphaCode = (alphaCode2 >> 15) | ((alphaCode1 << 1) & 0x06);
|
||||
}
|
||||
else // alphaCodeIndex >= 18 && alphaCodeIndex <= 45
|
||||
{
|
||||
alphaCode = (alphaCode1 >> (alphaCodeIndex - 16)) & 0x07;
|
||||
}
|
||||
|
||||
unsigned char finalAlpha;
|
||||
if (alphaCode == 0)
|
||||
{
|
||||
finalAlpha = alpha0;
|
||||
}
|
||||
else if (alphaCode == 1)
|
||||
{
|
||||
finalAlpha = alpha1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alpha0 > alpha1)
|
||||
{
|
||||
finalAlpha = ((8-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/7;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (alphaCode == 6)
|
||||
finalAlpha = 0;
|
||||
else if (alphaCode == 7)
|
||||
finalAlpha = 255;
|
||||
else
|
||||
finalAlpha = ((6-alphaCode)*alpha0 + (alphaCode-1)*alpha1)/5;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char colorCode = (code >> 2*(4*j+i)) & 0x03;
|
||||
|
||||
unsigned long finalColor;
|
||||
switch (colorCode)
|
||||
{
|
||||
case 0:
|
||||
finalColor = PackRGBA(r0, g0, b0, finalAlpha);
|
||||
break;
|
||||
case 1:
|
||||
finalColor = PackRGBA(r1, g1, b1, finalAlpha);
|
||||
break;
|
||||
case 2:
|
||||
finalColor = PackRGBA((2*r0+r1)/3, (2*g0+g1)/3, (2*b0+b1)/3, finalAlpha);
|
||||
break;
|
||||
case 3:
|
||||
finalColor = PackRGBA((r0+2*r1)/3, (g0+2*g1)/3, (b0+2*b1)/3, finalAlpha);
|
||||
break;
|
||||
}
|
||||
|
||||
if (x + i < width)
|
||||
image[(y + j)*width + (x + i)] = finalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// void BlockDecompressImageDXT5(): Decompresses all the blocks of a DXT5 compressed texture and stores the resulting pixels in 'image'.
|
||||
//
|
||||
// unsigned long width: Texture width.
|
||||
// unsigned long height: Texture height.
|
||||
// const unsigned char *blockStorage: pointer to compressed DXT5 blocks.
|
||||
// unsigned long *image: pointer to the image where the decompressed pixels will be stored.
|
||||
|
||||
void BlockDecompressImageDXT5(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image)
|
||||
{
|
||||
unsigned long blockCountX = (width + 3) / 4;
|
||||
unsigned long blockCountY = (height + 3) / 4;
|
||||
unsigned long blockWidth = (width < 4) ? width : 4;
|
||||
unsigned long blockHeight = (height < 4) ? height : 4;
|
||||
|
||||
for (unsigned long j = 0; j < blockCountY; j++)
|
||||
{
|
||||
for (unsigned long i = 0; i < blockCountX; i++) DecompressBlockDXT5(i*4, j*4, width, blockStorage + i * 16, image);
|
||||
blockStorage += blockCountX * 16;
|
||||
}
|
||||
}
|
||||
|
10
deps/s3tc-dxt-decompression/s3tc.h
vendored
Normal file
10
deps/s3tc-dxt-decompression/s3tc.h
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef S3TC_H
|
||||
#define S3TC_H
|
||||
|
||||
unsigned long PackRGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
|
||||
void DecompressBlockDXT1(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image);
|
||||
void BlockDecompressImageDXT1(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image);
|
||||
void DecompressBlockDXT5(unsigned long x, unsigned long y, unsigned long width, const unsigned char *blockStorage, unsigned long *image);
|
||||
void BlockDecompressImageDXT5(unsigned long width, unsigned long height, const unsigned char *blockStorage, unsigned long *image);
|
||||
|
||||
#endif // S3TC_H
|
@ -17,6 +17,10 @@ namespace Components
|
||||
{
|
||||
if (name[0] == '*') name.erase(name.begin());
|
||||
|
||||
if (Utils::StartsWith(name, "reflection_probe")) {
|
||||
CorrectSpecularImage(image);
|
||||
}
|
||||
|
||||
Utils::Stream buffer;
|
||||
buffer.saveArray("IW4xImg" IW4X_IMG_VERSION, 8); // just stick version in the magic since we have an extra char
|
||||
|
||||
@ -59,17 +63,17 @@ namespace Components
|
||||
}
|
||||
};
|
||||
|
||||
translateFlags(Game::IW3::IMG_FLAG_NOPICMIP, Game::IW4::IMG_FLAG_NOPICMIP);
|
||||
translateFlags(Game::IW3::IMG_FLAG_NOMIPMAPS, Game::IW4::IMG_FLAG_NOMIPMAPS);
|
||||
translateFlags(Game::IW3::IMG_FLAG_CUBEMAP, Game::IW4::IMG_FLAG_MAPTYPE_CUBE);
|
||||
translateFlags(Game::IW3::IMG_FLAG_VOLMAP, Game::IW4::IMG_FLAG_MAPTYPE_3D);
|
||||
translateFlags(Game::IW3::IMG_FLAG_STREAMING, Game::IW4::IMG_FLAG_STREAMING);
|
||||
translateFlags(Game::IW3::IMG_FLAG_NOPICMIP, Game::IW4::IMG_FLAG_NOPICMIP);
|
||||
translateFlags(Game::IW3::IMG_FLAG_NOMIPMAPS, Game::IW4::IMG_FLAG_NOMIPMAPS);
|
||||
translateFlags(Game::IW3::IMG_FLAG_CUBEMAP, Game::IW4::IMG_FLAG_MAPTYPE_CUBE);
|
||||
translateFlags(Game::IW3::IMG_FLAG_VOLMAP, Game::IW4::IMG_FLAG_MAPTYPE_3D);
|
||||
translateFlags(Game::IW3::IMG_FLAG_STREAMING, Game::IW4::IMG_FLAG_STREAMING);
|
||||
translateFlags(Game::IW3::IMG_FLAG_LEGACY_NORMALS, Game::IW4::IMG_FLAG_LEGACY_NORMALS);
|
||||
translateFlags(Game::IW3::IMG_FLAG_CLAMP_U, Game::IW4::IMG_FLAG_CLAMP_U);
|
||||
translateFlags(Game::IW3::IMG_FLAG_CLAMP_V, Game::IW4::IMG_FLAG_CLAMP_V);
|
||||
// translateFlags(Game::IW3::IMG_FLAG_DYNAMIC, Game::IW4::IMG_FLAG_DYNAMIC);
|
||||
// translateFlags(Game::IW3::IMG_FLAG_RENDER_TARGET, Game::IW4::IMG_FLAG_RENDER_TARGET);
|
||||
// translateFlags(Game::IW3::IMG_FLAG_SYSTEMMEM, Game::IW4::IMG_FLAG_SYSTEMMEM);
|
||||
translateFlags(Game::IW3::IMG_FLAG_CLAMP_U, Game::IW4::IMG_FLAG_CLAMP_U);
|
||||
translateFlags(Game::IW3::IMG_FLAG_CLAMP_V, Game::IW4::IMG_FLAG_CLAMP_V);
|
||||
// translateFlags(Game::IW3::IMG_FLAG_DYNAMIC, Game::IW4::IMG_FLAG_DYNAMIC);
|
||||
// translateFlags(Game::IW3::IMG_FLAG_RENDER_TARGET, Game::IW4::IMG_FLAG_RENDER_TARGET);
|
||||
// translateFlags(Game::IW3::IMG_FLAG_SYSTEMMEM, Game::IW4::IMG_FLAG_SYSTEMMEM);
|
||||
|
||||
header_iw4.flags |= Game::IW4::IMG_FLAG_ALPHA_WEIGHTED_COLORS;
|
||||
header_iw4.flags |= Game::IW4::IMG_FLAG_GAMMA_SRGB;
|
||||
@ -102,9 +106,128 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void IGfxImage::CorrectSpecularImage(Game::IW3::GfxImage* image) {
|
||||
assert(image->mapType == Game::IW3::MAPTYPE_CUBE);
|
||||
|
||||
auto sides = 6; // Cube has 6 sides!
|
||||
auto channels = 4; // R G B and A
|
||||
const unsigned int iwiHeaderSize = 28; // This would be 32 for IW4, and it's 28 for IW3. This is the size of the header on .IWI file before the actual data
|
||||
|
||||
std::string mapName = MapDumper::GetMapName();
|
||||
|
||||
// Here's the plan :
|
||||
//
|
||||
// iw3 has baked images for specular reflection (metallic reflection, sniper scope reflection)
|
||||
// but they're too contrasted and burnt for proper reflections in iw4
|
||||
// They're usually damaged so badly that we can't use them at all.
|
||||
//
|
||||
// As a workaround, we can use the loadscreen image of the map (as it usually has the same colors as the map or almost)
|
||||
// as a baked reflection probe. We still keep the alpha channel from the original probe so we have a bit of
|
||||
// difference between each probe on the map, but we take the RGB from the loadscreen image.
|
||||
// That image has to be decoded manually because the game doesn't know how to decompress it. For iw3, its
|
||||
// format will always be DXT1.
|
||||
// Then we also need to change the pixel data to make it a bit less contrasted and bright, otherwise
|
||||
// the specular reflections are very aggressive. That's about it! :)
|
||||
|
||||
Game::IW3::XAssetHeader baseMapHeader = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_IMAGE, Utils::VA("loadscreen_%s", mapName.c_str()));
|
||||
auto baseMapImg = baseMapHeader.image;
|
||||
|
||||
const int pixels = channels * baseMapImg->width * baseMapImg->height;
|
||||
|
||||
// Fetch & decompress loadscreen image
|
||||
// Note: We don't consider mipmaps here. Loadscreens shouldn't have mips, but
|
||||
// actually on some custom maps, they do.
|
||||
// I think it's okay. It will be technically incorrect but the colors will be right.
|
||||
|
||||
FileSystem::File baseImg(Utils::VA("images/%s.iwi", Utils::VA("loadscreen_%s", mapName.c_str())));
|
||||
std::vector<uint32_t> replacementImageBuffer = std::vector<uint32_t>(pixels);
|
||||
unsigned char* iwiData = reinterpret_cast<unsigned char*>(baseImg.GetBuffer().data());
|
||||
unsigned char* dxt1RawDataStart = &iwiData[iwiHeaderSize];
|
||||
BlockDecompressImageDXT1(baseMapImg->width, baseMapImg->height, dxt1RawDataStart, reinterpret_cast<unsigned long*>(&replacementImageBuffer[0]));
|
||||
|
||||
auto sizeOfASide = image->texture.loadDef->resourceSize / 6;
|
||||
int dataIndex = 0;
|
||||
|
||||
std::vector<std::tuple<int, int>> mips = std::vector<std::tuple<int, int>>();
|
||||
if (image->noPicmip == false) // => Has mipmaps
|
||||
{
|
||||
unsigned short maxDimension = max(image->height, image->width);
|
||||
int mipmapFactor = 1;
|
||||
int minBlockSize = 1;
|
||||
int totalSize = 0;
|
||||
|
||||
while (maxDimension != 0)
|
||||
{
|
||||
maxDimension >>= 1;
|
||||
auto x = max(image->width / mipmapFactor, minBlockSize);
|
||||
auto y = max(image->height / mipmapFactor, minBlockSize);
|
||||
totalSize += (x) * (y)*channels;
|
||||
mips.emplace_back(std::tuple<int, int>(x, y));
|
||||
mipmapFactor *= 2;
|
||||
}
|
||||
|
||||
assert(totalSize == sizeOfASide);
|
||||
}
|
||||
else {
|
||||
mips.emplace_back(std::tuple<int, int>(image->width, image->height));
|
||||
}
|
||||
|
||||
float xStep = (float)baseMapImg->width / (float)image->width;
|
||||
float yStep = (float)baseMapImg->height / (float)image->height;
|
||||
|
||||
for (int i = 0; i < mips.size(); i++)
|
||||
{
|
||||
short thisWidth = std::get<0>(mips[i]);
|
||||
short thisHeight = std::get<1>(mips[i]);
|
||||
|
||||
for (size_t side = 0; side < sides; side++)
|
||||
{
|
||||
for (size_t x = 0; x < thisWidth; x++)
|
||||
{
|
||||
for (size_t y = 0; y < thisWidth; y++)
|
||||
{
|
||||
union {
|
||||
char byteValue[4];
|
||||
long longValue;
|
||||
} baseMapPixels;
|
||||
|
||||
// Note: Rotation here is incorrect. It should be the following:
|
||||
//
|
||||
// side 0 is rotated ccw 90°
|
||||
// side 1 is rotated cw 90°
|
||||
// side 2 is rotated 180°
|
||||
// side 3 is not rotated
|
||||
// side 4 is not rotated
|
||||
// side 5 is not rotated
|
||||
//
|
||||
// I just don't have the time to do that at the moment, but feel
|
||||
// free to implement it in the future! switch(side){...}
|
||||
|
||||
size_t newPixelIndex = std::floor(xStep * x) * baseMapImg->width + std::floor(yStep * y);
|
||||
baseMapPixels.longValue = replacementImageBuffer[newPixelIndex];
|
||||
|
||||
for (size_t channel = 0; channel < channels; channel++)
|
||||
{
|
||||
if (channel < channels - 1) {
|
||||
unsigned char newByte = baseMapPixels.byteValue[channel+1];
|
||||
|
||||
newByte = std::clamp(newByte, static_cast<unsigned char>(60), static_cast<unsigned char>(200));
|
||||
newByte = std::lerp(newByte, 127, 0.3);
|
||||
|
||||
image->texture.loadDef->data[dataIndex] = newByte;
|
||||
}
|
||||
|
||||
dataIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int IGfxImage::StoreTexture()
|
||||
{
|
||||
Game::IW3::GfxImageLoadDef ** loadDef = *reinterpret_cast<Game::IW3::GfxImageLoadDef***>(0xE34814);
|
||||
Game::IW3::GfxImageLoadDef** loadDef = *reinterpret_cast<Game::IW3::GfxImageLoadDef***>(0xE34814);
|
||||
Game::IW3::GfxImage* image = *reinterpret_cast<Game::IW3::GfxImage**>(0xE346C4);
|
||||
|
||||
size_t size = 16 + (*loadDef)->resourceSize;
|
||||
@ -127,15 +250,15 @@ namespace Components
|
||||
IGfxImage::IGfxImage()
|
||||
{
|
||||
Command::Add("dumpGfxImage", [](Command::Params params)
|
||||
{
|
||||
if (params.Length() < 2) return;
|
||||
{
|
||||
if (params.Length() < 2) return;
|
||||
|
||||
Game::IW3::GfxImage image;
|
||||
image.name = params[1];
|
||||
image.texture.loadDef = nullptr;
|
||||
Game::IW3::GfxImage image;
|
||||
image.name = params[1];
|
||||
image.texture.loadDef = nullptr;
|
||||
|
||||
IGfxImage::Dump(&image);
|
||||
});
|
||||
IGfxImage::Dump(&image);
|
||||
});
|
||||
|
||||
Utils::Hook(0x616E80, IGfxImage::StoreTexture, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x488C00, IGfxImage::ReleaseTexture, HOOK_JUMP).install()->quick();
|
||||
|
@ -15,6 +15,7 @@ namespace Components
|
||||
private:
|
||||
static void Dump(Game::IW3::GfxImage* image);
|
||||
|
||||
static void CorrectSpecularImage(Game::IW3::GfxImage* image);
|
||||
static int StoreTexture();
|
||||
static void ReleaseTexture(Game::IW3::XAssetHeader header);
|
||||
};
|
||||
|
@ -2,12 +2,20 @@
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::string MapDumper::mapName;
|
||||
|
||||
std::string MapDumper::GetMapName() {
|
||||
return MapDumper::mapName;
|
||||
}
|
||||
|
||||
void MapDumper::DumpMap(std::string mapName)
|
||||
{
|
||||
MapDumper::mapName = mapName;
|
||||
std::string bspName = Utils::VA("maps/mp/%s.d3dbsp", mapName.data());
|
||||
|
||||
Logger::Print("Loading map '%s'...\n", mapName.data());
|
||||
Command::Execute(Utils::VA("map %s", mapName.data()), true);
|
||||
Command::Execute(Utils::VA("loadzone %s_load", mapName.data()), true);
|
||||
|
||||
// Search zone index
|
||||
int zoneIndex = 0;
|
||||
|
@ -7,11 +7,15 @@ namespace Components
|
||||
public:
|
||||
const char* getName() override { return "MapDumper"; };
|
||||
|
||||
static std::string GetMapName();
|
||||
|
||||
MapDumper();
|
||||
~MapDumper();
|
||||
|
||||
private:
|
||||
static void DumpMap(std::string mapName);
|
||||
static void DumpLoadedGSCs(std::string mapName);
|
||||
|
||||
static std::string mapName;
|
||||
};
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ namespace Game
|
||||
__int16 dimensions[3];
|
||||
int format;
|
||||
int resourceSize;
|
||||
char data[1];
|
||||
unsigned char data[1];
|
||||
};
|
||||
|
||||
union GfxTexture
|
||||
|
@ -51,6 +51,7 @@ template <size_t S> class Sizer { };
|
||||
#include <version.hpp>
|
||||
|
||||
#include <json11.hpp>
|
||||
#include <s3tc.h>
|
||||
|
||||
#include "Utils/Utils.hpp"
|
||||
#include "Utils/Memory.hpp"
|
||||
|
Loading…
Reference in New Issue
Block a user