You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

696 lines
17 KiB

//HEADER_GOES_HERE
#include "../types.h"
#define PASSWORD_SINGLE "xrgyrkj1"
#define PASSWORD_MULTI "szqnlsk1"
static char hero_names[MAX_CHARACTERS][PLR_NAME_LEN];
BOOL gbValidSaveFile;
void __cdecl pfile_init_save_directory()
{
DWORD len;
char Buffer[MAX_PATH];
len = GetWindowsDirectory(Buffer, sizeof(Buffer));
if (len) {
pfile_check_available_space(Buffer);
len = GetModuleFileName(ghInst, Buffer, sizeof(Buffer));
}
if (!len)
TermMsg("Unable to initialize save directory");
else
pfile_check_available_space(Buffer);
}
void __fastcall pfile_check_available_space(char *pszDir)
{
char *s;
BOOL hasSpace;
DWORD TotalNumberOfClusters;
DWORD NumberOfFreeClusters;
DWORD BytesPerSector;
DWORD SectorsPerCluster;
s = pszDir;
while (*s) {
if (*s++ != '\\')
continue;
*s = '\0';
break;
}
hasSpace = GetDiskFreeSpace(pszDir, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters);
if (hasSpace) {
// 10MB is the amount hardcoded in the error dialog
if ((__int64)SectorsPerCluster * BytesPerSector * NumberOfFreeClusters < (__int64)(10 << 20))
hasSpace = FALSE;
}
if (!hasSpace)
DiskFreeDlg(pszDir);
}
void __cdecl pfile_write_hero()
{
unsigned int 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);
}
}
unsigned int __fastcall pfile_get_save_num_from_name(const char *name)
{
unsigned int i;
for (i = 0; i < MAX_CHARACTERS; i++) {
if (!_strcmpi(hero_names[i], name))
break;
}
return i;
}
void __fastcall 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 __fastcall pfile_open_archive(BOOL a1, unsigned int save_num)
{
char FileName[MAX_PATH];
pfile_get_save_path(FileName, sizeof(FileName), save_num);
if (mpqapi_open_archive(FileName, FALSE, save_num))
return TRUE;
if (a1 && gbMaxPlayers > 1)
mpqapi_update_multi_creation_time(save_num);
return FALSE;
}
void __fastcall pfile_get_save_path(char *pszBuf, DWORD dwBufSize, unsigned int save_num)
{
DWORD plen;
char *s;
char path[MAX_PATH];
const char *fmt = "\\multi_%d.sv";
if (gbMaxPlayers <= 1)
fmt = "\\single_%d.sv";
// BUGFIX: ignores dwBufSize and uses MAX_PATH instead
plen = GetModuleFileName(ghInst, pszBuf, MAX_PATH);
s = strrchr(pszBuf, '\\');
if (s)
*s = '\0';
if (!plen)
TermMsg("Unable to get save directory");
sprintf(path, fmt, save_num);
strcat(pszBuf, path);
_strlwr(pszBuf);
}
void __fastcall pfile_flush(BOOL is_single_player, unsigned int 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 __fastcall pfile_create_player_description(char *dst, unsigned int 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 __fastcall pfile_create_save_file(const char *name_1, const char *name_2)
{
int i;
unsigned int 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 __cdecl pfile_flush_W()
{
pfile_flush(TRUE, pfile_get_save_num_from_name(plr[myplr]._pName));
}
void __fastcall 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 = (unsigned char)p->pDiabloKillLevel;
heroinfo->spawned = 0;
}
unsigned char __fastcall game_2_ui_class(const PlayerStruct *p)
{
unsigned char 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 *))
{
unsigned int i, save_num;
char FileName[MAX_PATH];
char NewFileName[MAX_PATH];
int a1;
memset(hero_names, 0, sizeof(hero_names));
if (gbMaxPlayers > 1) {
for (i = 0, save_num = 0; i < MAX_CHARACTERS && save_num < MAX_CHARACTERS; i++) {
struct _OFSTRUCT ReOpenBuff;
const char *s;
GetSaveDirectory(FileName, sizeof(FileName), i);
s = strrchr(FileName, '\\') + 1;
if (s == (const char *)1)
continue;
if (OpenFile(FileName, &ReOpenBuff, OF_EXIST) == HFILE_ERROR)
continue;
if (!SRegLoadString("Diablo\\Converted", s, 0, NewFileName, sizeof(NewFileName))) {
while (save_num < MAX_CHARACTERS) {
pfile_get_save_path(NewFileName, sizeof(NewFileName), save_num++);
if (OpenFile(NewFileName, &ReOpenBuff, OF_EXIST) == HFILE_ERROR) {
if (CopyFile(FileName, NewFileName, TRUE)) {
DWORD attrib;
SRegSaveString("Diablo\\Converted", s, 0, NewFileName);
attrib = GetFileAttributes(NewFileName);
if (attrib != INVALID_FILE_ATTRIBUTES) {
attrib &= ~(FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM);
SetFileAttributes(NewFileName, attrib);
}
}
break;
}
}
}
}
}
a1 = 1;
for (i = 0; i < MAX_CHARACTERS; i++) {
PkPlayerStruct pkplr;
HANDLE archive = pfile_open_save_archive(&a1, 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;
}
char *__fastcall GetSaveDirectory(char *dst, int dst_size, unsigned int save_num)
{
DWORD dirLen;
char FileName[MAX_PATH];
const char *savename;
// BUGFIX: ignores dst_size and uses MAX_PATH instead
if (gbMaxPlayers > 1) {
savename = "\\dlinfo_%d.drv";
dirLen = GetWindowsDirectory(dst, MAX_PATH);
} else {
char *s;
savename = "\\single_%d.sv";
dirLen = GetModuleFileName(ghInst, dst, MAX_PATH);
s = strrchr(dst, '\\');
if (s)
*s = '\0';
}
if (!dirLen)
TermMsg("Unable to get save directory");
sprintf(FileName, savename, save_num);
strcat(dst, FileName);
return _strlwr(dst);
}
BOOL __fastcall pfile_read_hero(HANDLE archive, PkPlayerStruct *pPack)
{
HANDLE file;
BOOL decoded;
DWORD dwlen, nSize;
unsigned char *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, 0) || !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;
}
}
HANDLE __fastcall pfile_open_save_archive(int *unused, unsigned int save_num)
{
char SrcStr[MAX_PATH];
HANDLE archive;
pfile_get_save_path(SrcStr, sizeof(SrcStr), save_num);
if (SFileOpenArchive(SrcStr, 0x7000, 0, &archive))
return archive;
return NULL;
}
void __fastcall pfile_SFileCloseArchive(HANDLE hsArchive)
{
SFileCloseArchive(hsArchive);
}
BOOL __fastcall pfile_archive_contains_game(HANDLE hsArchive, unsigned int 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 __fastcall 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)
{
unsigned int 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)
{
unsigned int 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 __cdecl pfile_read_player_from_save()
{
HANDLE archive;
unsigned int save_num;
PkPlayerStruct pkplr;
save_num = pfile_get_save_num_from_name(gszHero);
archive = pfile_open_save_archive(NULL, save_num);
if (archive == NULL)
TermMsg("Unable to open archive");
if (!pfile_read_hero(archive, &pkplr))
TermMsg("Unable to load character");
UnPackPlayer(&pkplr, myplr, FALSE);
gbValidSaveFile = pfile_archive_contains_game(archive, save_num);
pfile_SFileCloseArchive(archive);
}
void __fastcall 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 __fastcall GetPermLevelNames(char *szPerm)
{
unsigned int 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))
TermMsg("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 __fastcall 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 __cdecl pfile_remove_temp_files()
{
if (gbMaxPlayers <= 1) {
unsigned int save_num = pfile_get_save_num_from_name(plr[myplr]._pName);
if (!pfile_open_archive(FALSE, save_num))
TermMsg("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 __cdecl pfile_rename_temp_to_perm()
{
unsigned int save_num;
unsigned int i;
char TempName[MAX_PATH];
char PermName[MAX_PATH];
save_num = pfile_get_save_num_from_name(plr[myplr]._pName);
if (!pfile_open_archive(FALSE, save_num))
TermMsg("Unable to write to save file archive");
i = 0;
while (GetTempSaveNames(i, TempName)) {
GetPermSaveNames(i, PermName);
i++;
if (mpqapi_has_file(TempName)) {
if (mpqapi_has_file(PermName))
mpqapi_remove_hash_entry(PermName);
mpqapi_rename(TempName, PermName);
}
}
GetPermSaveNames(i, PermName);
pfile_flush(TRUE, save_num);
}
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 __fastcall pfile_write_save_file(const char *pszName, BYTE *pbData, DWORD dwLen, DWORD qwLen)
{
unsigned int 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))
TermMsg("Unable to write so save file archive");
mpqapi_write_file(FileName, pbData, qwLen);
pfile_flush(TRUE, save_num);
}
void __fastcall pfile_strcpy(char *dst, const char *src)
{
strcpy(dst, src);
}
BYTE *__fastcall pfile_read(const char *pszName, DWORD *pdwLen)
{
unsigned int save_num;
char FileName[MAX_PATH];
HANDLE archive, save;
BYTE *buf;
DWORD nread;
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)
TermMsg("Unable to open save file archive");
if (!SFileOpenFileEx(archive, FileName, 0, &save))
TermMsg("Unable to open save file");
*pdwLen = SFileGetFileSize(save, NULL);
if (*pdwLen == 0)
TermMsg("Invalid save file");
buf = (BYTE *)DiabloAllocPtr(*pdwLen);
if (!SFileReadFile(save, buf, *pdwLen, &nread, NULL))
TermMsg("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, 0))
TermMsg("Unable to read save file");
if (!SFileReadFile(save, buf, *pdwLen, &nread, NULL))
TermMsg("Unable to read save file");
*pdwLen = codec_decode(buf, *pdwLen, password);
}
if (*pdwLen == 0)
TermMsg("Invalid save file");
}
}
return buf;
}
void __fastcall 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();
}
}
}