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:
jacob1 2021-06-27 18:25:08 -04:00
parent 0292344328
commit b3aa6252ce
No known key found for this signature in database
GPG Key ID: 4E58A32D510E1995
5 changed files with 182 additions and 35 deletions

View File

@ -738,32 +738,34 @@ int main(int argc, char * argv[])
return 1;
}
Platform::originalCwd = Platform::GetCwd();
std::map<ByteString, ByteString> arguments = readArguments(argc, argv);
if(arguments["ddir"].length())
if (arguments["ddir"].length())
{
#ifdef WIN
int failure = _chdir(arguments["ddir"].c_str());
#else
int failure = chdir(arguments["ddir"].c_str());
#endif
if (failure)
{
if (!failure)
Platform::sharedCwd = Platform::GetCwd();
else
perror("failed to chdir to requested ddir");
}
}
else
{
char *ddir = SDL_GetPrefPath(NULL, "The Powder Toy");
#ifdef WIN
struct _stat s;
if(_stat("powder.pref", &s) != 0)
if (_stat("powder.pref", &s) != 0)
#else
struct stat s;
if(stat("powder.pref", &s) != 0)
if (stat("powder.pref", &s) != 0)
#endif
{
char *ddir = SDL_GetPrefPath(NULL, "The Powder Toy");
if(ddir)
if (ddir)
{
#ifdef WIN
int failure = _chdir(ddir);
@ -773,10 +775,17 @@ int main(int argc, char * argv[])
if (failure)
{
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);

View File

@ -5,6 +5,7 @@
#include <cstdio>
#include <cassert>
#include <dirent.h>
#include <fstream>
#include <sys/stat.h>
#ifdef WIN
@ -29,11 +30,13 @@
namespace Platform
{
std::string originalCwd;
std::string sharedCwd;
ByteString GetCwd()
{
char cwdTemp[PATH_MAX];
getcwd(cwdTemp, PATH_MAX);
return cwdTemp;
char *cwd = getcwd(NULL, 0);
return cwd == nullptr ? "" : cwd;
}
ByteString ExecutableName()
@ -324,11 +327,13 @@ std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search,
{
ByteString currentFileName = ByteString(directoryEntry->d_name);
if (currentFileName.length()>4)
directoryList.push_back(directory+currentFileName);
directoryList.push_back(currentFileName);
}
closedir(directoryHandle);
#endif
search = search.ToLower();
std::vector<ByteString> searchResults;
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))
{
extensionMatch = true;
tempfilename = filename.SubstrFromEnd(0, (*extIter).size()).ToUpper();
tempfilename = filename.SubstrFromEnd(0, (*extIter).size()).ToLower();
break;
}
}
@ -355,6 +360,138 @@ std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search,
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
ByteString WinNarrow(const std::wstring &source)

View File

@ -39,11 +39,15 @@ namespace Platform
*/
bool MakeDirectory(ByteString dir);
std::vector<ByteString> DirectorySearch(ByteString directory, ByteString search, std::vector<ByteString> extensions);
String DoMigration(ByteString fromDir, ByteString toDir);
#ifdef WIN
ByteString WinNarrow(const std::wstring &source);
std::wstring WinWiden(const ByteString &source);
#endif
extern std::string originalCwd;
extern std::string sharedCwd;
}
#endif

View File

@ -44,10 +44,10 @@ class LoadFilesTask: public Task
notifyProgress(-1);
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
{
std::vector<unsigned char> data = Client::Ref().ReadFile(*iter);
std::vector<unsigned char> data = Client::Ref().ReadFile(directory + *iter);
GameSave * tempSave = new GameSave(data);
saveFile->SetGameSave(tempSave);
saveFiles.push_back(saveFile);

View File

@ -2,22 +2,19 @@
#include <cstdio>
#include <cstring>
#ifdef WIN
#include <direct.h>
#define getcwd _getcwd
#else
#include <unistd.h>
#endif
#include "SDLCompat.h"
#include "OptionsController.h"
#include "OptionsModel.h"
#include "client/Client.h"
#include "common/Platform.h"
#include "graphics/Graphics.h"
#include "gui/Style.h"
#include "simulation/ElementDefs.h"
#include "gui/dialogues/ConfirmPrompt.h"
#include "gui/dialogues/InformationMessage.h"
#include "gui/interface/Button.h"
#include "gui/interface/Checkbox.h"
#include "gui/interface/DropDown.h"
@ -305,8 +302,6 @@ OptionsView::OptionsView():
scrollPanel->AddChild(tempLabel);
scrollPanel->AddChild(perfectCirclePressure);
//perfectCirclePressure
currentY+=20;
decoSpace = new ui::DropDown(ui::Point(8, currentY), ui::Point(60, 16));
decoSpace->SetActionCallback({ [this] { c->SetDecoSpace(decoSpace->GetOption().second); } });
@ -324,23 +319,25 @@ OptionsView::OptionsView():
currentY+=20;
ui::Button * dataFolderButton = new ui::Button(ui::Point(8, currentY), ui::Point(90, 16), "Open Data Folder");
dataFolderButton->SetActionCallback({ [] {
auto *cwd = getcwd(NULL, 0);
if (cwd)
{
ByteString cwd = Platform::GetCwd();
if (!cwd.empty())
Platform::OpenURI(cwd);
}
else
{
fprintf(stderr, "cannot open data folder: getcwd(...) failed\n");
}
} });
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");
autowidth(tempLabel);
tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
scrollPanel->AddChild(tempLabel);
ui::Button * migrationButton = new ui::Button(ui::Point(Size.X - 178, currentY), ui::Point(163, 16), "Migrate to shared data directory");
migrationButton->SetActionCallback({ [] {
ByteString from = Platform::originalCwd;
ByteString to = Platform::sharedCwd;
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");
tempButton->SetActionCallback({ [this] { c->Exit(); } });