Skip to content

Commit

Permalink
Add TFC support for Smite
Browse files Browse the repository at this point in the history
  • Loading branch information
yretenai committed Jun 10, 2022
1 parent c444911 commit 8db4b23
Show file tree
Hide file tree
Showing 10 changed files with 499 additions and 1 deletion.
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(Ar, 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

0 comments on commit 8db4b23

Please sign in to comment.