int FIWadManager::ScanIWAD (const char *iwad) { FResourceFile *iwadfile = FResourceFile::OpenResourceFile(iwad, NULL, true); if (iwadfile != NULL) { ClearChecks(); for(DWORD ii = 0; ii < iwadfile->LumpCount(); ii++) { FResourceLump *lump = iwadfile->GetLump(ii); CheckLumpName(lump->Name); if (lump->FullName != NULL) { if (strnicmp(lump->FullName, "maps/", 5) == 0) { FString mapname(lump->FullName+5, strcspn(lump->FullName+5, ".")); CheckLumpName(mapname); } else if (0 == strlen(lump->Name) && strlen(lump->FullName) > 8) { CheckLumpName(lump->FullName); } } } delete iwadfile; } return GetIWadInfo(); }
FResourceFile *CheckDir(const char *filename, FileReader *file, bool quiet) { FResourceFile *rf = new FDirectory(filename); if (rf->Open(quiet)) return rf; delete rf; return NULL; }
FResourceFile *CheckLump(const char *filename, FileReader *file, bool quiet) { // always succeeds FResourceFile *rf = new FLumpFile(filename, file); if (rf->Open(quiet)) return rf; delete rf; return NULL; }
FResourceFile *CheckPak(const char *filename, FileReader *file, bool quiet) { char head[4]; if (file->GetLength() >= 12) { file->Seek(0, SEEK_SET); file->Read(&head, 4); file->Seek(0, SEEK_SET); if (!memcmp(head, "PACK", 4)) { FResourceFile *rf = new FPakFile(filename, file); if (rf->Open(quiet)) return rf; delete rf; } } return NULL; }
FResourceFile *CheckGRP(const char *filename, FileReader *file, bool quiet) { char head[12]; if (file->GetLength() >= 12) { file->Seek(0, SEEK_SET); file->Read(&head, 12); file->Seek(0, SEEK_SET); if (!memcmp(head, "KenSilverman", 12)) { FResourceFile *rf = new FGrpFile(filename, file); if (rf->Open(quiet)) return rf; delete rf; } } return NULL; }
FResourceFile *CheckZip(const char *filename, FileReader *file, bool quiet) { char head[4]; if (file->GetLength() >= (long)sizeof(FZipLocalFileHeader)) { file->Seek(0, SEEK_SET); file->Read(&head, 4); file->Seek(0, SEEK_SET); if (!memcmp(head, "PK\x3\x4", 4)) { FResourceFile *rf = new FZipFile(filename, file); if (rf->Open(quiet)) return rf; delete rf; } } return NULL; }
FResourceFile *Check7Z(const char *filename, FileReader *file, bool quiet) { char head[k7zSignatureSize]; if (file->GetLength() >= k7zSignatureSize) { file->Seek(0, SEEK_SET); file->Read(&head, k7zSignatureSize); file->Seek(0, SEEK_SET); if (!memcmp(head, k7zSignature, k7zSignatureSize)) { FResourceFile *rf = new F7ZFile(filename, file); if (rf->Open(quiet)) return rf; rf->Reader = NULL; // to avoid destruction of reader delete rf; } } return NULL; }
void FIWadManager::ParseIWadInfos(const char *fn) { FResourceFile *resfile = FResourceFile::OpenResourceFile(fn, NULL, true); if (resfile != NULL) { DWORD cnt = resfile->LumpCount(); for(int i=cnt-1; i>=0; i--) { FResourceLump *lmp = resfile->GetLump(i); if (lmp->Namespace == ns_global && !stricmp(lmp->Name, "IWADINFO")) { // Found one! ParseIWadInfo(resfile->Filename, (const char*)lmp->CacheLump(), lmp->LumpSize); break; } } delete resfile; } if (mIWadNames.Size() == 0 || mIWads.Size() == 0) { I_FatalError("No IWAD definitions found"); } }
void FWadCollection::AddFile (const char *filename, FileReader *wadinfo) { int startlump; bool isdir = false; if (wadinfo == NULL) { // Does this exist? If so, is it a directory? if (!DirEntryExists(filename, &isdir)) { Printf(TEXTCOLOR_RED "%s: File or Directory not found\n", filename); PrintLastError(); return; } if (!isdir) { try { wadinfo = new FileReader(filename); } catch (CRecoverableError &err) { // Didn't find file Printf (TEXTCOLOR_RED "%s\n", err.GetMessage()); PrintLastError (); return; } } } if (!batchrun) Printf (" adding %s", filename); startlump = NumLumps; FResourceFile *resfile; if (!isdir) resfile = FResourceFile::OpenResourceFile(filename, wadinfo); else resfile = FResourceFile::OpenDirectory(filename); if (resfile != NULL) { uint32_t lumpstart = LumpInfo.Size(); resfile->SetFirstLump(lumpstart); for (uint32_t i=0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); FWadCollection::LumpRecord *lump_p = &LumpInfo[LumpInfo.Reserve(1)]; lump_p->lump = lump; lump_p->wadnum = Files.Size(); } if (static_cast<int>(Files.Size()) == GetIwadNum() && gameinfo.gametype == GAME_Strife && gameinfo.flags & GI_SHAREWARE) { resfile->FindStrifeTeaserVoices(); } Files.Push(resfile); for (uint32_t i=0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); if (lump->Flags & LUMPF_EMBEDDED) { FString path; path.Format("%s:%s", filename, lump->FullName.GetChars()); FileReader *embedded = lump->NewReader(); AddFile(path, embedded); } } if (hashfile) { uint8_t cksum[16]; char cksumout[33]; memset(cksumout, 0, sizeof(cksumout)); FileReader *reader = wadinfo; if (reader != NULL) { MD5Context md5; reader->Seek(0, SEEK_SET); md5.Update(reader, reader->GetLength()); md5.Final(cksum); for (size_t j = 0; j < sizeof(cksum); ++j) { sprintf(cksumout + (j * 2), "%02X", cksum[j]); } fprintf(hashfile, "file: %s, hash: %s, size: %ld\n", filename, cksumout, reader->GetLength()); } else fprintf(hashfile, "file: %s, Directory structure\n", filename); for (uint32_t i = 0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); if (!(lump->Flags & LUMPF_EMBEDDED)) { reader = lump->NewReader(); MD5Context md5; md5.Update(reader, lump->LumpSize); md5.Final(cksum); for (size_t j = 0; j < sizeof(cksum); ++j) { sprintf(cksumout + (j * 2), "%02X", cksum[j]); } fprintf(hashfile, "file: %s, lump: %s, hash: %s, size: %d\n", filename, lump->FullName.IsNotEmpty() ? lump->FullName.GetChars() : lump->Name, cksumout, lump->LumpSize); delete reader; } } } return; } }
static EIWADType ScanIWAD (const char *iwad) { static const char checklumps[][8] = { "AD2LIB", "E1M1", "E4M2", "MAP01", "MAP40", "MAP60", "TITLE", "REDTNT2", "CAMO1", { 'E','X','T','E','N','D','E','D'}, "ENDSTRF", "MAP33", "INVCURS", { 'F','R','E','E','D','O','O','M' }, { 'B','L','A','S','P','H','E','M' }, "W94_1", { 'P','O','S','S','H','0','M','0' }, "CYCLA1", "FLMBA1", "MAPINFO", "0HAWK01", "0CARA3", "0NOSE1", { 'G','A','M','E','I','N','F','O' }, "E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9", "E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9", "DPHOOF","BFGGA0","HEADA1","CYBRA1", { 'S','P','I','D','A','1','D','1' }, }; #define NUM_CHECKLUMPS (countof(checklumps)) enum { Check_ad2lib, Check_e1m1, Check_e4m1, Check_map01, Check_map40, Check_map60, Check_title, Check_redtnt2, Check_cam01, Check_Extended, Check_endstrf, Check_map33, Check_invcurs, Check_FreeDoom, Check_Blasphem, Check_W94_1, Check_POSSH0M0, Check_Cycla1, Check_Flmba1, Check_Mapinfo, Check_Hawk, Check_Car, Check_Nose, Check_Gameinfo, Check_e2m1 }; bool lumpsfound[NUM_CHECKLUMPS]; size_t i; memset (lumpsfound, 0, sizeof(lumpsfound)); FResourceFile *iwadfile = FResourceFile::OpenResourceFile(iwad, NULL, true); if (iwadfile != NULL) { for(DWORD ii = 0; ii < iwadfile->LumpCount(); ii++) { FResourceLump *lump = iwadfile->GetLump(ii); size_t j; for (j = 0; j < NUM_CHECKLUMPS; j++) { if (!lumpsfound[j]) { if (strnicmp (lump->Name, checklumps[j], 8) == 0) { lumpsfound[j] = true; break; } // Check for maps inside zips, too. else if (lump->FullName != NULL) { if (checklumps[j][0] == 'E' && checklumps[j][2] == 'M' && checklumps[j][4] == '\0') { if (strnicmp(lump->FullName, "maps/", 5) == 0 && strnicmp(lump->FullName + 5, checklumps[j], 4) == 0 && stricmp(lump->FullName + 9, ".wad") == 0) { lumpsfound[j] = true; break; } } else if (checklumps[j][0] == 'M' && checklumps[j][1] == 'A' && checklumps[j][2] == 'P' && checklumps[j][5] == '\0') { if (strnicmp(lump->FullName, "maps/", 5) == 0 && strnicmp(lump->FullName + 5, checklumps[j], 5) == 0 && stricmp(lump->FullName + 10, ".wad") == 0) { lumpsfound[j] = true; break; } } } } } } delete iwadfile; } // Always check for custom iwads first. #if 0 if (lumpsfound[Check_Gameinfo]) { return IWAD_Custom; } #endif if (lumpsfound[Check_title] && lumpsfound[Check_map60]) { return IWAD_HexenDK; } else if (lumpsfound[Check_map33] && lumpsfound[Check_endstrf]) { if (lumpsfound[Check_map01]) { return IWAD_Strife; } else if (lumpsfound[Check_invcurs]) { return IWAD_StrifeTeaser2; // Strife0.wad from 14 Mar 1996 } else { return IWAD_StrifeTeaser; // Strife0.wad from 22 Feb 1996 } } else if (lumpsfound[Check_map01]) { if (lumpsfound[Check_ad2lib]) { return IWAD_ActionDoom2; } else if (lumpsfound[Check_Hawk] && lumpsfound[Check_Car] && lumpsfound[Check_Nose]) { return IWAD_Harmony; } else if (lumpsfound[Check_FreeDoom]) { // Is there a 100% reliable way to tell FreeDoom and FreeDM // apart based solely on the lump names? if (strstr(iwad, "freedm.wad") || strstr(iwad, "FREEDM.WAD")) { return IWAD_FreeDM; } else { return IWAD_FreeDoom; } } else if (lumpsfound[Check_redtnt2]) { return IWAD_Doom2TNT; } else if (lumpsfound[Check_cam01]) { return IWAD_Doom2Plutonia; } else { if (lumpsfound[Check_title]) { if (lumpsfound[Check_map40]) { return IWAD_Hexen; } else { return IWAD_HexenDemo; } } else { return IWAD_Doom2; } } } else if (lumpsfound[Check_e1m1]) { if (lumpsfound[Check_title]) { if (!lumpsfound[Check_e2m1]) { return IWAD_HereticShareware; } else { if (lumpsfound[Check_Blasphem]) { return IWAD_Blasphemer; } else if (lumpsfound[Check_Extended]) { return IWAD_HereticExtended; } else { return IWAD_Heretic; } } } else if (lumpsfound[Check_Cycla1] && lumpsfound[Check_Flmba1]) { if (!lumpsfound[Check_Mapinfo]) { // The original release won't work without its hacked custom EXE. //I_FatalError("Found an incompatible version of Chex Quest 3"); return NUM_IWAD_TYPES; // Can't use it. } return IWAD_ChexQuest3; } else { if (lumpsfound[Check_FreeDoom]) { if (!lumpsfound[Check_e2m1]) { return IWAD_FreeDoom1; } else { return IWAD_FreeDoomU; } } for (i = Check_e2m1; i < NUM_CHECKLUMPS; i++) { if (!lumpsfound[i]) { return IWAD_DoomShareware; } } if (i == NUM_CHECKLUMPS) { if (lumpsfound[Check_e4m1]) { if (lumpsfound[Check_W94_1] && lumpsfound[Check_POSSH0M0]) { return IWAD_ChexQuest; } else { return IWAD_UltimateDoom; } } else { return IWAD_DoomRegistered; } } } } return NUM_IWAD_TYPES; // Don't know }
void FWadCollection::AddFile (const char *filename, FileReader *wadinfo) { int startlump; bool isdir = false; if (wadinfo == NULL) { // Does this exist? If so, is it a directory? struct stat info; if (stat(filename, &info) != 0) { Printf(TEXTCOLOR_RED "Could not stat %s\n", filename); PrintLastError(); return; } isdir = (info.st_mode & S_IFDIR) != 0; if (!isdir) { try { wadinfo = new FileReader(filename); } catch (CRecoverableError &err) { // Didn't find file Printf (TEXTCOLOR_RED "%s\n", err.GetMessage()); PrintLastError (); return; } } } Printf (" adding %s", filename); startlump = NumLumps; FResourceFile *resfile; if (!isdir) resfile = FResourceFile::OpenResourceFile(filename, wadinfo); else resfile = FResourceFile::OpenDirectory(filename); if (resfile != NULL) { DWORD lumpstart = LumpInfo.Size(); resfile->SetFirstLump(lumpstart); for (DWORD i=0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); FWadCollection::LumpRecord *lump_p = &LumpInfo[LumpInfo.Reserve(1)]; lump_p->lump = lump; lump_p->wadnum = Files.Size(); } if (Files.Size() == IWAD_FILENUM && gameinfo.gametype == GAME_Strife && gameinfo.flags & GI_SHAREWARE) { resfile->FindStrifeTeaserVoices(); } Files.Push(resfile); for (DWORD i=0; i < resfile->LumpCount(); i++) { FResourceLump *lump = resfile->GetLump(i); if (lump->Flags & LUMPF_EMBEDDED) { char path[256]; mysnprintf(path, countof(path), "%s:", filename); char *wadstr = path + strlen(path); FileReader *embedded = lump->NewReader(); strcpy(wadstr, lump->FullName); AddFile(path, embedded); } } return; } }
unsigned FSavegameManager::ExtractSaveData(int index) { FResourceFile *resf; FSaveGameNode *node; if (index == -1) { if (SaveGames.Size() > 0 && SaveGames[0]->bNoDelete) { index = LastSaved + 1; } else { index = LastAccessed < 0? 0 : LastAccessed; } } UnloadSaveData(); if ((unsigned)index < SaveGames.Size() && (node = SaveGames[index]) && !node->Filename.IsEmpty() && !node->bOldVersion && (resf = FResourceFile::OpenResourceFile(node->Filename.GetChars(), true)) != nullptr) { FResourceLump *info = resf->FindLump("info.json"); if (info == nullptr) { // this should not happen because the file has already been verified. return index; } void *data = info->CacheLump(); FSerializer arc(nullptr); if (arc.OpenReader((const char *)data, info->LumpSize)) { FString comment; FString time = arc.GetString("Creation Time"); FString pcomment = arc.GetString("Comment"); comment = time; if (time.Len() > 0) comment += "\n"; comment += pcomment; SaveCommentString = comment; // Extract pic FResourceLump *pic = resf->FindLump("savepic.png"); if (pic != nullptr) { FileReader picreader; picreader.OpenMemoryArray([=](TArray<uint8_t> &array) { auto cache = pic->CacheLump(); array.Resize(pic->LumpSize); memcpy(&array[0], cache, pic->LumpSize); pic->ReleaseCache(); return true; }); PNGHandle *png = M_VerifyPNG(picreader); if (png != nullptr) { SavePic = PNGTexture_CreateFromFile(png, node->Filename); delete png; if (SavePic && SavePic->GetDisplayWidth() == 1 && SavePic->GetDisplayHeight() == 1) { delete SavePic; SavePic = nullptr; SavePicData.Clear(); } } } } delete resf; } return index; }
void FSavegameManager::ReadSaveStrings() { if (SaveGames.Size() == 0) { void *filefirst; findstate_t c_file; FString filter; LastSaved = LastAccessed = -1; quickSaveSlot = nullptr; filter = G_BuildSaveName("*." SAVEGAME_EXT, -1); filefirst = I_FindFirst(filter.GetChars(), &c_file); if (filefirst != ((void *)(-1))) { do { // I_FindName only returns the file's name and not its full path FString filepath = G_BuildSaveName(I_FindName(&c_file), -1); FResourceFile *savegame = FResourceFile::OpenResourceFile(filepath, true, true); if (savegame != nullptr) { bool oldVer = false; bool missing = false; FResourceLump *info = savegame->FindLump("info.json"); if (info == nullptr) { // savegame info not found. This is not a savegame so leave it alone. delete savegame; continue; } void *data = info->CacheLump(); FSerializer arc(nullptr); if (arc.OpenReader((const char *)data, info->LumpSize)) { int savever = 0; arc("Save Version", savever); FString engine = arc.GetString("Engine"); FString iwad = arc.GetString("Game WAD"); FString title = arc.GetString("Title"); if (engine.Compare(GAMESIG) != 0 || savever > SAVEVER) { // different engine or newer version: // not our business. Leave it alone. delete savegame; continue; } if (savever < MINSAVEVER) { // old, incompatible savegame. List as not usable. oldVer = true; } else if (iwad.CompareNoCase(Wads.GetWadName(Wads.GetIwadNum())) == 0) { missing = !G_CheckSaveGameWads(arc, false); } else { // different game. Skip this. delete savegame; continue; } FSaveGameNode *node = new FSaveGameNode; node->Filename = filepath; node->bOldVersion = oldVer; node->bMissingWads = missing; node->SaveTitle = title; InsertSaveNode(node); delete savegame; } } else // check for old formats. { FileReader file; if (file.OpenFile(filepath)) { PNGHandle *png; char sig[16]; char title[OLDSAVESTRINGSIZE + 1]; bool oldVer = true; bool addIt = false; bool missing = false; // ZDoom 1.23 betas 21-33 have the savesig first. // Earlier versions have the savesig second. // Later versions have the savegame encapsulated inside a PNG. // // Old savegame versions are always added to the menu so // the user can easily delete them if desired. title[OLDSAVESTRINGSIZE] = 0; if (nullptr != (png = M_VerifyPNG(file))) { char *ver = M_GetPNGText(png, "ZDoom Save Version"); if (ver != nullptr) { // An old version if (!M_GetPNGText(png, "Title", title, OLDSAVESTRINGSIZE)) { strncpy(title, I_FindName(&c_file), OLDSAVESTRINGSIZE); } addIt = true; delete[] ver; } delete png; } else { file.Seek(0, FileReader::SeekSet); if (file.Read(sig, 16) == 16) { if (strncmp(sig, "ZDOOMSAVE", 9) == 0) { if (file.Read(title, OLDSAVESTRINGSIZE) == OLDSAVESTRINGSIZE) { addIt = true; } } else { memcpy(title, sig, 16); if (file.Read(title + 16, OLDSAVESTRINGSIZE - 16) == OLDSAVESTRINGSIZE - 16 && file.Read(sig, 16) == 16 && strncmp(sig, "ZDOOMSAVE", 9) == 0) { addIt = true; } } } } if (addIt) { FSaveGameNode *node = new FSaveGameNode; node->Filename = filepath; node->bOldVersion = true; node->bMissingWads = false; node->SaveTitle = title; InsertSaveNode(node); } } } } while (I_FindNext(filefirst, &c_file) == 0); I_FindClose(filefirst); } } }