Add button in options menu to migrate to shared data directory a18855301306
Summary of migrated files will be shown to user in a popup, and a log file with every moved file will be left in the original directory stamps, saves, scripts, screenshots, and powder.pref will be migrated. Recordings are not.
This commit is contained in:
parent
0292344328
commit
b3aa6252ce
@ -738,32 +738,34 @@ int main(int argc, char * argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Platform::originalCwd = Platform::GetCwd();
|
||||||
|
|
||||||
std::map<ByteString, ByteString> arguments = readArguments(argc, argv);
|
std::map<ByteString, ByteString> arguments = readArguments(argc, argv);
|
||||||
|
|
||||||
if(arguments["ddir"].length())
|
if (arguments["ddir"].length())
|
||||||
{
|
{
|
||||||
#ifdef WIN
|
#ifdef WIN
|
||||||
int failure = _chdir(arguments["ddir"].c_str());
|
int failure = _chdir(arguments["ddir"].c_str());
|
||||||
#else
|
#else
|
||||||
int failure = chdir(arguments["ddir"].c_str());
|
int failure = chdir(arguments["ddir"].c_str());
|
||||||
#endif
|
#endif
|
||||||
if (failure)
|
if (!failure)
|
||||||
{
|
Platform::sharedCwd = Platform::GetCwd();
|
||||||
|
else
|
||||||
perror("failed to chdir to requested ddir");
|
perror("failed to chdir to requested ddir");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
char *ddir = SDL_GetPrefPath(NULL, "The Powder Toy");
|
||||||
#ifdef WIN
|
#ifdef WIN
|
||||||
struct _stat s;
|
struct _stat s;
|
||||||
if(_stat("powder.pref", &s) != 0)
|
if (_stat("powder.pref", &s) != 0)
|
||||||
#else
|
#else
|
||||||
struct stat s;
|
struct stat s;
|
||||||
if(stat("powder.pref", &s) != 0)
|
if (stat("powder.pref", &s) != 0)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
char *ddir = SDL_GetPrefPath(NULL, "The Powder Toy");
|
if (ddir)
|
||||||
if(ddir)
|
|
||||||
{
|
{
|
||||||
#ifdef WIN
|
#ifdef WIN
|
||||||
int failure = _chdir(ddir);
|
int failure = _chdir(ddir);
|
||||||
@ -773,10 +775,17 @@ int main(int argc, char * argv[])
|
|||||||
if (failure)
|
if (failure)
|
||||||
{
|
{
|
||||||
perror("failed to chdir to default ddir");
|
perror("failed to chdir to default ddir");
|
||||||
|
SDL_free(ddir);
|
||||||
|
ddir = nullptr;
|
||||||
}
|
}
|
||||||
SDL_free(ddir);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ddir)
|
||||||
|
{
|
||||||
|
Platform::sharedCwd = ddir;
|
||||||
|
SDL_free(ddir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scale = Client::Ref().GetPrefInteger("Scale", 1);
|
scale = Client::Ref().GetPrefInteger("Scale", 1);
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <fstream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#ifdef WIN
|
#ifdef WIN
|
||||||
@ -29,11 +30,13 @@
|
|||||||
namespace Platform
|
namespace Platform
|
||||||
{
|
{
|
||||||
|
|
||||||
|
std::string originalCwd;
|
||||||
|
std::string sharedCwd;
|
||||||
|
|
||||||
ByteString GetCwd()
|
ByteString GetCwd()
|
||||||
{
|
{
|
||||||
char cwdTemp[PATH_MAX];
|
char *cwd = getcwd(NULL, 0);
|
||||||
getcwd(cwdTemp, PATH_MAX);
|
return cwd == nullptr ? "" : cwd;
|
||||||
return cwdTemp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteString ExecutableName()
|
ByteString ExecutableName()
|
||||||
@ -324,11 +327,13 @@ std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search,
|
|||||||
{
|
{
|
||||||
ByteString currentFileName = ByteString(directoryEntry->d_name);
|
ByteString currentFileName = ByteString(directoryEntry->d_name);
|
||||||
if (currentFileName.length()>4)
|
if (currentFileName.length()>4)
|
||||||
directoryList.push_back(directory+currentFileName);
|
directoryList.push_back(currentFileName);
|
||||||
}
|
}
|
||||||
closedir(directoryHandle);
|
closedir(directoryHandle);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
search = search.ToLower();
|
||||||
|
|
||||||
std::vector<ByteString> searchResults;
|
std::vector<ByteString> searchResults;
|
||||||
for (std::vector<ByteString>::iterator iter = directoryList.begin(), end = directoryList.end(); iter != end; ++iter)
|
for (std::vector<ByteString>::iterator iter = directoryList.begin(), end = directoryList.end(); iter != end; ++iter)
|
||||||
{
|
{
|
||||||
@ -339,7 +344,7 @@ std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search,
|
|||||||
if (filename.EndsWith(*extIter))
|
if (filename.EndsWith(*extIter))
|
||||||
{
|
{
|
||||||
extensionMatch = true;
|
extensionMatch = true;
|
||||||
tempfilename = filename.SubstrFromEnd(0, (*extIter).size()).ToUpper();
|
tempfilename = filename.SubstrFromEnd(0, (*extIter).size()).ToLower();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,6 +360,138 @@ std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search,
|
|||||||
return searchResults;
|
return searchResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String DoMigration(ByteString fromDir, ByteString toDir)
|
||||||
|
{
|
||||||
|
if (fromDir.at(fromDir.length() - 1) != '/')
|
||||||
|
fromDir = fromDir + '/';
|
||||||
|
if (toDir.at(toDir.length() - 1) != '/')
|
||||||
|
toDir = toDir + '/';
|
||||||
|
|
||||||
|
std::ofstream logFile(fromDir + "/migrationlog.txt", std::ios::out);
|
||||||
|
logFile << "Running migration of data from " << fromDir + " to " << toDir << std::endl;
|
||||||
|
|
||||||
|
// Get lists of files to migrate
|
||||||
|
auto stamps = DirectorySearch(fromDir + "stamps", "", { ".stm" });
|
||||||
|
auto saves = DirectorySearch(fromDir + "Saves", "", { ".cps", ".stm" });
|
||||||
|
auto scripts = DirectorySearch(fromDir + "scripts", "", { ".lua", ".txt" });
|
||||||
|
auto downloadedScripts = DirectorySearch(fromDir + "scripts/downloaded", "", { ".lua" });
|
||||||
|
bool hasScriptinfo = FileExists(toDir + "scripts/downloaded/scriptinfo");
|
||||||
|
auto screenshots = DirectorySearch(fromDir, "powdertoy-", { ".png" });
|
||||||
|
bool hasAutorun = FileExists(fromDir + "autorun.lua");
|
||||||
|
bool hasPref = FileExists(fromDir + "powder.pref");
|
||||||
|
|
||||||
|
if (stamps.empty() && saves.empty() && scripts.empty() && downloadedScripts.empty() && screenshots.empty() && !hasAutorun && !hasPref)
|
||||||
|
{
|
||||||
|
logFile << "Nothing to migrate.";
|
||||||
|
return "Nothing to migrate. This button is used to migrate data from pre-96.0 TPT installations to the shared directory";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder result;
|
||||||
|
std::stack<ByteString> dirsToDelete;
|
||||||
|
|
||||||
|
// Migrate a list of files
|
||||||
|
auto migrateList = [&](std::vector<ByteString> list, ByteString directory, String niceName) {
|
||||||
|
result << '\n' << niceName << ": ";
|
||||||
|
if (!list.empty() && !directory.empty())
|
||||||
|
MakeDirectory(toDir + directory);
|
||||||
|
int migratedCount = 0, failedCount = 0;
|
||||||
|
for (auto &item : list)
|
||||||
|
{
|
||||||
|
std::string from = fromDir + directory + "/" + item;
|
||||||
|
std::string to = toDir + directory + "/" + item;
|
||||||
|
if (!FileExists(to))
|
||||||
|
{
|
||||||
|
if (rename(from.c_str(), to.c_str()))
|
||||||
|
{
|
||||||
|
failedCount++;
|
||||||
|
logFile << "failed to move " << from << " to " << to << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
migratedCount++;
|
||||||
|
logFile << "moved " << from << " to " << to << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logFile << "skipping " << from << "(already exists)" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dirsToDelete.push(directory);
|
||||||
|
result << "\bt" << migratedCount << " migratated\x0E, \br" << failedCount << " failed\x0E";
|
||||||
|
int duplicates = list.size() - migratedCount - failedCount;
|
||||||
|
if (duplicates)
|
||||||
|
result << ", " << list.size() - migratedCount - failedCount << " skipped (duplicate)";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Migrate a single file
|
||||||
|
auto migrateFile = [&fromDir, &toDir, &result, &logFile](ByteString filename) {
|
||||||
|
ByteString from = fromDir + filename;
|
||||||
|
ByteString to = toDir + filename;
|
||||||
|
if (!FileExists(to))
|
||||||
|
{
|
||||||
|
if (rename(from.c_str(), to.c_str()))
|
||||||
|
{
|
||||||
|
logFile << "failed to move " << from << " to " << to << std::endl;
|
||||||
|
result << "\n\br" << filename.FromUtf8() << " migration failed\x0E";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logFile << "moved " << from << " to " << to << std::endl;
|
||||||
|
result << '\n' << filename.FromUtf8() << " migrated";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logFile << "skipping " << from << "(already exists)" << std::endl;
|
||||||
|
result << '\n' << filename.FromUtf8() << " skipped (already exists)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!DeleteFile(fromDir + filename)) {
|
||||||
|
logFile << "failed to delete " << filename << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Do actual migration
|
||||||
|
DeleteFile(fromDir + "stamps/stamps.def");
|
||||||
|
migrateList(stamps, "stamps", "Stamps");
|
||||||
|
migrateList(saves, "Saves", "Saves");
|
||||||
|
if (!scripts.empty())
|
||||||
|
migrateList(scripts, "scripts", "Scripts");
|
||||||
|
if (!hasScriptinfo && !downloadedScripts.empty())
|
||||||
|
{
|
||||||
|
migrateList(downloadedScripts, "scripts/downloaded", "Downloaded scripts");
|
||||||
|
migrateFile("scripts/downloaded/scriptinfo");
|
||||||
|
}
|
||||||
|
if (!screenshots.empty())
|
||||||
|
migrateList(screenshots, "", "Screenshots");
|
||||||
|
if (hasAutorun)
|
||||||
|
migrateFile("autorun.lua");
|
||||||
|
if (hasPref)
|
||||||
|
migrateFile("powder.pref");
|
||||||
|
|
||||||
|
// Delete leftover directories
|
||||||
|
while (!dirsToDelete.empty())
|
||||||
|
{
|
||||||
|
ByteString toDelete = dirsToDelete.top();
|
||||||
|
if (!DeleteDirectory(fromDir + toDelete)) {
|
||||||
|
logFile << "failed to delete " << toDelete << std::endl;
|
||||||
|
}
|
||||||
|
dirsToDelete.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// chdir into the new directory
|
||||||
|
chdir(toDir.c_str());
|
||||||
|
|
||||||
|
if (scripts.size())
|
||||||
|
Client::Ref().RescanStamps();
|
||||||
|
|
||||||
|
logFile << std::endl << std::endl << "Migration complete. Results: " << result.Build().ToUtf8();
|
||||||
|
logFile.close();
|
||||||
|
|
||||||
|
return result.Build();
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIN
|
#ifdef WIN
|
||||||
ByteString WinNarrow(const std::wstring &source)
|
ByteString WinNarrow(const std::wstring &source)
|
||||||
|
@ -39,11 +39,15 @@ namespace Platform
|
|||||||
*/
|
*/
|
||||||
bool MakeDirectory(ByteString dir);
|
bool MakeDirectory(ByteString dir);
|
||||||
std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search, std::vector<ByteString> extensions);
|
std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search, std::vector<ByteString> extensions);
|
||||||
|
String DoMigration(ByteString fromDir, ByteString toDir);
|
||||||
|
|
||||||
#ifdef WIN
|
#ifdef WIN
|
||||||
ByteString WinNarrow(const std::wstring &source);
|
ByteString WinNarrow(const std::wstring &source);
|
||||||
std::wstring WinWiden(const ByteString &source);
|
std::wstring WinWiden(const ByteString &source);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern std::string originalCwd;
|
||||||
|
extern std::string sharedCwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -44,10 +44,10 @@ class LoadFilesTask: public Task
|
|||||||
notifyProgress(-1);
|
notifyProgress(-1);
|
||||||
for(std::vector<ByteString>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)
|
for(std::vector<ByteString>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)
|
||||||
{
|
{
|
||||||
SaveFile * saveFile = new SaveFile(*iter);
|
SaveFile * saveFile = new SaveFile(directory + *iter);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> data = Client::Ref().ReadFile(*iter);
|
std::vector<unsigned char> data = Client::Ref().ReadFile(directory + *iter);
|
||||||
GameSave * tempSave = new GameSave(data);
|
GameSave * tempSave = new GameSave(data);
|
||||||
saveFile->SetGameSave(tempSave);
|
saveFile->SetGameSave(tempSave);
|
||||||
saveFiles.push_back(saveFile);
|
saveFiles.push_back(saveFile);
|
||||||
|
@ -2,22 +2,19 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#ifdef WIN
|
|
||||||
#include <direct.h>
|
|
||||||
#define getcwd _getcwd
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
#include "SDLCompat.h"
|
#include "SDLCompat.h"
|
||||||
|
|
||||||
#include "OptionsController.h"
|
#include "OptionsController.h"
|
||||||
#include "OptionsModel.h"
|
#include "OptionsModel.h"
|
||||||
|
|
||||||
|
#include "client/Client.h"
|
||||||
#include "common/Platform.h"
|
#include "common/Platform.h"
|
||||||
#include "graphics/Graphics.h"
|
#include "graphics/Graphics.h"
|
||||||
#include "gui/Style.h"
|
#include "gui/Style.h"
|
||||||
#include "simulation/ElementDefs.h"
|
#include "simulation/ElementDefs.h"
|
||||||
|
|
||||||
|
#include "gui/dialogues/ConfirmPrompt.h"
|
||||||
|
#include "gui/dialogues/InformationMessage.h"
|
||||||
#include "gui/interface/Button.h"
|
#include "gui/interface/Button.h"
|
||||||
#include "gui/interface/Checkbox.h"
|
#include "gui/interface/Checkbox.h"
|
||||||
#include "gui/interface/DropDown.h"
|
#include "gui/interface/DropDown.h"
|
||||||
@ -305,8 +302,6 @@ OptionsView::OptionsView():
|
|||||||
scrollPanel->AddChild(tempLabel);
|
scrollPanel->AddChild(tempLabel);
|
||||||
scrollPanel->AddChild(perfectCirclePressure);
|
scrollPanel->AddChild(perfectCirclePressure);
|
||||||
|
|
||||||
//perfectCirclePressure
|
|
||||||
|
|
||||||
currentY+=20;
|
currentY+=20;
|
||||||
decoSpace = new ui::DropDown(ui::Point(8, currentY), ui::Point(60, 16));
|
decoSpace = new ui::DropDown(ui::Point(8, currentY), ui::Point(60, 16));
|
||||||
decoSpace->SetActionCallback({ [this] { c->SetDecoSpace(decoSpace->GetOption().second); } });
|
decoSpace->SetActionCallback({ [this] { c->SetDecoSpace(decoSpace->GetOption().second); } });
|
||||||
@ -324,23 +319,25 @@ OptionsView::OptionsView():
|
|||||||
currentY+=20;
|
currentY+=20;
|
||||||
ui::Button * dataFolderButton = new ui::Button(ui::Point(8, currentY), ui::Point(90, 16), "Open Data Folder");
|
ui::Button * dataFolderButton = new ui::Button(ui::Point(8, currentY), ui::Point(90, 16), "Open Data Folder");
|
||||||
dataFolderButton->SetActionCallback({ [] {
|
dataFolderButton->SetActionCallback({ [] {
|
||||||
auto *cwd = getcwd(NULL, 0);
|
ByteString cwd = Platform::GetCwd();
|
||||||
if (cwd)
|
if (!cwd.empty())
|
||||||
{
|
|
||||||
Platform::OpenURI(cwd);
|
Platform::OpenURI(cwd);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
fprintf(stderr, "cannot open data folder: getcwd(...) failed\n");
|
fprintf(stderr, "cannot open data folder: getcwd(...) failed\n");
|
||||||
}
|
|
||||||
} });
|
} });
|
||||||
scrollPanel->AddChild(dataFolderButton);
|
scrollPanel->AddChild(dataFolderButton);
|
||||||
|
|
||||||
tempLabel = new ui::Label(ui::Point(dataFolderButton->Position.X+dataFolderButton->Size.X+3, currentY), ui::Point(1, 16), "\bg- Open the data and preferences folder");
|
ui::Button * migrationButton = new ui::Button(ui::Point(Size.X - 178, currentY), ui::Point(163, 16), "Migrate to shared data directory");
|
||||||
autowidth(tempLabel);
|
migrationButton->SetActionCallback({ [] {
|
||||||
tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
|
ByteString from = Platform::originalCwd;
|
||||||
tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
|
ByteString to = Platform::sharedCwd;
|
||||||
scrollPanel->AddChild(tempLabel);
|
new ConfirmPrompt("Do Migration?", "This will migrate all stamps, saves, and scripts from\n\bt" + from.FromUtf8() + "\bw\nto the shared data directory at\n\bt" + to.FromUtf8() + "\bw\n\n" +
|
||||||
|
"Files that already exist will not be overwritten.", { [=] () {
|
||||||
|
String ret = Platform::DoMigration(from, to);
|
||||||
|
new InformationMessage("Migration Complete", ret, false);
|
||||||
|
} });
|
||||||
|
} });
|
||||||
|
scrollPanel->AddChild(migrationButton);
|
||||||
|
|
||||||
ui::Button * tempButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK");
|
ui::Button * tempButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK");
|
||||||
tempButton->SetActionCallback({ [this] { c->Exit(); } });
|
tempButton->SetActionCallback({ [this] { c->Exit(); } });
|
||||||
|
Reference in New Issue
Block a user