Fix the replay menu for people with non-latin characters in their username.

Use OsPath instead of CStr and CStrW where possible,
wstring_from_utf8(OsPath.string8()) to pass printable strings to the
JSAPI,
OsString when opening a filestream and
off_t instead of int for filesizes.

Fixes #4320
Differential Revision: https://code.wildfiregames.com/D518
Reviewed By: Imarok
Tested By: Imarok on Windows, wraitii on OSX
Special thanks to Philip for advice and the lib/path.h fix in
47cc447322.

This was SVN commit r19824.
This commit is contained in:
elexis 2017-06-25 14:54:00 +00:00
parent 47cc447322
commit e7ab22286e
11 changed files with 59 additions and 60 deletions

View File

@ -189,7 +189,7 @@ function reallyDeleteReplay(replayDirectory)
var selectedIndex = replaySelection.selected;
if (!Engine.DeleteReplay(replayDirectory))
error(sprintf("Could not delete replay '%(id)s'", { "id": replayDirectory }));
error("Could not delete replay!");
// Refresh replay list
init();

View File

@ -211,7 +211,7 @@ std::wstring GetCurrentReplayDirectory(ScriptInterface::CxPrivate* UNUSED(pCxPri
return std::wstring();
if (g_Game->IsVisualReplay())
return OsPath(g_Game->GetReplayPath()).Parent().Filename().string();
return g_Game->GetReplayPath().Parent().Filename().string();
return g_Game->GetReplayLogger().GetDirectory().Filename().string();
}

View File

@ -467,20 +467,20 @@ static void RunGameOrAtlas(int argc, const char* argv[])
const bool isNonVisualReplay = args.Has("replay");
const bool isNonVisual = args.Has("autostart-nonvisual");
const CStr replayFile =
const OsPath replayFile(
isVisualReplay ? args.Get("replay-visual") :
isNonVisualReplay ? args.Get("replay") : "";
isNonVisualReplay ? args.Get("replay") : "");
if (isVisualReplay || isNonVisualReplay)
{
if (!FileExists(OsPath(replayFile)))
if (!FileExists(replayFile))
{
debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str());
debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.string8().c_str());
return;
}
if (DirectoryExists(OsPath(replayFile)))
if (DirectoryExists(replayFile))
{
debug_printf("ERROR: The requested replay file '%s' is a directory!\n", replayFile.c_str());
debug_printf("ERROR: The requested replay file '%s' is a directory!\n", replayFile.string8().c_str());
return;
}
}

View File

@ -143,7 +143,7 @@ void CNetClientTurnManager::OnSyncError(u32 turn, const CStr& expectedHash, cons
scriptInterface.SetProperty(msg, "players", playerNamesStrings);
scriptInterface.SetProperty(msg, "expectedHash", expectedHashHex);
scriptInterface.SetProperty(msg, "hash", Hexify(hash));
scriptInterface.SetProperty(msg, "path_oos_dump", path.string8());
scriptInterface.SetProperty(msg, "path_replay", m_Replay.GetDirectory().string8());
scriptInterface.SetProperty(msg, "path_oos_dump", wstring_from_utf8(path.string8()));
scriptInterface.SetProperty(msg, "path_replay", wstring_from_utf8(m_Replay.GetDirectory().string8()));
m_NetClient.PushGuiMessage(msg);
}

View File

@ -168,9 +168,9 @@ int CGame::LoadVisualReplayData()
return 0;
}
bool CGame::StartVisualReplay(const std::string& replayPath)
bool CGame::StartVisualReplay(const OsPath& replayPath)
{
debug_printf("Starting to replay %s\n", replayPath.c_str());
debug_printf("Starting to replay %s\n", replayPath.string8().c_str());
m_IsVisualReplay = true;
ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
@ -178,7 +178,7 @@ bool CGame::StartVisualReplay(const std::string& replayPath)
SetTurnManager(new CReplayTurnManager(*m_Simulation2, GetReplayLogger()));
m_ReplayPath = replayPath;
m_ReplayStream = new std::ifstream(m_ReplayPath.c_str());
m_ReplayStream = new std::ifstream(OsString(replayPath).c_str());
std::string type;
ENSURE((*m_ReplayStream >> type).good() && type == "start");

View File

@ -18,9 +18,10 @@
#ifndef INCLUDED_GAME
#define INCLUDED_GAME
#include "ps/Errors.h"
#include <vector>
#include "ps/Errors.h"
#include "ps/Filesystem.h"
#include "scriptinterface/ScriptVal.h"
#include "simulation2/helpers/Player.h"
@ -91,7 +92,7 @@ public:
void StartGame(JS::MutableHandleValue attribs, const std::string& savedState);
PSRETURN ReallyStartGame();
bool StartVisualReplay(const std::string& replayPath);
bool StartVisualReplay(const OsPath& replayPath);
/**
* Periodic heartbeat that controls the process. performs all per-frame updates.
@ -196,7 +197,7 @@ public:
inline float GetSimRate() const
{ return m_SimRate; }
inline std::string GetReplayPath() const
inline OsPath GetReplayPath() const
{ return m_ReplayPath; }
/**
@ -222,7 +223,7 @@ private:
bool m_IsSavedGame; // true if loading a saved game; false for a new game
int LoadVisualReplayData();
std::string m_ReplayPath;
OsPath m_ReplayPath;
bool m_IsVisualReplay;
std::istream* m_ReplayStream;
u32 m_FinalReplayTurn;

View File

@ -106,11 +106,11 @@ CReplayPlayer::~CReplayPlayer()
delete m_Stream;
}
void CReplayPlayer::Load(const std::string& path)
void CReplayPlayer::Load(const OsPath& path)
{
ENSURE(!m_Stream);
m_Stream = new std::ifstream(path.c_str());
m_Stream = new std::ifstream(OsString(path).c_str());
ENSURE(m_Stream->good());
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016 Wildfire Games.
/* Copyright (C) 2017 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -96,7 +96,7 @@ public:
CReplayPlayer();
~CReplayPlayer();
void Load(const std::string& path);
void Load(const OsPath& path);
void Replay(bool serializationtest, int rejointestturn, bool ooslog);
private:

View File

@ -49,7 +49,7 @@ OsPath VisualReplay::GetDirectoryName()
return OsPath(paths.UserData() / "replays" / engine_version);
}
void VisualReplay::StartVisualReplay(const CStrW& directory)
void VisualReplay::StartVisualReplay(const OsPath& directory)
{
ENSURE(!g_NetServer);
ENSURE(!g_NetClient);
@ -61,7 +61,7 @@ void VisualReplay::StartVisualReplay(const CStrW& directory)
return;
g_Game = new CGame(false, false);
g_Game->StartVisualReplay(replayFile.string8());
g_Game->StartVisualReplay(replayFile);
}
bool VisualReplay::ReadCacheFile(ScriptInterface& scriptInterface, JS::MutableHandleObject cachedReplaysObject)
@ -72,7 +72,7 @@ bool VisualReplay::ReadCacheFile(ScriptInterface& scriptInterface, JS::MutableHa
if (!FileExists(cacheFileName))
return false;
std::ifstream cacheStream(cacheFileName.string8().c_str());
std::ifstream cacheStream(OsString(cacheFileName).c_str());
CStr cacheStr((std::istreambuf_iterator<char>(cacheStream)), std::istreambuf_iterator<char>());
cacheStream.close();
@ -95,7 +95,7 @@ void VisualReplay::StoreCacheFile(ScriptInterface& scriptInterface, JS::HandleOb
JSAutoRequest rq(cx);
JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays));
std::ofstream cacheStream(tempCacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc);
std::ofstream cacheStream(OsString(tempCacheFileName).c_str(), std::ofstream::out | std::ofstream::trunc);
cacheStream << scriptInterface.StringifyJSON(&replaysRooted);
cacheStream.close();
@ -111,7 +111,7 @@ JS::HandleObject VisualReplay::ReloadReplayCache(ScriptInterface& scriptInterfac
JSAutoRequest rq(cx);
// Maps the filename onto the index and size
typedef std::map<CStr, std::pair<u32, off_t>> replayCacheMap;
typedef std::map<OsPath, std::pair<u32, off_t>> replayCacheMap;
replayCacheMap fileList;
@ -127,7 +127,7 @@ JS::HandleObject VisualReplay::ReloadReplayCache(ScriptInterface& scriptInterfac
JS_GetElement(cx, cachedReplaysObject, j, &replay);
JS::RootedValue file(cx);
CStr fileName;
OsPath fileName;
double fileSize;
scriptInterface.GetProperty(replay, "directory", fileName);
scriptInterface.GetProperty(replay, "fileSize", fileSize);
@ -158,7 +158,7 @@ JS::HandleObject VisualReplay::ReloadReplayCache(ScriptInterface& scriptInterfac
continue;
bool isNew = true;
replayCacheMap::iterator it = fileList.find(directory.string8());
replayCacheMap::iterator it = fileList.find(directory);
if (it != fileList.end())
{
if (compareFiles)
@ -180,7 +180,7 @@ JS::HandleObject VisualReplay::ReloadReplayCache(ScriptInterface& scriptInterfac
CFileInfo fileInfo;
GetFileInfo(replayFile, &fileInfo);
scriptInterface.Eval("({})", &replayData);
scriptInterface.SetProperty(replayData, "directory", directory);
scriptInterface.SetProperty(replayData, "directory", directory.string());
scriptInterface.SetProperty(replayData, "fileSize", (double)fileInfo.Size());
}
JS_SetElement(cx, replays, i++, replayData);
@ -241,7 +241,7 @@ JS::Value VisualReplay::GetReplays(ScriptInterface& scriptInterface, bool compar
*
* @return The current cursor position or -1 on error.
*/
inline int goBackToLineBeginning(std::istream* replayStream, const CStr& fileName, const u64& fileSize)
inline off_t goBackToLineBeginning(std::istream* replayStream, const OsPath& fileName, off_t fileSize)
{
int currentPos;
char character;
@ -255,7 +255,7 @@ inline int goBackToLineBeginning(std::istream* replayStream, const CStr& fileNam
if (!replayStream->good())
{
LOGERROR("Unknown error when returning to the last line (%i of %lu) of %s", currentPos, fileSize, fileName.c_str());
LOGERROR("Unknown error when returning to the last line (%i of %lu) of %s", currentPos, fileSize, fileName.string8().c_str());
return -1;
}
@ -269,7 +269,7 @@ inline int goBackToLineBeginning(std::istream* replayStream, const CStr& fileNam
replayStream->seekg(-2, std::ios_base::cur);
}
LOGERROR("Infinite loop when going back to a line beginning in %s", fileName.c_str());
LOGERROR("Infinite loop when going back to a line beginning in %s", fileName.string8().c_str());
return -1;
}
@ -279,7 +279,7 @@ inline int goBackToLineBeginning(std::istream* replayStream, const CStr& fileNam
*
* @return seconds or -1 on error
*/
inline int getReplayDuration(std::istream* replayStream, const CStr& fileName, const u64& fileSize)
inline int getReplayDuration(std::istream* replayStream, const OsPath& fileName, off_t fileSize)
{
CStr type;
@ -290,7 +290,7 @@ inline int getReplayDuration(std::istream* replayStream, const CStr& fileName, c
// There should be about 5 lines to read until a turn is found.
for (int linesRead = 1; linesRead < 1000; ++linesRead)
{
int currentPosition = goBackToLineBeginning(replayStream, fileName, fileSize);
off_t currentPosition = goBackToLineBeginning(replayStream, fileName, fileSize);
// Read error or reached file beginning. No turns exist.
if (currentPosition < 1)
@ -298,12 +298,12 @@ inline int getReplayDuration(std::istream* replayStream, const CStr& fileName, c
if (!replayStream->good())
{
LOGERROR("Read error when determining replay duration at %i of %llu in %s", currentPosition - 2, fileSize, fileName.c_str());
LOGERROR("Read error when determining replay duration at %i of %llu in %s", currentPosition - 2, fileSize, fileName.string8().c_str());
return -1;
}
// Found last turn, compute duration.
if ((u64) currentPosition + 4 < fileSize && (*replayStream >> type).good() && type == "turn")
if (currentPosition + 4 < fileSize && (*replayStream >> type).good() && type == "turn")
{
u32 turn = 0, turnLength = 0;
*replayStream >> turn >> turnLength;
@ -314,7 +314,7 @@ inline int getReplayDuration(std::istream* replayStream, const CStr& fileName, c
replayStream->seekg(currentPosition - 2, std::ios_base::beg);
}
LOGERROR("Infinite loop when determining replay duration for %s", fileName.c_str());
LOGERROR("Infinite loop when determining replay duration for %s", fileName.string8().c_str());
return -1;
}
@ -329,26 +329,24 @@ JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const O
// Get file size and modification date
CFileInfo fileInfo;
GetFileInfo(replayFile, &fileInfo);
const u64 fileSize = (u64)fileInfo.Size();
const off_t fileSize = fileInfo.Size();
if (fileSize == 0)
return JSVAL_NULL;
// Open file
const CStr fileName = replayFile.string8();
std::ifstream* replayStream = new std::ifstream(fileName.c_str());
std::ifstream* replayStream = new std::ifstream(OsString(replayFile).c_str());
// File must begin with "start"
CStr type;
if (!(*replayStream >> type).good())
{
LOGERROR("Couldn't open %s. Non-latin characters are not supported yet.", fileName.c_str());
LOGERROR("Couldn't open %s.", replayFile.string8().c_str());
SAFE_DELETE(replayStream);
return JSVAL_NULL;
}
if (type != "start")
{
LOGWARNING("The replay %s is broken!", fileName.c_str());
LOGWARNING("The replay %s doesn't begin with 'start'!", replayFile.string8().c_str());
SAFE_DELETE(replayStream);
return JSVAL_NULL;
}
@ -361,7 +359,7 @@ JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const O
JS::RootedValue attribs(cx);
if (!scriptInterface.ParseJSON(header, &attribs))
{
LOGERROR("Couldn't parse replay header of %s", fileName.c_str());
LOGERROR("Couldn't parse replay header of %s", replayFile.string8().c_str());
SAFE_DELETE(replayStream);
return JSVAL_NULL;
}
@ -382,7 +380,7 @@ JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const O
return JSVAL_NULL;
}
int duration = getReplayDuration(replayStream, fileName, fileSize);
int duration = getReplayDuration(replayStream, replayFile, fileSize);
SAFE_DELETE(replayStream);
@ -393,14 +391,14 @@ JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const O
// Return the actual data
JS::RootedValue replayData(cx);
scriptInterface.Eval("({})", &replayData);
scriptInterface.SetProperty(replayData, "directory", directory);
scriptInterface.SetProperty(replayData, "directory", directory.string());
scriptInterface.SetProperty(replayData, "fileSize", (double)fileSize);
scriptInterface.SetProperty(replayData, "attribs", attribs);
scriptInterface.SetProperty(replayData, "duration", duration);
return replayData;
}
bool VisualReplay::DeleteReplay(const CStrW& replayDirectory)
bool VisualReplay::DeleteReplay(const OsPath& replayDirectory)
{
if (replayDirectory.empty())
return false;
@ -409,7 +407,7 @@ bool VisualReplay::DeleteReplay(const CStrW& replayDirectory)
return DirectoryExists(directory) && DeleteDirectory(directory) == INFO::OK;
}
JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName)
JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const OsPath& directoryName)
{
// Create empty JS object
JSContext* cx = pCxPrivate->pScriptInterface->GetContext();
@ -423,7 +421,7 @@ JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPriva
return attribs;
// Open file
std::istream* replayStream = new std::ifstream(replayFile.string8().c_str());
std::istream* replayStream = new std::ifstream(OsString(replayFile).c_str());
CStr type, line;
ENSURE((*replayStream >> type).good() && type == "start");
@ -473,13 +471,13 @@ void VisualReplay::SaveReplayMetadata(ScriptInterface* scriptInterface)
const OsPath fileName = g_Game->GetReplayLogger().GetDirectory() / L"metadata.json";
CreateDirectories(fileName.Parent(), 0700);
std::ofstream stream (fileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc);
std::ofstream stream (OsString(fileName).c_str(), std::ofstream::out | std::ofstream::trunc);
stream << scriptInterface->StringifyJSON(&metadata, false);
stream.close();
debug_printf("Saved replay metadata to %s\n", fileName.string8().c_str());
}
bool VisualReplay::HasReplayMetadata(const CStrW& directoryName)
bool VisualReplay::HasReplayMetadata(const OsPath& directoryName)
{
const OsPath filePath(GetDirectoryName() / directoryName / L"metadata.json");
@ -492,7 +490,7 @@ bool VisualReplay::HasReplayMetadata(const CStrW& directoryName)
return fileInfo.Size() > 0;
}
JS::Value VisualReplay::GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName)
JS::Value VisualReplay::GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const OsPath& directoryName)
{
if (!HasReplayMetadata(directoryName))
return JSVAL_NULL;
@ -501,7 +499,7 @@ JS::Value VisualReplay::GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate
JSAutoRequest rq(cx);
JS::RootedValue metadata(cx);
std::ifstream* stream = new std::ifstream(OsPath(GetDirectoryName() / directoryName / L"metadata.json").string8());
std::ifstream* stream = new std::ifstream(OsString(GetDirectoryName() / directoryName / L"metadata.json").c_str());
ENSURE(stream->good());
CStr line;
std::getline(*stream, line);

View File

@ -39,7 +39,7 @@ OsPath GetDirectoryName();
/**
* Replays the commands.txt file in the given subdirectory visually.
*/
void StartVisualReplay(const CStrW& directory);
void StartVisualReplay(const OsPath& directory);
/**
* Reads the replay Cache file and parses it into a jsObject
@ -89,22 +89,22 @@ JS::Value LoadReplayData(ScriptInterface& scriptInterface, const OsPath& directo
* @param replayFile - path to commands.txt, whose parent directory will be deleted.
* @return true if deletion was successful, false on error
*/
bool DeleteReplay(const CStrW& replayFile);
bool DeleteReplay(const OsPath& replayFile);
/**
* Returns the parsed header of the replay file (commands.txt).
*/
JS::Value GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
JS::Value GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const OsPath& directoryName);
/**
* Returns whether or not the metadata / summary screen data has been saved properly when the game ended.
*/
bool HasReplayMetadata(const CStrW& directoryName);
bool HasReplayMetadata(const OsPath& directoryName);
/**
* Returns the metadata of a replay.
*/
JS::Value GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
JS::Value GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const OsPath& directoryName);
/**
* Saves the metadata from the session to metadata.json.

View File

@ -60,7 +60,7 @@ void JSI_VisualReplay::AddReplayToCache(ScriptInterface::CxPrivate* pCxPrivate,
CStrW JSI_VisualReplay::GetReplayDirectoryName(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& directoryName)
{
return OsPath(VisualReplay::GetDirectoryName() / directoryName).string();
return wstring_from_utf8(OsPath(VisualReplay::GetDirectoryName() / directoryName).string8());
}
void JSI_VisualReplay::RegisterScriptFunctions(ScriptInterface& scriptInterface)