Tthis code simply logs the save time of a multiplayer game in the register database, this was likly done as part of an anti cheat scheme (the key was "Video Player"), but appears to have since been disabled. Probably to allow moving save games between PC's which was supported when better Windows 2000 support was added.
606 lines
14 KiB
C++
606 lines
14 KiB
C++
#include "diablo.h"
|
|
#include "../3rdParty/Storm/Source/storm.h"
|
|
#include "../DiabloUI/diabloui.h"
|
|
|
|
DEVILUTION_BEGIN_NAMESPACE
|
|
|
|
#ifdef SPAWN
|
|
#define PASSWORD_SINGLE "adslhfb1"
|
|
#define PASSWORD_MULTI "lshbkfg1"
|
|
#else
|
|
#define PASSWORD_SINGLE "xrgyrkj1"
|
|
#define PASSWORD_MULTI "szqnlsk1"
|
|
#endif
|
|
|
|
static char hero_names[MAX_CHARACTERS][PLR_NAME_LEN];
|
|
BOOL gbValidSaveFile;
|
|
|
|
void pfile_write_hero()
|
|
{
|
|
DWORD save_num;
|
|
PkPlayerStruct pkplr;
|
|
|
|
save_num = pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
if (pfile_open_archive(TRUE, save_num)) {
|
|
PackPlayer(&pkplr, myplr, gbMaxPlayers == 1);
|
|
pfile_encode_hero(&pkplr);
|
|
pfile_flush(gbMaxPlayers == 1, save_num);
|
|
}
|
|
}
|
|
|
|
DWORD pfile_get_save_num_from_name(const char *name)
|
|
{
|
|
DWORD i;
|
|
|
|
for (i = 0; i < MAX_CHARACTERS; i++) {
|
|
if (!_strcmpi(hero_names[i], name))
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void pfile_encode_hero(const PkPlayerStruct *pPack)
|
|
{
|
|
BYTE *packed;
|
|
DWORD packed_len;
|
|
char password[16] = PASSWORD_SINGLE;
|
|
|
|
if (gbMaxPlayers > 1)
|
|
strcpy(password, PASSWORD_MULTI);
|
|
|
|
packed_len = codec_get_encoded_len(sizeof(*pPack));
|
|
packed = (BYTE *)DiabloAllocPtr(packed_len);
|
|
memcpy(packed, pPack, sizeof(*pPack));
|
|
codec_encode(packed, sizeof(*pPack), packed_len, password);
|
|
mpqapi_write_file("hero", packed, packed_len);
|
|
mem_free_dbg(packed);
|
|
}
|
|
|
|
BOOL pfile_open_archive(BOOL update, DWORD save_num)
|
|
{
|
|
char FileName[MAX_PATH];
|
|
|
|
pfile_get_save_path(FileName, sizeof(FileName), save_num);
|
|
if (OpenMPQ(FileName, FALSE, save_num))
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void pfile_get_save_path(char *pszBuf, DWORD dwBufSize, DWORD save_num)
|
|
{
|
|
char path[MAX_PATH];
|
|
|
|
#ifdef SPAWN
|
|
const char *fmt = "%sshare_%d.sv";
|
|
|
|
if (gbMaxPlayers <= 1)
|
|
fmt = "%sspawn%d.sv";
|
|
#else
|
|
const char *fmt = "%smulti_%d.sv";
|
|
|
|
if (gbMaxPlayers <= 1)
|
|
fmt = "%ssingle_%d.sv";
|
|
#endif
|
|
|
|
GetPrefPath(path, MAX_PATH);
|
|
snprintf(pszBuf, MAX_PATH, fmt, path, save_num);
|
|
}
|
|
|
|
void pfile_flush(BOOL is_single_player, DWORD save_num)
|
|
{
|
|
char FileName[MAX_PATH];
|
|
|
|
pfile_get_save_path(FileName, sizeof(FileName), save_num);
|
|
mpqapi_flush_and_close(FileName, is_single_player, save_num);
|
|
}
|
|
|
|
BOOL pfile_create_player_description(char *dst, DWORD len)
|
|
{
|
|
char desc[128];
|
|
_uiheroinfo uihero;
|
|
|
|
myplr = 0;
|
|
pfile_read_player_from_save();
|
|
game_2_ui_player(plr, &uihero, gbValidSaveFile);
|
|
UiSetupPlayerInfo(gszHero, &uihero, GAME_ID);
|
|
|
|
if (dst != NULL && len) {
|
|
if (UiCreatePlayerDescription(&uihero, GAME_ID, desc) == 0)
|
|
return FALSE;
|
|
SStrCopy(dst, desc, len);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL pfile_rename_hero(const char *name_1, const char *name_2)
|
|
{
|
|
int i;
|
|
DWORD save_num;
|
|
_uiheroinfo uihero;
|
|
BOOL found = FALSE;
|
|
|
|
if (pfile_get_save_num_from_name(name_2) == MAX_CHARACTERS) {
|
|
for (i = 0; i != MAX_PLRS; i++) {
|
|
if (!_strcmpi(name_1, plr[i]._pName)) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return FALSE;
|
|
save_num = pfile_get_save_num_from_name(name_1);
|
|
if (save_num == MAX_CHARACTERS)
|
|
return FALSE;
|
|
|
|
SStrCopy(hero_names[save_num], name_2, PLR_NAME_LEN);
|
|
SStrCopy(plr[i]._pName, name_2, PLR_NAME_LEN);
|
|
if (!_strcmpi(gszHero, name_1))
|
|
SStrCopy(gszHero, name_2, sizeof(gszHero));
|
|
game_2_ui_player(plr, &uihero, gbValidSaveFile);
|
|
UiSetupPlayerInfo(gszHero, &uihero, GAME_ID);
|
|
pfile_write_hero();
|
|
return TRUE;
|
|
}
|
|
|
|
void pfile_flush_W()
|
|
{
|
|
pfile_flush(TRUE, pfile_get_save_num_from_name(plr[myplr]._pName));
|
|
}
|
|
|
|
void game_2_ui_player(const PlayerStruct *p, _uiheroinfo *heroinfo, BOOL bHasSaveFile)
|
|
{
|
|
memset(heroinfo, 0, sizeof(*heroinfo));
|
|
strncpy(heroinfo->name, p->_pName, sizeof(heroinfo->name) - 1);
|
|
heroinfo->name[sizeof(heroinfo->name) - 1] = '\0';
|
|
heroinfo->level = p->_pLevel;
|
|
heroinfo->heroclass = game_2_ui_class(p);
|
|
heroinfo->strength = p->_pStrength;
|
|
heroinfo->magic = p->_pMagic;
|
|
heroinfo->dexterity = p->_pDexterity;
|
|
heroinfo->vitality = p->_pVitality;
|
|
heroinfo->gold = p->_pGold;
|
|
heroinfo->hassaved = bHasSaveFile;
|
|
heroinfo->herorank = p->pDiabloKillLevel;
|
|
#ifdef SPAWN
|
|
heroinfo->spawned = TRUE;
|
|
#else
|
|
heroinfo->spawned = FALSE;
|
|
#endif
|
|
}
|
|
|
|
BYTE game_2_ui_class(const PlayerStruct *p)
|
|
{
|
|
BYTE uiclass;
|
|
if (p->_pClass == PC_WARRIOR)
|
|
uiclass = UI_WARRIOR;
|
|
else if (p->_pClass == PC_ROGUE)
|
|
uiclass = UI_ROGUE;
|
|
else
|
|
uiclass = UI_SORCERER;
|
|
|
|
return uiclass;
|
|
}
|
|
|
|
BOOL __stdcall pfile_ui_set_hero_infos(BOOL(__stdcall *ui_add_hero_info)(_uiheroinfo *))
|
|
{
|
|
DWORD i, save_num;
|
|
char FileName[MAX_PATH];
|
|
char NewFileName[MAX_PATH];
|
|
BOOL showFixedMsg;
|
|
|
|
memset(hero_names, 0, sizeof(hero_names));
|
|
|
|
showFixedMsg = TRUE;
|
|
for (i = 0; i < MAX_CHARACTERS; i++) {
|
|
PkPlayerStruct pkplr;
|
|
HANDLE archive = pfile_open_save_archive(&showFixedMsg, i);
|
|
if (archive) {
|
|
if (pfile_read_hero(archive, &pkplr)) {
|
|
_uiheroinfo uihero;
|
|
strcpy(hero_names[i], pkplr.pName);
|
|
UnPackPlayer(&pkplr, 0, FALSE);
|
|
game_2_ui_player(plr, &uihero, pfile_archive_contains_game(archive, i));
|
|
ui_add_hero_info(&uihero);
|
|
}
|
|
pfile_SFileCloseArchive(archive);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL pfile_read_hero(HANDLE archive, PkPlayerStruct *pPack)
|
|
{
|
|
HANDLE file;
|
|
BOOL decoded;
|
|
DWORD dwlen, nSize;
|
|
BYTE *buf;
|
|
|
|
if (!SFileOpenFileEx(archive, "hero", 0, &file)) {
|
|
return FALSE;
|
|
} else {
|
|
BOOL ret = FALSE;
|
|
char password[16] = PASSWORD_SINGLE;
|
|
nSize = 16;
|
|
|
|
if (gbMaxPlayers > 1)
|
|
strcpy(password, PASSWORD_MULTI);
|
|
|
|
dwlen = SFileGetFileSize(file, NULL);
|
|
if (dwlen) {
|
|
DWORD read;
|
|
buf = DiabloAllocPtr(dwlen);
|
|
if (SFileReadFile(file, buf, dwlen, &read, NULL)) {
|
|
decoded = TRUE;
|
|
read = codec_decode(buf, dwlen, password);
|
|
if (!read && gbMaxPlayers > 1) {
|
|
GetComputerName(password, &nSize);
|
|
if (SFileSetFilePointer(file, 0, NULL, FILE_BEGIN) || !SFileReadFile(file, buf, dwlen, &read, NULL))
|
|
decoded = FALSE;
|
|
else
|
|
read = codec_decode(buf, dwlen, password);
|
|
}
|
|
if (decoded && read == sizeof(*pPack)) {
|
|
memcpy(pPack, buf, sizeof(*pPack));
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
if (buf)
|
|
mem_free_dbg(buf);
|
|
}
|
|
SFileCloseFile(file);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param showFixedMsg Display a dialog if a save file was corrected (deprecated)
|
|
*/
|
|
HANDLE pfile_open_save_archive(BOOL *showFixedMsg, DWORD save_num)
|
|
{
|
|
char SrcStr[MAX_PATH];
|
|
HANDLE archive;
|
|
|
|
pfile_get_save_path(SrcStr, sizeof(SrcStr), save_num);
|
|
if (SFileOpenArchive(SrcStr, 0x7000, FS_PC, &archive))
|
|
return archive;
|
|
return NULL;
|
|
}
|
|
|
|
void pfile_SFileCloseArchive(HANDLE hsArchive)
|
|
{
|
|
SFileCloseArchive(hsArchive);
|
|
}
|
|
|
|
BOOL pfile_archive_contains_game(HANDLE hsArchive, DWORD save_num)
|
|
{
|
|
HANDLE file;
|
|
|
|
if (gbMaxPlayers != 1)
|
|
return FALSE;
|
|
|
|
if (!SFileOpenFileEx(hsArchive, "game", 0, &file))
|
|
return FALSE;
|
|
|
|
SFileCloseFile(file);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL __stdcall pfile_ui_set_class_stats(unsigned int player_class_nr, _uidefaultstats *class_stats)
|
|
{
|
|
int c;
|
|
|
|
c = pfile_get_player_class(player_class_nr);
|
|
class_stats->strength = StrengthTbl[c];
|
|
class_stats->magic = MagicTbl[c];
|
|
class_stats->dexterity = DexterityTbl[c];
|
|
class_stats->vitality = VitalityTbl[c];
|
|
return TRUE;
|
|
}
|
|
|
|
char pfile_get_player_class(unsigned int player_class_nr)
|
|
{
|
|
char pc_class;
|
|
|
|
if (player_class_nr == UI_WARRIOR)
|
|
pc_class = PC_WARRIOR;
|
|
else if (player_class_nr == UI_ROGUE)
|
|
pc_class = PC_ROGUE;
|
|
else
|
|
pc_class = PC_SORCERER;
|
|
return pc_class;
|
|
}
|
|
|
|
BOOL __stdcall pfile_ui_save_create(_uiheroinfo *heroinfo)
|
|
{
|
|
DWORD save_num;
|
|
char cl;
|
|
PkPlayerStruct pkplr;
|
|
|
|
save_num = pfile_get_save_num_from_name(heroinfo->name);
|
|
if (save_num == MAX_CHARACTERS) {
|
|
for (save_num = 0; save_num < MAX_CHARACTERS; save_num++) {
|
|
if (!hero_names[save_num][0])
|
|
break;
|
|
}
|
|
if (save_num == MAX_CHARACTERS)
|
|
return FALSE;
|
|
}
|
|
if (!pfile_open_archive(FALSE, save_num))
|
|
return FALSE;
|
|
mpqapi_remove_hash_entries(pfile_get_file_name);
|
|
strncpy(hero_names[save_num], heroinfo->name, PLR_NAME_LEN);
|
|
hero_names[save_num][PLR_NAME_LEN - 1] = '\0';
|
|
cl = pfile_get_player_class(heroinfo->heroclass);
|
|
CreatePlayer(0, cl);
|
|
strncpy(plr[0]._pName, heroinfo->name, PLR_NAME_LEN);
|
|
plr[0]._pName[PLR_NAME_LEN - 1] = '\0';
|
|
PackPlayer(&pkplr, 0, TRUE);
|
|
pfile_encode_hero(&pkplr);
|
|
game_2_ui_player(&plr[0], heroinfo, FALSE);
|
|
pfile_flush(TRUE, save_num);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL __stdcall pfile_get_file_name(DWORD lvl, char *dst)
|
|
{
|
|
const char *fmt;
|
|
|
|
if (gbMaxPlayers > 1) {
|
|
if (lvl)
|
|
return FALSE;
|
|
fmt = "hero";
|
|
} else {
|
|
if (lvl < 17)
|
|
fmt = "perml%02d";
|
|
else if (lvl < 34) {
|
|
lvl -= 17;
|
|
fmt = "perms%02d";
|
|
} else if (lvl == 34)
|
|
fmt = "game";
|
|
else if (lvl == 35)
|
|
fmt = "hero";
|
|
else
|
|
return FALSE;
|
|
}
|
|
sprintf(dst, fmt, lvl);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL __stdcall pfile_delete_save(_uiheroinfo *hero_info)
|
|
{
|
|
DWORD save_num;
|
|
char FileName[MAX_PATH];
|
|
|
|
save_num = pfile_get_save_num_from_name(hero_info->name);
|
|
if (save_num < MAX_CHARACTERS) {
|
|
hero_names[save_num][0] = '\0';
|
|
pfile_get_save_path(FileName, sizeof(FileName), save_num);
|
|
DeleteFile(FileName);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void pfile_read_player_from_save()
|
|
{
|
|
HANDLE archive;
|
|
DWORD save_num;
|
|
PkPlayerStruct pkplr;
|
|
|
|
save_num = pfile_get_save_num_from_name(gszHero);
|
|
archive = pfile_open_save_archive(NULL, save_num);
|
|
if (archive == NULL)
|
|
app_fatal("Unable to open archive");
|
|
if (!pfile_read_hero(archive, &pkplr))
|
|
app_fatal("Unable to load character");
|
|
|
|
UnPackPlayer(&pkplr, myplr, FALSE);
|
|
gbValidSaveFile = pfile_archive_contains_game(archive, save_num);
|
|
pfile_SFileCloseArchive(archive);
|
|
}
|
|
|
|
void GetTempLevelNames(char *szTemp)
|
|
{
|
|
// BUGFIX: function call has no purpose
|
|
pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
if (setlevel)
|
|
sprintf(szTemp, "temps%02d", setlvlnum);
|
|
else
|
|
sprintf(szTemp, "templ%02d", currlevel);
|
|
}
|
|
|
|
void GetPermLevelNames(char *szPerm)
|
|
{
|
|
DWORD save_num;
|
|
BOOL has_file;
|
|
|
|
save_num = pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
GetTempLevelNames(szPerm);
|
|
if (!pfile_open_archive(FALSE, save_num))
|
|
app_fatal("Unable to read to save file archive");
|
|
|
|
has_file = mpqapi_has_file(szPerm);
|
|
pfile_flush(TRUE, save_num);
|
|
if (!has_file) {
|
|
if (setlevel)
|
|
sprintf(szPerm, "perms%02d", setlvlnum);
|
|
else
|
|
sprintf(szPerm, "perml%02d", currlevel);
|
|
}
|
|
}
|
|
|
|
void pfile_get_game_name(char *dst)
|
|
{
|
|
// BUGFIX: function call with no purpose
|
|
pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
strcpy(dst, "game");
|
|
}
|
|
|
|
void pfile_remove_temp_files()
|
|
{
|
|
if (gbMaxPlayers <= 1) {
|
|
DWORD save_num = pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
if (!pfile_open_archive(FALSE, save_num))
|
|
app_fatal("Unable to write to save file archive");
|
|
mpqapi_remove_hash_entries(GetTempSaveNames);
|
|
pfile_flush(TRUE, save_num);
|
|
}
|
|
}
|
|
|
|
BOOL __stdcall GetTempSaveNames(DWORD dwIndex, char *szTemp)
|
|
{
|
|
const char *fmt;
|
|
|
|
if (dwIndex < 17)
|
|
fmt = "templ%02d";
|
|
else if (dwIndex < 34) {
|
|
dwIndex -= 17;
|
|
fmt = "temps%02d";
|
|
} else
|
|
return FALSE;
|
|
|
|
sprintf(szTemp, fmt, dwIndex);
|
|
return TRUE;
|
|
}
|
|
|
|
void pfile_rename_temp_to_perm()
|
|
{
|
|
DWORD dwChar, dwIndex;
|
|
BOOL bResult;
|
|
char szTemp[MAX_PATH];
|
|
char szPerm[MAX_PATH];
|
|
|
|
dwChar = pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
assert(dwChar < MAX_CHARACTERS);
|
|
assert(gbMaxPlayers == 1);
|
|
if (!pfile_open_archive(FALSE, dwChar))
|
|
app_fatal("Unable to write to save file archive");
|
|
|
|
dwIndex = 0;
|
|
while (GetTempSaveNames(dwIndex, szTemp)) {
|
|
bResult = GetPermSaveNames(dwIndex, szPerm);
|
|
assert(bResult);
|
|
dwIndex++;
|
|
if (mpqapi_has_file(szTemp)) {
|
|
if (mpqapi_has_file(szPerm))
|
|
mpqapi_remove_hash_entry(szPerm);
|
|
mpqapi_rename(szTemp, szPerm);
|
|
}
|
|
}
|
|
assert(! GetPermSaveNames(dwIndex,szPerm));
|
|
pfile_flush(TRUE, dwChar);
|
|
}
|
|
|
|
BOOL __stdcall GetPermSaveNames(DWORD dwIndex, char *szPerm)
|
|
{
|
|
const char *fmt;
|
|
|
|
if (dwIndex < 17)
|
|
fmt = "perml%02d";
|
|
else if (dwIndex < 34) {
|
|
dwIndex -= 17;
|
|
fmt = "perms%02d";
|
|
} else
|
|
return FALSE;
|
|
|
|
sprintf(szPerm, fmt, dwIndex);
|
|
return TRUE;
|
|
}
|
|
|
|
void pfile_write_save_file(const char *pszName, BYTE *pbData, DWORD dwLen, DWORD qwLen)
|
|
{
|
|
DWORD save_num;
|
|
char FileName[MAX_PATH];
|
|
|
|
pfile_strcpy(FileName, pszName);
|
|
save_num = pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
{
|
|
char password[16] = PASSWORD_SINGLE;
|
|
if (gbMaxPlayers > 1)
|
|
strcpy(password, PASSWORD_MULTI);
|
|
|
|
codec_encode(pbData, dwLen, qwLen, password);
|
|
}
|
|
if (!pfile_open_archive(FALSE, save_num))
|
|
app_fatal("Unable to write so save file archive");
|
|
mpqapi_write_file(FileName, pbData, qwLen);
|
|
pfile_flush(TRUE, save_num);
|
|
}
|
|
|
|
void pfile_strcpy(char *dst, const char *src)
|
|
{
|
|
strcpy(dst, src);
|
|
}
|
|
|
|
BYTE *pfile_read(const char *pszName, DWORD *pdwLen)
|
|
{
|
|
DWORD save_num, nread;
|
|
char FileName[MAX_PATH];
|
|
HANDLE archive, save;
|
|
BYTE *buf;
|
|
|
|
pfile_strcpy(FileName, pszName);
|
|
save_num = pfile_get_save_num_from_name(plr[myplr]._pName);
|
|
archive = pfile_open_save_archive(NULL, save_num);
|
|
if (archive == NULL)
|
|
app_fatal("Unable to open save file archive");
|
|
|
|
if (!SFileOpenFileEx(archive, FileName, 0, &save))
|
|
app_fatal("Unable to open save file");
|
|
|
|
*pdwLen = SFileGetFileSize(save, NULL);
|
|
if (*pdwLen == 0)
|
|
app_fatal("Invalid save file");
|
|
|
|
buf = (BYTE *)DiabloAllocPtr(*pdwLen);
|
|
if (!SFileReadFile(save, buf, *pdwLen, &nread, NULL))
|
|
app_fatal("Unable to read save file");
|
|
SFileCloseFile(save);
|
|
pfile_SFileCloseArchive(archive);
|
|
|
|
{
|
|
char password[16] = PASSWORD_SINGLE;
|
|
DWORD nSize = 16;
|
|
|
|
if (gbMaxPlayers > 1)
|
|
strcpy(password, PASSWORD_MULTI);
|
|
|
|
*pdwLen = codec_decode(buf, *pdwLen, password);
|
|
if (*pdwLen == 0) {
|
|
// BUGFIFX: *pdwLen has already been overwritten with zero and the savefile has been closed
|
|
// there is no way this can work correctly
|
|
if (gbMaxPlayers > 1) {
|
|
GetComputerName(password, &nSize);
|
|
if (SFileSetFilePointer(save, 0, NULL, FILE_BEGIN))
|
|
app_fatal("Unable to read save file");
|
|
|
|
if (!SFileReadFile(save, buf, *pdwLen, &nread, NULL))
|
|
app_fatal("Unable to read save file");
|
|
*pdwLen = codec_decode(buf, *pdwLen, password);
|
|
}
|
|
if (*pdwLen == 0)
|
|
app_fatal("Invalid save file");
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
void pfile_update(BOOL force_save)
|
|
{
|
|
// BUGFIX: these tick values should be treated as unsigned to handle overflows correctly
|
|
static int save_prev_tc;
|
|
|
|
if (gbMaxPlayers != 1) {
|
|
int tick = GetTickCount();
|
|
if (force_save || tick - save_prev_tc > 60000) {
|
|
save_prev_tc = tick;
|
|
pfile_write_hero();
|
|
}
|
|
}
|
|
}
|
|
|
|
DEVILUTION_END_NAMESPACE
|