FArchive& FArray::Serialize(FArchive &Ar, void (*Serializer)(FArchive&, void*), int elementSize) { int i = 0; guard(TArray::Serialize); //-- if (Ar.IsLoading) Empty(); -- cleanup is done in TArray serializer (do not need // -- to pass array eraser/destructor to this function) // Here: // 1) when loading: 'this' array is empty (cleared from TArray's operator<<) // 2) when saving : data is not modified by this function // serialize data count int Count = DataCount; if (GameUsesFCompactIndex(Ar)) Ar << AR_INDEX(Count); else Ar << Count; if (Ar.IsLoading) { // loading array items - should prepare array Empty(Count, elementSize); DataCount = Count; } // perform serialization itself void *ptr; for (i = 0, ptr = DataPtr; i < Count; i++, ptr = OffsetPointer(ptr, elementSize)) Serializer(Ar, ptr); return Ar; unguardf("%d/%d", i, DataCount); }
void Sys_ProcessMessages() { #if !DEDICATED_ONLY guard(Sys_ProcessMessages); MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) GIsRequestingExit = true; // TranslateMessage() used for convert VK_XXX messages to WM_CHAR-like // We don't need this, but keep TranslateMessage() just in case of compatibility // with future Windows versions (at least in Win2K MS implemented "some feature", // which requires to call this function -- what feature - they don't tell) TranslateMessage(&msg); guard(DispatchMessage); DispatchMessage(&msg); unguardf(("msg=%X", msg.message)); } // grab frame time; this should be in MainLoop ? sys_frame_time = appMilliseconds(); unguard; #endif }
bool LoadWholePackage(UnPackage* Package, IProgressCallback* progress) { guard(LoadWholePackage); if (GFullyLoadedPackages.FindItem(Package) >= 0) return true; // already loaded #if PROFILE appResetProfiler(); #endif UObject::BeginLoad(); for (int idx = 0; idx < Package->Summary.ExportCount; idx++) { if (!IsKnownClass(Package->GetObjectName(Package->GetExport(idx).ClassIndex))) continue; if (progress && !progress->Tick()) return false; Package->CreateExport(idx); } UObject::EndLoad(); GFullyLoadedPackages.Add(Package); #if PROFILE appPrintProfiler(); #endif return true; unguardf("%s", Package->Name); }
void* FArray::GetItem(int index, int elementSize) const { guard(operator[]); assert(index >= 0 && index < DataCount); return OffsetPointer(DataPtr, index * elementSize); unguardf("%d/%d", index, DataCount); }
// This method will grow array's MaxCount. No items will be allocated. // The allocated memory is not initialized because items could be inserted // and removed at any time - so initialization should be performed in // upper level functions like Insert() void FArray::GrowArray(int count, int elementSize) { guard(FArray::GrowArray); assert(count > 0); int prevCount = MaxCount; // check for available space if (DataCount + count > MaxCount) { // not enough space, resize ... MaxCount = ((DataCount + count + 15) / 16) * 16 + 16; if (!IsStatic()) { DataPtr = appRealloc(DataPtr, MaxCount * elementSize); } else { // "static" array becomes non-static void* oldData = DataPtr; // this is a static pointer DataPtr = appMalloc(MaxCount * elementSize); memcpy(DataPtr, oldData, prevCount * elementSize); } } unguardf("%d x %d", count, elementSize); }
void USetupDefinition::InstallTree( const TCHAR* Action, FInstallPoll* Poll, void (USetupDefinition::*Process)( FString Key, FString Value, UBOOL Selected, FInstallPoll* Poll ), USetupGroup* SetupGroup, UBOOL Selected ) { guard(USetupDefinition::InstallTree); if( !SetupGroup ) SetupGroup = RootGroup; // Update selection status. Selected = Selected && SetupGroup->Selected; // Process this item. (this->*Process)( TEXT("GroupSpecial"), SetupGroup->GetName(), Selected, Poll ); TMultiMap<FString,FString>* Map = GConfig->GetSectionPrivate( SetupGroup->GetName(), 0, 1, *ConfigFile ); check(Map); for( TMultiMap<FString,FString>::TIterator It(*Map); It; ++It ) { FString V = Format(It.Value()); (this->*Process)( It.Key(), V, Selected, Poll ); } // Handle all subgroups here. for( TArray<USetupGroup*>::TIterator ItG(SetupGroup->Subgroups); ItG; ++ItG ) InstallTree( Action, Poll, Process, *ItG, Selected ); unguardf(( TEXT("(%s %s)"), Action, SetupGroup->GetFullName() )); }
UProperty* FindStrictScriptVariable( UStruct* InStruct, const TCHAR* PropName) { guard(FindStrictScriptVariable); FName NAME_PropName = FName( PropName, FNAME_Find); check( NAME_PropName != NAME_None ); for( TStrictFieldIterator<UProperty> Prop( InStruct ); Prop; ++Prop ) if( Prop->GetFName() == NAME_PropName ) return *Prop; appThrowf( (TEXT("Property not found in %s!!"), InStruct->GetName() ) ); return NULL; unguardf( (PropName) ); }
void appSetRootDirectory(const char *dir, bool recurse) { guard(appSetRootDirectory); if (dir[0] == 0) dir = "."; // using dir="" will cause scanning of "/dir1", "/dir2" etc (i.e. drive root) appStrncpyz(RootDirectory, dir, ARRAY_COUNT(RootDirectory)); ScanGameDirectory(RootDirectory, recurse); appPrintf("Found %d game files (%d skipped)\n", GNumGameFiles, GNumForeignFiles); #if PRINT_HASH_DISTRIBUTION PrintHashDistribution(); #endif unguardf("dir=%s", dir); }
static bool BioReadBulkCatalogFile(const CGameFileInfo *file) { guard(BioReadBulkCatalogFile); FArchive *Ar = appCreateFileReader(file); // setup for reading Bioshock data Ar->ArVer = 141; Ar->ArLicenseeVer = 0x38; Ar->Game = GAME_Bioshock; // serialize appPrintf("Reading %s\n", file->RelativeName); BioBulkCatalog *cat = new (bioCatalog) BioBulkCatalog; *Ar << *cat; // finalize delete Ar; return true; unguardf("%s", file->RelativeName); }
// This method will grow array's MaxCount. No items will be allocated. // The allocated memory is not initialized because items could be inserted // and removed at any time - so initialization should be performed in // upper level functions like Insert() void FArray::GrowArray(int count, int elementSize) { guard(FArray::GrowArray); assert(count > 0); int prevCount = MaxCount; // check for available space int newCount = DataCount + count; if (newCount > MaxCount) { // Not enough space, resize ... // Allow small initial size of array const int minCount = 4; if (newCount > minCount) { MaxCount = Align(DataCount + count, 16) + 16; } else { MaxCount = minCount; } // Align memory block to reduce fragmentation int dataSize = Align(MaxCount * elementSize, 16); // Recompute MaxCount in a case if alignment increases its capacity MaxCount = dataSize / elementSize; // Reallocate memory if (!IsStatic()) { DataPtr = appRealloc(DataPtr, dataSize); } else { // "static" array becomes non-static void* oldData = DataPtr; // this is a static pointer DataPtr = appMalloc(dataSize); memcpy(DataPtr, oldData, prevCount * elementSize); } } unguardf("%d x %d", count, elementSize); }
//?? TODO: may be pass "Ext" here void appFindGameFiles(const char *Filename, TArray<const CGameFileInfo*>& Files) { guard(appFindGameFiles); if (!appContainsWildcard(Filename)) { const CGameFileInfo* File = appFindGameFile(Filename); if (File) Files.Add(File); return; } // here we're working with wildcard and should iterate over all files char buf[MAX_PACKAGE_PATH]; appStrncpyz(buf, Filename, ARRAY_COUNT(buf)); // replace backslashes bool containsPath = false; for (char* s = buf; *s; s++) { char c = *s; if (c == '\\') { *s = '/'; containsPath = true; } else if (c == '/') { containsPath = true; } } FindPackageWildcardData findData; findData.WildcardContainsPath = containsPath; findData.Wildcard = buf; appEnumGameFiles(FindPackageWildcardCallback, findData); CopyArray(Files, findData.FoundFiles); unguardf("wildcard=%s", Filename); }
void FArray::Empty(int count, int elementSize) { guard(FArray::Empty); DataCount = 0; if (IsStatic()) { if (count <= MaxCount) return; // "static" array becomes non-static, invalidate data pointer DataPtr = NULL; } //!! TODO: perhaps round up 'Max' to 16 bytes, allow comparison below to be 'softer' //!! (i.e. when array is 16 items, and calling Empty(15) - don't reallicate it, unless //!! item size is large if (DataPtr) { // check if we need to release old array if (count == MaxCount) { // the size was not changed return; } // delete old memory block appFree(DataPtr); DataPtr = NULL; } MaxCount = count; if (count) { DataPtr = appMalloc(count * elementSize); } unguardf("%d x %d", count, elementSize); }
model_t *FindModel(const char *name) { guard(R_FindModel); TString<64> Name2; Name2.filename(name); #if 0 // try to load md3 instead of md2 //!! disable later char *ext = strrchr(Name2, '.'); if (ext && !strcmp(ext, ".md2")) { ext[3] = '3'; // ".md2" -> ".md3" if (GFileSystem->FileExists(Name2)) return FindModel(Name2); // md3 model with the same name is not found -- load md2 ext[3] = '2'; // ".md3" -> ".md2" } #endif model_t *m; // search already loaded models for (int i = 0; i < modelCount; i++) { m = modelsArray[i]; if (!m) continue; //?? should not happens if (Name2 == m->Name) { // found if (m->type == MODEL_UNKNOWN) return NULL; // model name was cached to avoid file system lookup again return m; } } if (Name2[0] == '*') Com_DropError("R_FindModel: inline model \"%s\" was not found", *Name2); if (modelCount == MAX_GLMODELS) { appWPrintf("R_FindModel: MAX_GLMODELS\n"); return NULL; } /*----- not found -- load model ------*/ unsigned len; unsigned *file; if (!(file = (unsigned*) GFileSystem->LoadFile(Name2, &len))) { m = new model_t; m->Name = Name2; modelsArray[modelCount++] = m; Com_DPrintf("R_FindModel: not found: %s\n", *Name2); return NULL; // file not found } switch (LittleLong(*file)) { case MD2_IDENT: m = LoadMd2(Name2, (byte*)file, len); break; case MD3_IDENT: m = LoadMd3(Name2, (byte*)file, len); break; case SP2_IDENT: m = LoadSp2(Name2, (byte*)file, len); break; case BYTES4('/','/','S','P'): m = LoadSpr(Name2, (byte*)file, len); break; default: // no error here: simply ignore unknown model formats appWPrintf("R_FindModel: unknown ID 0x%X in %s", (unsigned)LittleLong(*file), name); m = NULL; } if (m) modelsArray[modelCount++] = m; delete file; return m; unguardf(("mdl=%s", name)); }
int appDecompress(byte *CompressedBuffer, int CompressedSize, byte *UncompressedBuffer, int UncompressedSize, int Flags) { guard(appDecompress); #if BLADENSOUL if (GForceGame == GAME_BladeNSoul && Flags == COMPRESS_LZO_ENC) // note: GForceGame is required (to not pass 'Game' here) { if (CompressedSize >= 32) { static const char *key = "qiffjdlerdoqymvketdcl0er2subioxq"; for (int i = 0; i < CompressedSize; i++) CompressedBuffer[i] ^= key[i % 32]; } // overide compression Flags = COMPRESS_LZO; } #endif // BLADENSOUL #if TAO_YUAN if (GForceGame == GAME_TaoYuan) // note: GForceGame is required (to not pass 'Game' here); { static const byte key[] = { 137, 35, 95, 142, 69, 136, 243, 119, 25, 35, 111, 94, 101, 136, 243, 204, 243, 67, 95, 158, 69, 106, 107, 187, 237, 35, 103, 142, 72, 142, 243 }; for (int i = 0; i < CompressedSize; i++) CompressedBuffer[i] ^= key[i % 31]; // note: "N % 31" is not the same as "N & 0x1F" } #endif // TAO_YUAN #if ALICE // this code exists in Alice: Madness Returns only if (GForceGame == GAME_Alice && CompressedSize == UncompressedSize) { // CompressedSize == UncompressedSize -> no compression memcpy(UncompressedBuffer, CompressedBuffer, UncompressedSize); return UncompressedSize; } #endif // ALICE if (Flags == COMPRESS_FIND && CompressedSize >= 2) { byte b1 = CompressedBuffer[0]; byte b2 = CompressedBuffer[1]; // detect compression // zlib: // http://tools.ietf.org/html/rfc1950 // http://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like if ( b1 == 0x78 && // b1=CMF: 7=32k buffer (CINFO), 8=deflate (CM) (b2 == 0x9C || b2 == 0xDA) ) // b2=FLG { Flags = COMPRESS_ZLIB; } else Flags = COMPRESS_LZO; } if (Flags == COMPRESS_LZO) { int r; r = lzo_init(); if (r != LZO_E_OK) appError("lzo_init() returned %d", r); lzo_uint newLen = UncompressedSize; r = lzo1x_decompress_safe(CompressedBuffer, CompressedSize, UncompressedBuffer, &newLen, NULL); if (r != LZO_E_OK) appError("lzo_decompress(%d,%d) returned %d", CompressedSize, UncompressedSize, r); if (newLen != UncompressedSize) appError("len mismatch: %d != %d", newLen, UncompressedSize); return newLen; } if (Flags == COMPRESS_ZLIB) { #if 0 appError("appDecompress: Zlib compression is not supported"); #else unsigned long newLen = UncompressedSize; int r = uncompress(UncompressedBuffer, &newLen, CompressedBuffer, CompressedSize); if (r != Z_OK) appError("zlib uncompress(%d,%d) returned %d", CompressedSize, UncompressedSize, r); // if (newLen != UncompressedSize) appError("len mismatch: %d != %d", newLen, UncompressedSize); -- needed by Bioshock return newLen; #endif } if (Flags == COMPRESS_LZX) { #if SUPPORT_XBOX360 # if !USE_XDK appDecompressLZX(CompressedBuffer, CompressedSize, UncompressedBuffer, UncompressedSize); return UncompressedSize; # else void *context; int r; r = XMemCreateDecompressionContext(0, NULL, 0, &context); if (r < 0) appError("XMemCreateDecompressionContext failed"); unsigned int newLen = UncompressedSize; r = XMemDecompress(context, UncompressedBuffer, &newLen, CompressedBuffer, CompressedSize); if (r < 0) appError("XMemDecompress failed"); if (newLen != UncompressedSize) appError("len mismatch: %d != %d", newLen, UncompressedSize); XMemDestroyDecompressionContext(context); return newLen; # endif // USE_XDK #else // SUPPORT_XBOX360 appError("appDecompress: LZX compression is not supported"); #endif // SUPPORT_XBOX360 } appError("appDecompress: unknown compression flags: %d", Flags); return 0; unguardf("CompSize=%d UncompSize=%d Flags=0x%X", CompressedSize, UncompressedSize, Flags); }
const CGameFileInfo *appFindGameFile(const char *Filename, const char *Ext) { guard(appFindGameFile); #if UNREAL3 bool findStartupPackage = (strcmp(Filename, GStartupPackage) == 0); if (findStartupPackage) return GStartupPackageInfo; #endif char buf[MAX_PACKAGE_PATH]; appStrncpyz(buf, Filename, ARRAY_COUNT(buf)); // replace backslashes const char* ShortFilename = buf; for (char* s = buf; *s; s++) { char c = *s; if (c == '\\') *s = '/'; if (*s == '/') ShortFilename = s + 1; } if (Ext) { // extension is provided assert(!strchr(buf, '.')); } else { // check for extension in filename char *s = strrchr(buf, '.'); if (s) { Ext = s + 1; // remember extension *s = 0; // cut extension } } int nameLen = strlen(ShortFilename); int hash = GetHashForFileName(buf); #ifdef DEBUG_HASH_NAME printf("--> Loading %s (%s, len=%d)\n", buf, ShortFilename, nameLen); #endif CGameFileInfo* bestMatch = NULL; int bestMatchWeight = -1; for (CGameFileInfo* info = GGameFileHash[hash]; info; info = info->HashNext) { #ifdef DEBUG_HASH_NAME printf("----> verify %s\n", info->RelativeName); #endif if (info->Extension - 1 - info->ShortFilename != nameLen) // info->Extension points to char after '.' { // printf("-----> wrong length %d\n", info->Extension - info->ShortFilename); continue; // different filename length } // verify extension if (Ext) { if (stricmp(info->Extension, Ext) != 0) continue; } else { // Ext = NULL => should be any package extension if (!info->IsPackage) continue; } // verify a filename if (strnicmp(info->ShortFilename, ShortFilename, nameLen) != 0) continue; // if (info->ShortFilename[nameLen] != '.') -- verified before extension comparison // continue; // Short filename matched, now compare path before the filename. // Assume 'ShortFilename' is part of 'buf' and 'info->ShortFilename' is part of 'info->RelativeName'. int matchWeight = 0; const char *s = ShortFilename; const char *d = info->ShortFilename; while (--s >= buf && --d >= info->RelativeName) { if (*s != *d) break; matchWeight++; } // printf("--> matched: %s (weight=%d)\n", info->RelativeName, matchWeight); if (matchWeight > bestMatchWeight) { // printf("---> better match\n"); bestMatch = info; bestMatchWeight = matchWeight; } } return bestMatch; unguardf("name=%s ext=%s", Filename, Ext); }
static bool RegisterGameFile(const char *FullName, FVirtualFileSystem* parentVfs = NULL) { guard(RegisterGameFile); // printf("..file %s\n", FullName); // return false when MAX_GAME_FILES if (GNumGameFiles >= ARRAY_COUNT(GameFiles)) return false; if (!parentVfs) // no nested VFSs { const char* ext = strrchr(FullName, '.'); if (ext) { guard(MountVFS); ext++; FVirtualFileSystem* vfs = NULL; FArchive* reader = NULL; #if SUPPORT_ANDROID if (!stricmp(ext, "obb")) { GForcePlatform = PLATFORM_ANDROID; reader = new FFileReader(FullName); if (!reader) return true; reader->Game = GAME_UE3; vfs = new FObbVFS(); } #endif // SUPPORT_ANDROID #if UNREAL4 if (!stricmp(ext, "pak")) { reader = new FFileReader(FullName); if (!reader) return true; reader->Game = GAME_UE4; vfs = new FPakVFS(); //!! detect game by file name } #endif // UNREAL4 //!! process other VFS types here if (vfs) { assert(reader); // read VF directory if (!vfs->AttachReader(reader)) { // something goes wrong delete vfs; delete reader; return true; } // add game files int NumVFSFiles = vfs->NumFiles(); for (int i = 0; i < NumVFSFiles; i++) { if (!RegisterGameFile(vfs->FileName(i), vfs)) return false; } return true; } unguard; } } bool IsPackage; if (FindExtension(FullName, ARRAY_ARG(PackageExtensions))) { IsPackage = true; } else { #if HAS_SUPORT_FILES if (!FindExtension(FullName, ARRAY_ARG(KnownExtensions))) #endif { // perhaps this file was exported by our tool - skip it if (FindExtension(FullName, ARRAY_ARG(SkipExtensions))) return true; // unknown file type if (++GNumForeignFiles >= MAX_FOREIGN_FILES) appError("Too much unknown files - bad root directory (%s)?", RootDirectory); return true; } IsPackage = false; } // create entry CGameFileInfo *info = new CGameFileInfo; GameFiles[GNumGameFiles++] = info; info->IsPackage = IsPackage; info->FileSystem = parentVfs; if (IsPackage) GNumPackageFiles++; if (!parentVfs) { // regular file FILE* f = fopen(FullName, "rb"); if (f) { fseek(f, 0, SEEK_END); info->SizeInKb = (ftell(f) + 512) / 1024; fclose(f); } else { info->SizeInKb = 0; } // cut RootDirectory from filename const char *s = FullName + strlen(RootDirectory) + 1; assert(s[-1] == '/'); appStrncpyz(info->RelativeName, s, ARRAY_COUNT(info->RelativeName)); } else { // file in virtual file system info->SizeInKb = parentVfs->GetFileSize(FullName); appStrncpyz(info->RelativeName, FullName, ARRAY_COUNT(info->RelativeName)); } // find filename const char* s = strrchr(info->RelativeName, '/'); if (s) s++; else s = info->RelativeName; info->ShortFilename = s; // find extension s = strrchr(info->ShortFilename, '.'); if (s) s++; info->Extension = s; // printf(".. -> %s (pkg=%d)\n", info->ShortFilename, info->IsPackage); #if UNREAL3 if (info->IsPackage && (strnicmp(info->ShortFilename, "startup", 7) == 0)) { // Register a startup package // possible name variants: // - startup // - startup_int // - startup_* int startupWeight = 0; if (info->ShortFilename[7] == '.') startupWeight = 30; // "startup.upk" else if (strnicmp(info->ShortFilename+7, "_int.", 5) == 0) startupWeight = 20; // "startup_int.upk" else if (strnicmp(info->ShortFilename+7, "_loc_int.", 9) == 0) startupWeight = 20; // "startup_int.upk" else if (info->ShortFilename[7] == '_') startupWeight = 1; // non-int locale, lower priority - use if when other is not detected if (startupWeight > GStartupPackageInfoWeight) { GStartupPackageInfoWeight = startupWeight; GStartupPackageInfo = info; } } #endif // UNREAL3 // insert CGameFileInfo into hash table int hash = GetHashForFileName(info->ShortFilename); info->HashNext = GGameFileHash[hash]; GGameFileHash[hash] = info; return true; unguardf("%s", FullName); }
bool UTexture::GetTextureData(CTextureData &TexData) const { guard(UTexture::GetTextureData); TexData.Platform = PLATFORM_PC; TexData.OriginalFormatEnum = Format; TexData.OriginalFormatName = EnumToName("ETextureFormat", Format); TexData.Obj = this; TexData.Palette = Palette; const FArchive* PackageAr = GetPackageArchive(); // process external sources for some games #if BIOSHOCK if (PackageAr && PackageAr->Game == GAME_Bioshock && CachedBulkDataSize) //?? check bStripped or Baked ? { byte* CompressedData = FindBioTexture(this); // may be NULL if (CompressedData) { CMipMap* DstMip = new (TexData.Mips) CMipMap; DstMip->CompressedData = CompressedData; DstMip->ShouldFreeData = true; DstMip->USize = USize; DstMip->VSize = VSize; DstMip->DataSize = (int)CachedBulkDataSize; } TexData.Platform = PackageAr->Platform; } #endif // BIOSHOCK #if UC2 if (PackageAr && PackageAr->Engine() == GAME_UE2X) { // try to find texture inside XBox xpr files int DataSize; byte* CompressedData = FindXprData(Name, &DataSize); // may be NULL if (CompressedData) { CMipMap* DstMip = new (TexData.Mips) CMipMap; DstMip->CompressedData = CompressedData; DstMip->ShouldFreeData = true; DstMip->USize = USize; DstMip->VSize = VSize; DstMip->DataSize = DataSize; } } #endif // UC2 if (TexData.Mips.Num() == 0) { // texture was not taken from external source for (int n = 0; n < Mips.Num(); n++) { // find 1st mipmap with non-null data array // reference: DemoPlayerSkins.utx/DemoSkeleton have null-sized 1st 2 mips const FMipmap &Mip = Mips[n]; if (!Mip.DataArray.Num()) continue; CMipMap* DstMip = new (TexData.Mips) CMipMap; DstMip->CompressedData = &Mip.DataArray[0]; DstMip->ShouldFreeData = false; DstMip->USize = Mip.USize; DstMip->VSize = Mip.VSize; DstMip->DataSize = Mip.DataArray.Num(); break; } } ETexturePixelFormat intFormat; //?? return old code back - UE1 and UE2 differs in codes 6 and 7 only if (Package && (PackageAr->Engine() == GAME_UE1)) { // UE1 has different ETextureFormat layout switch (Format) { case 0: intFormat = TPF_P8; break; // case 1: // intFormat = TPF_RGB32; // in script source code: TEXF_RGB32, but TEXF_RGBA7 in .h // break; // case 2: // intFormat = TPF_RGB64; // in script source code: TEXF_RGB64, but TEXF_RGB16 in .h // break; case 3: intFormat = TPF_DXT1; break; case 4: intFormat = TPF_RGB8; break; case 5: intFormat = TPF_BGRA8; break; // newer UE1 versions has DXT3 and DXT5 case 6: intFormat = TPF_DXT3; break; case 7: intFormat = TPF_DXT5; break; default: appNotify("Unknown UE1 texture format: %d", Format); return false; } } else { // UE2 switch (Format) { case TEXF_P8: intFormat = TPF_P8; break; case TEXF_DXT1: intFormat = TPF_DXT1; break; case TEXF_RGB8: intFormat = TPF_RGB8; break; case TEXF_RGBA8: intFormat = TPF_BGRA8; break; case TEXF_DXT3: intFormat = TPF_DXT3; break; case TEXF_DXT5: intFormat = TPF_DXT5; break; case TEXF_L8: intFormat = TPF_G8; break; case TEXF_CxV8U8: intFormat = TPF_V8U8_2; break; case TEXF_DXT5N: intFormat = TPF_DXT5N; break; case TEXF_3DC: intFormat = TPF_BC5; break; default: appNotify("Unknown UE2 texture format: %s (%d)", TexData.OriginalFormatName, Format); return false; } } TexData.Format = intFormat; #if BIOSHOCK && SUPPORT_XBOX360 if (TexData.Mips.Num() && TexData.Platform == PLATFORM_XBOX360) { for (int MipLevel = 0; MipLevel < TexData.Mips.Num(); MipLevel++) TexData.DecodeXBox360(MipLevel); } #endif #if BIOSHOCK if (Package && PackageAr->Game == GAME_Bioshock) { // This game has DataSize stored for all mipmaps, we should compute side of 1st mipmap // in order to accept this value when uploading texture to video card (some vendors rejects // large values) //?? Place code to CTextureData method? const CPixelFormatInfo &Info = PixelFormatInfo[intFormat]; int numBlocks = TexData.Mips[0].USize * TexData.Mips[0].VSize / (Info.BlockSizeX * Info.BlockSizeY); // used for validation only int requiredDataSize = numBlocks * Info.BytesPerBlock; if (requiredDataSize > TexData.Mips[0].DataSize) { appNotify("Bioshock texture %s: data too small; %dx%d, requires %X bytes, got %X\n", Name, TexData.Mips[0].USize, TexData.Mips[0].VSize, requiredDataSize, TexData.Mips[0].DataSize); } else if (requiredDataSize < TexData.Mips[0].DataSize) { // appPrintf("Bioshock texture %s: stripping data size from %X to %X\n", Name, TexData.Mips[0].DataSize, requiredDataSize); TexData.Mips[0].DataSize = requiredDataSize; } } #endif // BIOSHOCK return (TexData.Mips.Num() > 0); unguardf("%s", Name); }
static bool ReadXprFile(const CGameFileInfo *file) { guard(ReadXprFile); FArchive *Ar = appCreateFileReader(file); int Tag, FileLen, DataStart, DataCount; *Ar << Tag << FileLen << DataStart << DataCount; //?? "XPR0" - xpr variant with a single object (texture) inside if (Tag != BYTES4('X','P','R','1')) { #if XPR_DEBUG appPrintf("Unknown XPR tag in %s\n", file->RelativeName); #endif delete Ar; return true; } #if XPR_DEBUG appPrintf("Scanning %s ...\n", file->RelativeName); #endif XprInfo *Info = new(xprFiles) XprInfo; Info->File = file; Info->DataStart = DataStart; // read filelist int i; for (i = 0; i < DataCount; i++) { int NameOffset, DataOffset; *Ar << NameOffset << DataOffset; int savePos = Ar->Tell(); Ar->Seek(NameOffset + 12); // read name char c, buf[256]; int n = 0; while (true) { *Ar << c; if (n < ARRAY_COUNT(buf)) buf[n++] = c; if (!c) break; } buf[ARRAY_COUNT(buf)-1] = 0; // just in case // create item XprEntry *Entry = new(Info->Items) XprEntry; appStrncpyz(Entry->Name, buf, ARRAY_COUNT(Entry->Name)); Entry->DataOffset = DataOffset + 12; assert(Entry->DataOffset < DataStart); // seek back Ar->Seek(savePos); // setup size of previous item if (i >= 1) { XprEntry *PrevEntry = &Info->Items[i - 1]; PrevEntry->DataSize = Entry->DataOffset - PrevEntry->DataOffset; } // setup size of the last item if (i == DataCount - 1) Entry->DataSize = DataStart - Entry->DataOffset; } // scan data // data block is either embedded in this block or followed after DataStart position for (i = 0; i < DataCount; i++) { XprEntry *Entry = &Info->Items[i]; #if XPR_DEBUG // appPrintf(" %08X [%08X] %s\n", Entry->DataOffset, Entry->DataSize, Entry->Name); #endif Ar->Seek(Entry->DataOffset); int id; *Ar << id; switch (id) { case 0x80020001: // header is 4 dwords + immediately followed data Entry->DataOffset += 4 * 4; Entry->DataSize -= 4 * 4; break; case 0x00040001: // header is 5 dwords + external data { int pos; *Ar << pos; Entry->DataOffset = DataStart + pos; } break; case 0x00020001: // header is 4 dwords + external data { int d1, d2, pos; *Ar << d1 << d2 << pos; Entry->DataOffset = DataStart + pos; } break; default: // header is 2 dwords - offset and size + external data { int pos; *Ar << pos; Entry->DataOffset = DataStart + pos; } break; } } // setup sizes of blocks placed after DataStart (not embedded into file list) for (i = 0; i < DataCount; i++) { XprEntry *Entry = &Info->Items[i]; if (Entry->DataOffset < DataStart) continue; // embedded data // Entry points to a data block placed after DataStart position // we should find a next block int NextPos = FileLen; for (int j = i + 1; j < DataCount; j++) { XprEntry *NextEntry = &Info->Items[j]; if (NextEntry->DataOffset < DataStart) continue; // embedded data NextPos = NextEntry->DataOffset; break; } Entry->DataSize = NextPos - Entry->DataOffset; } #if XPR_DEBUG for (i = 0; i < DataCount; i++) { XprEntry *Entry = &Info->Items[i]; appPrintf(" %3d %08X [%08X] .. %08X %s\n", i, Entry->DataOffset, Entry->DataSize, Entry->DataOffset + Entry->DataSize, Entry->Name); } #endif delete Ar; return true; unguardf("%s", file->RelativeName); }
void CSkelMeshViewer::FindUE4Animations() { #if UNREAL4 guard(CSkelMeshViewer::FindUE4Animations); if (!bIsUE4Mesh) return; if (!Skeleton) { appPrintf("No Skeleton object attached to the mesh, doing nothing\n"); return; } // Find all packages TArray<const CGameFileInfo*> PackageInfos; appEnumGameFiles<TArray<const CGameFileInfo*> >( // won't compile with lambda without explicitly providing template argument [](const CGameFileInfo* file, TArray<const CGameFileInfo*>& param) -> bool { param.Add(file); return true; }, PackageInfos); UIProgressDialog progress; progress.Show("Finding animations"); progress.SetDescription("Scanning package"); // Perform full scan to be able to locate AnimSequence objects if (!ScanContent(PackageInfos, &progress)) { appPrintf("Interrupted by user\n"); return; } // Find potential uasset files with animations TArray<UnPackage*> packagesToLoad; packagesToLoad.Empty(256); const char* lookupSkeletonName = Skeleton->Name; for (int i = 0; i < PackageInfos.Num(); i++) { UnPackage* package = PackageInfos[i]->Package; bool found = false; for (int importIndex = 0; importIndex < package->Summary.ImportCount; importIndex++) { FObjectImport& imp = package->GetImport(importIndex); const char* ObjectClass = *imp.ClassName; const char* ObjectName = *imp.ObjectName; if (!stricmp(ObjectClass, "Skeleton") && !stricmp(ObjectName, lookupSkeletonName)) { // This uasset refers to the Skeleton object with the same name, check if this // is an exactly the same Skeleton object as we're using const char* referencedFilename = package->GetObjectPackageName(imp.PackageIndex); const CGameFileInfo* referencedFile = appFindGameFile(referencedFilename); if (!stricmp(Skeleton->Package->Filename, *referencedFile->GetRelativeName())) { found = true; break; } } } if (!found) continue; // this package doesn't use our Skeleton // Now, if this package has animation sequence - enqueue it for loading if (PackageInfos[i]->NumAnimations) { packagesToLoad.Add(package); } } // Sort packages by name for easier navigation after loading packagesToLoad.Sort([](UnPackage* const& a, UnPackage* const& b) -> int { return stricmp(a->Name, b->Name); }); // Load queued packages progress.SetDescription("Loading animations"); for (int i = 0; i < packagesToLoad.Num(); i++) { guard(Load); UnPackage* package = packagesToLoad[i]; if (!progress.Progress(package->Filename, i, packagesToLoad.Num())) break; LoadWholePackage(package); unguardf("%d/%d", i, packagesToLoad.Num()); } unguard; #endif // UNREAL4 }
static bool ScanPackage(const CGameFileInfo *file, ScanPackageData &data) { guard(ScanPackage); if (data.Progress) { if (!data.Progress->Progress(file->RelativeName, data.Index++, GNumPackageFiles)) { data.Cancelled = true; return false; } } // read a few first bytes as integers FArchive *Ar = appCreateFileReader(file); unsigned int FileData[16]; Ar->Serialize(FileData, sizeof(FileData)); delete Ar; unsigned Tag = FileData[0]; if (Tag == PACKAGE_FILE_TAG_REV) { // big-endian package appReverseBytes(&FileData, ARRAY_COUNT(FileData), sizeof(FileData[0])); } else if (Tag != PACKAGE_FILE_TAG) //?? possibly Lineage2 file etc { //!! use CreatePackageLoader() here to allow scanning of packages with custom header (Lineage etc); //!! do that only when something "strange" within data noticed return true; } unsigned int Version = FileData[1]; FileInfo Info; #if UNREAL4 if ((Version & 0xFFFFF000) == 0xFFFFF000) { // next fields are: int VersionUE3, Version, LicenseeVersion Info.Ver = FileData[3]; Info.LicVer = FileData[4]; } else #endif // UNREAL4 { Info.Ver = Version & 0xFFFF; Info.LicVer = Version >> 16; } Info.Count = 0; strcpy(Info.FileName, file->RelativeName); // printf("%s - %d/%d\n", file->RelativeName, Info.Ver, Info.LicVer); int Index = INDEX_NONE; for (int i = 0; i < data.PkgInfo->Num(); i++) { FileInfo &Info2 = (*data.PkgInfo)[i]; if (Info2.Ver == Info.Ver && Info2.LicVer == Info.LicVer) { Index = i; break; } } if (Index == INDEX_NONE) Index = data.PkgInfo->Add(Info); // update info FileInfo& fileInfo = (*data.PkgInfo)[Index]; fileInfo.Count++; // combine filename char *s = fileInfo.FileName; char *d = Info.FileName; while (*s == *d && *s != 0) { s++; d++; } *s = 0; return true; unguardf("%s", file->RelativeName); }