Skip to content

Support for SMITE TFC Rendering #273

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion UmodelTool/umodel.project
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,4 @@ sources(MAIN) = {
!message NO EXE_NAME
!endif

target(executable, $EXE_NAME, MAIN + COMP_LIBS + UE4_LIBS + IMG_LIBS + NV_LIBS + MOBILE_LIBS, MAIN)
target(executable, $EXE_NAME, MAIN + COMP_LIBS + UE4_LIBS + IMG_LIBS + NV_LIBS + MOBILE_LIBS + MD5_LIBS, MAIN)
15 changes: 15 additions & 0 deletions Unreal/FileSystem/GameFileSystem.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Core.h"
#include "UnCore.h"
#include "GameFileSystem.h"
#include "GameFileSystemSmite.h"

#include "UnArchiveObb.h"
#include "UnArchivePak.h"
Expand Down Expand Up @@ -816,6 +817,20 @@ void appSetRootDirectory(const char *dir, bool recurse)
}
#endif // GEARS4

#if SMITE
if(GForceGame == GAME_Smite) {
const CGameFileInfo* manifest = CGameFileInfo::Find("MergedFileIndexCache.bin");
if (manifest)
{
LoadSmiteManifest(manifest);
}
else
{
appNotify("Smite: missing MergedFileIndexCache.bin file.");
}
}
#endif

appPrintf("Found %d game files (%d skipped) in %d folders at path \"%s\"\n", GameFiles.Num(), GNumForeignFiles, GameFolders.Num() ? GameFolders.Num()-1 : 0, dir);

#if UNREAL4
Expand Down
74 changes: 74 additions & 0 deletions Unreal/FileSystem/GameFileSystemSmite.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "Core.h"
#include "UnCore.h"
#include "GameFileSystem.h"
#include "GameFileSystemSmite.h"
#include <md5/md5.h>

#if SMITE
static FSmiteManifest* GSmiteManifest = NULL;

void LoadSmiteManifest(const CGameFileInfo* info) {
guard(LoadSmiteManifest);

appPrintf("Loading Smite manifest %s...\n", *info->GetRelativeName());

FArchive* loader = info->CreateReader();
assert(loader);
loader->Game = GAME_Smite;
if(GSmiteManifest != nullptr) {
delete GSmiteManifest;
}
GSmiteManifest = new FSmiteManifest;
GSmiteManifest->Serialize(*loader);
delete loader;

unguard;
}


FMemReader* GetSmiteBlob(const char* name, int name_len, int level, const char* ext) {
guard(GetSmiteBlob);

if(GSmiteManifest == nullptr) {
return nullptr;
}

MD5Context ctx;
md5Init(&ctx);
md5Update(&ctx, (unsigned char*)name, name_len);
md5Finalize(&ctx);

TArray<FSmiteFile>* item = GSmiteManifest->Files.Find(*reinterpret_cast<FGuid*>(ctx.digest));
if(item == nullptr) {
return nullptr;
}

int i;
for(i = 0; i < item->Num(); i++) {
FSmiteFile* entry = item->GetData() + i;
if(entry->tier == level) {
FString* bulk = GSmiteManifest->BulkFiles.Find(entry->guid);
char filename[512];
appSprintf(ARRAY_ARG(filename), "%s.%s", bulk->GetDataArray().GetData(), ext);
const CGameFileInfo* info = CGameFileInfo::Find(filename);
if(info == nullptr) {
appNotify("Smite: can't find tfc %s for %s", filename, name);
return nullptr;
}

FArchive* Ar = info->CreateReader();
Ar->Game = GAME_Smite;
Ar->Seek(entry->offset);
byte *data = (byte*)appMalloc(entry->blob_size);
Ar->Serialize(data, entry->blob_size);
delete Ar;
FMemReader* MemAr = new FMemReader(data, entry->blob_size);
MemAr->Game = GAME_Smite;
return MemAr;
}
}
unguard;

return nullptr;
}
#endif // SMITE
34 changes: 34 additions & 0 deletions Unreal/FileSystem/GameFileSystemSmite.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "Core.h"
#include "UnCore.h"

#if SMITE
struct FSmiteFile {
int32 tier;
int32 size;
int32 offset;
int32 blob_size;
FGuid guid;

friend FArchive& operator<<(FArchive &Ar, FSmiteFile &H)
{
return Ar << H.tier << H.size << H.offset << H.blob_size << H.guid;
}
};

struct FSmiteManifest
{
TMap<FGuid, TArray<FSmiteFile>> Files;
TMap<FGuid, FString> BulkFiles;

void Serialize(FArchive& Ar)
{
Ar << Files;
Ar << BulkFiles;
}
};

void LoadSmiteManifest(const CGameFileInfo* info);

FMemReader* GetSmiteBlob(const char* name, int name_len, int level, const char* ext);

#endif // SMITE
12 changes: 12 additions & 0 deletions Unreal/UnCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,13 @@ class FMemReader : public FArchive
return DataSize;
}

void Free() {
appFree((void*)DataPtr);
DataPtr = nullptr;
DataSize = 0;
ArPos = 0;
}

protected:
const byte *DataPtr;
int DataSize;
Expand Down Expand Up @@ -2555,6 +2562,11 @@ void appReadCompressedChunk(FArchive &Ar, byte *Buffer, int Size, int Compressio
#define BULKDATA_SeparateData 0x40 // unknown name - bulk stored in a different place in the same file
#define BULKDATA_CompressedLzx 0x80 // unknown name

#if SMITE
#define BULKDATA_Unkn 0x100 // ??? on mip 1+ if in tfc
#define BULKDATA_CompressedOodle_SMITE 0x200 // oodle
#endif

#if BLADENSOUL
#define BULKDATA_CompressedLzoEncr 0x100 // encrypted LZO
#endif
Expand Down
101 changes: 101 additions & 0 deletions Unreal/UnrealMaterial/UnTexture3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,97 @@ static int GetRealTextureOffset_MH(const UTexture2D *Obj, int MipIndex)

#endif // MARVEL_HEROES

#if SMITE
#include "../FileSystem/GameFileSystemSmite.h"
#include "../UnrealPackage/UnPackageUE3Reader.h"

static bool LoadBulkTextureSMITE(const UTexture2D* texture, const TArray<FTexture2DMipMap> &MipsArray, int MipIndex, bool verbose) {
FMemReader* MemAr = nullptr;
const FTexture2DMipMap &Mip = MipsArray[MipIndex];

int i;
static char buf[2048];
for(i = 0; i < 4; ++i) {
static char tmp[2048];
texture->GetFullName(ARRAY_ARG(tmp), true, true, false);
switch(i) {
case 0:
appSprintf(ARRAY_ARG(buf), "%s", tmp);
break;
case 1:
if(texture->Package == nullptr) {
continue;
}
appSprintf(ARRAY_ARG(buf), "%s.%s", texture->Package->Name, tmp);
break;
case 2:
appSprintf(ARRAY_ARG(buf), "Textures.%s", tmp);
break;
case 3:
if(texture->Package == nullptr) {
continue;
}
appSprintf(ARRAY_ARG(buf), "%s.Textures.%s", texture->Package->Name, tmp);
break;
}
char *s = buf;
int len = 0;
if(verbose) {
appPrintf("Smite: Finding %s (Mip %d) in MergedFileIndexCache\n", buf, MipIndex);
}
while (*s) {
*s = toupper((unsigned char) *s);
len++;
s++;
}

MemAr = GetSmiteBlob(buf, len, MipIndex, "tfc");
if(MemAr != NULL) {
break;
}
}

if(MemAr == NULL) {
appPrintf("Smite: unable to find %s (Mip %d) in MergedFileIndexCache\n", texture->Name, MipIndex);
return false;
}

FCompressedChunkHeader H;
*MemAr << H;
TArray<FCompressedChunk> Chunks;
FCompressedChunk *Chunk = new (Chunks) FCompressedChunk;
Chunk->UncompressedOffset = 0;
Chunk->UncompressedSize = H.Sum.UncompressedSize;
Chunk->CompressedOffset = 0;
Chunk->CompressedSize = H.Sum.CompressedSize;
FByteBulkData *Bulk = const_cast<FByteBulkData*>(&Mip.Data);
int flags = COMPRESS_LZO;
if (Bulk->BulkDataFlags & BULKDATA_CompressedOodle_SMITE) flags = COMPRESS_OODLE;
else if (Bulk->BulkDataFlags & BULKDATA_CompressedZlib) flags = COMPRESS_ZLIB;
else if (Bulk->BulkDataFlags & BULKDATA_CompressedLzx) flags = COMPRESS_LZX;

FUE3ArchiveReader* Ar = new FUE3ArchiveReader(MemAr, flags, Chunks);
Ar->IsFullyCompressed = true;

if (verbose)
{
appPrintf("Reading %s mip level %d (%dx%d) from TFC\n", texture->Name, MipIndex, Mip.SizeX, Mip.SizeY);
}

Bulk->BulkDataSizeOnDisk = H.Sum.UncompressedSize;
Bulk->ElementCount = H.Sum.UncompressedSize;
Bulk->BulkDataOffsetInFile = 0;
int backup = Bulk->BulkDataFlags;
Bulk->BulkDataFlags = 0; // wipe compression flags temporarily
Bulk->SerializeData(*Ar);
Bulk->BulkDataFlags = backup;

MemAr->Free();
delete Ar;
return true;
}
#endif // SMITE


bool UTexture2D::LoadBulkTexture(const TArray<FTexture2DMipMap> &MipsArray, int MipIndex, const char* tfcSuffix, bool verbose) const
{
Expand All @@ -656,6 +747,11 @@ bool UTexture2D::LoadBulkTexture(const TArray<FTexture2DMipMap> &MipsArray, int
FStaticString<MAX_PACKAGE_PATH> bulkFileName;
if (TextureFileCacheName != "None")
{
#if SMITE
if(Package && Package->Game == GAME_Smite) {
return LoadBulkTextureSMITE(this, MipsArray, MipIndex, verbose);
}
#endif
// TFC file is assigned
bulkFileName = *TextureFileCacheName;

Expand Down Expand Up @@ -1014,6 +1110,11 @@ bool UTexture2D::GetTextureData(CTextureData &TexData) const
//?? Separate this function ?
//!! * -notfc cmdline switch
//!! * material viewer: support switching mip levels (for xbox decompression testing)
#if SMITE
if(Package && Package->Game == GAME_Smite) {
bulkFailed = false;
} else
#endif
if (Bulk.BulkDataFlags & BULKDATA_Unused) continue; // mip level is stripped
if (!(Bulk.BulkDataFlags & BULKDATA_StoreInSeparateFile)) continue; // equals to BULKDATA_PayloadAtEndOfFile for UE4
// some optimization in a case of missing bulk file
Expand Down
4 changes: 4 additions & 0 deletions common.project
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ sources(UE4_LIBS) = {
$R/libs/rijndael/*.c
}

LIBINCLUDES += $R/libs/md5
sources(MD5_LIBS) = {
$R/libs/md5/*.c
}

#------------------------------------------------
# Project-specific options
Expand Down
1 change: 1 addition & 0 deletions libs/md5/SOURCE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://github.com/Zunawe/md5-c
Loading