Skip to content
Permalink
Browse files
Make case-sensitive file systems work on filename missmatches
  • Loading branch information
Wohlstand committed May 12, 2020
1 parent 22d811d commit 6edef830c0afa06f08ffa37007aa538e65883a82
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 15 deletions.
@@ -2,6 +2,7 @@ TheXTech Changelog.

Changes for 1.3.2.1
-Reworked joysticks support
-Added a workaround for case-sensitive file systems


Changes for 1.3.2
@@ -9,6 +9,7 @@ list(APPEND UTILS_SRCS
${CMAKE_CURRENT_LIST_DIR}/files.cpp
${CMAKE_CURRENT_LIST_DIR}/strings.cpp
${CMAKE_CURRENT_LIST_DIR}/elapsed_timer.cpp
${CMAKE_CURRENT_LIST_DIR}/dir_list_ci.cpp
)

if(WIN32)
@@ -0,0 +1,111 @@
#include "dir_list_ci.h"

#include "../DirManager/dirman.h"
#include "strings.h"
#include <SDL2/SDL_stdinc.h>


DirListCI::DirListCI(const std::string &curDir)
: m_curDir(curDir)
{
rescan();
}

void DirListCI::setCurDir(const std::string &path)
{
m_curDir = path;
rescan();
}

static void replaceSlashes(std::string &str, const std::string &from)
{
str.clear();
if(from.empty())
return;

str.reserve(from.size());

char prevC = '\0';

for(char c : from)
{
if(c == '\\')
c = '/'; // Replace backslashes
if(c == '/' && prevC == '/')
continue; // skip duplicated slashes
prevC = c;
str.push_back(c);
}
}

std::string DirListCI::resolveFileCase(const std::string &in_name)
{
#ifdef _WIN32
return name; // no need on Windows
#else
if(in_name.empty())
return in_name;

std::string name;
replaceSlashes(name, in_name);

// For sub-directory path, look deeply
auto subDir = name.find('/');
if(subDir != std::string::npos)
{
auto sdName = resolveDirCase(name.substr(0, subDir));
DirListCI sd(m_curDir + "/" + sdName);
return sdName + "/" + sd.resolveFileCase(name.substr(subDir + 1));
}

// keep MixerX path arguments untouched
auto pathArgs = name.find('|');
if(pathArgs != std::string::npos)
{
auto n = name.substr(0, pathArgs);
for(std::string &c : m_fileList)
{
if(SDL_strcasecmp(c.c_str(), n.c_str()) == 0)
return c + name.substr(pathArgs);
}
}
else
for(std::string &c : m_fileList)
{
if(SDL_strcasecmp(c.c_str(), name.c_str()) == 0)
return c;
}

return name;
#endif
}

std::string DirListCI::resolveDirCase(const std::string &name)
{
#ifdef _WIN32
return name;
#else
if(name.empty())
return name;

for(std::string &c : m_dirList)
{
if(SDL_strcasecmp(c.c_str(), name.c_str()) == 0)
return c;
}

return name;
#endif
}

void DirListCI::rescan()
{
m_fileList.clear();
m_dirList.clear();
if(m_curDir.empty())
return;

DirMan d(m_curDir);
d.getListOfFiles(m_fileList);
d.getListOfFolders(m_dirList);
}
@@ -0,0 +1,25 @@
#ifndef DIRLISTCI_H
#define DIRLISTCI_H

#include <string>
#include <vector>

/**
* @brief Case-Insensitive directory list
*/
class DirListCI
{
std::string m_curDir;
std::vector<std::string> m_fileList;
std::vector<std::string> m_dirList;
public:
DirListCI(const std::string &curDir = std::string());
void setCurDir(const std::string &path);

std::string resolveFileCase(const std::string &name);
std::string resolveDirCase(const std::string &name);

void rescan();
};

#endif // DIRLISTCI_H
@@ -28,9 +28,12 @@

#include <DirManager/dirman.h>
#include <Utils/files.h>
#include <Utils/dir_list_ci.h>
#include <PGE_File_Formats/file_formats.h>
#include <fmt_format_ne.h>

static DirListCI s_dirEpisode;
static DirListCI s_dirCustom;

void LoadCustomNPC(int A, std::string cFileName);

@@ -137,6 +140,9 @@ void FindCustomNPCs(/*std::string cFilePath*/)
for(auto &p : files)
existingFiles.insert(FileNamePath + p);

s_dirEpisode.setCurDir(FileNamePath);
s_dirCustom.setCurDir(FileNamePath + FileName);

if(DirMan::exists(FileNamePath + FileName))
{
DirMan searchDataDir(FileNamePath + FileName);
@@ -147,8 +153,8 @@ void FindCustomNPCs(/*std::string cFilePath*/)

for(int A = 1; A < maxNPCType; ++A)
{
std::string npcPath = FileNamePath + fmt::format_ne("npc-{0}.txt", A);
std::string npcPathC = FileNamePath + FileName + fmt::format_ne("/npc-{0}.txt", A);
std::string npcPath = FileNamePath + s_dirEpisode.resolveFileCase(fmt::format_ne("npc-{0}.txt", A));
std::string npcPathC = FileNamePath + FileName + "/" + s_dirCustom.resolveFileCase(fmt::format_ne("npc-{0}.txt", A));
if(Files::fileExists(npcPath))
LoadCustomNPC(A, npcPath);
if(Files::fileExists(npcPathC))
@@ -27,6 +27,7 @@
#include "load_gfx.h"
#include "graphics.h" // SuperPrint
#include <Utils/files.h>
#include <Utils/dir_list_ci.h>
#include <DirManager/dirman.h>
#include <InterProcess/intproc.h>
#include <fmt_format_ne.h>
@@ -36,6 +37,8 @@

#include <set>

static DirListCI s_dirEpisode;
static DirListCI s_dirCustom;

bool gfxLoaderTestMode = false;

@@ -79,13 +82,13 @@ static void loadCGFX(const std::set<std::string> &files,
int *width, int *height, bool& isCustom, StdPicture &texture,
bool world = false)
{
std::string imgPath = dEpisode + fName + ".png";
std::string gifPath = dEpisode + fName + ".gif";
std::string maskPath = dEpisode + fName + "m.gif";
std::string imgPath = dEpisode + s_dirEpisode.resolveFileCase(fName + ".png");
std::string gifPath = dEpisode + s_dirEpisode.resolveFileCase(fName + ".gif");
std::string maskPath = dEpisode + s_dirEpisode.resolveFileCase(fName + "m.gif");

std::string imgPathC = dEpisode + dData + "/" + fName + ".png";
std::string gifPathC = dEpisode + dData + "/" + fName + ".gif";
std::string maskPathC = dEpisode + dData + "/" + fName + "m.gif";
std::string imgPathC = dEpisode + dData + "/" + s_dirEpisode.resolveFileCase(fName + ".png");
std::string gifPathC = dEpisode + dData + "/" + s_dirCustom.resolveFileCase(fName + ".gif");
std::string maskPathC = dEpisode + dData + "/" + s_dirCustom.resolveFileCase(fName + "m.gif");
bool alreadyLoaded = false;

std::string loadedPath;
@@ -511,6 +514,9 @@ void LoadCustomGFX()
std::set<std::string> existingFiles;
getExistingFiles(existingFiles);

s_dirEpisode.setCurDir(FileNamePath);
s_dirCustom.setCurDir(FileNamePath + FileName);

for(int A = 1; A < maxBlockType; ++A)
{
loadCGFX(existingFiles, GfxRoot + fmt::format_ne("block/block-{0}.png", A),
@@ -621,6 +627,9 @@ void LoadWorldCustomGFX()
std::set<std::string> existingFiles;
getExistingFiles(existingFiles);

s_dirEpisode.setCurDir(FileNamePath);
s_dirCustom.setCurDir(FileNamePath + FileName);

for(int A = 1; A < maxTileType; ++A)
{
loadCGFX(existingFiles, GfxRoot + fmt::format_ne("tile/tile-{0}.png", A),
@@ -35,6 +35,7 @@

#include <DirManager/dirman.h>
#include <Utils/files.h>
#include <Utils/dir_list_ci.h>
#include <Logger/logger.h>
#include <PGE_File_Formats/file_formats.h>

@@ -95,6 +96,8 @@ bool OpenLevelData(LevelData &lvl, const std::string FilePath)
// int mSections = 0;
// Location_t tempLocation;

DirListCI dirEpisode;

qScreen = false;
ClearLevel();
BlockSound();
@@ -105,9 +108,11 @@ bool OpenLevelData(LevelData &lvl, const std::string FilePath)
FileFormats::smbx64LevelSortBlocks(lvl);
FileFormats::smbx64LevelSortBGOs(lvl);

FileName = lvl.meta.filename;
dirEpisode.setCurDir(lvl.meta.path);
FileName = dirEpisode.resolveDirCase(lvl.meta.filename);
FileNamePath = lvl.meta.path + "/";


if(!FilePath.empty())
{
FileNameFull = Files::basename(FilePath);
@@ -165,7 +170,7 @@ bool OpenLevelData(LevelData &lvl, const std::string FilePath)
Background2REAL[B] = Background2[B];
NoTurnBack[B] = s.lock_left_scroll;
UnderWater[B] = s.underwater;
CustomMusic[B] = s.music_file;
CustomMusic[B] = dirEpisode.resolveFileCase(s.music_file);
B++;
if(B > maxSections)
break;
@@ -440,7 +445,7 @@ bool OpenLevelData(LevelData &lvl, const std::string FilePath)
warp.Direction = w.idirect;
warp.Direction2 = w.odirect;
warp.Effect = w.type;
warp.level = w.lname;
warp.level = dirEpisode.resolveFileCase(w.lname);
warp.LevelWarp = int(w.warpto);
warp.LevelEnt = w.lvl_i;

@@ -31,6 +31,7 @@

#include <Utils/strings.h>
#include <Utils/files.h>
#include <Utils/dir_list_ci.h>
#include <Logger/logger.h>
#include <PGE_File_Formats/file_formats.h>

@@ -42,6 +43,7 @@ void OpenWorld(std::string FilePath)
int A = 0;
int B = 0;
WorldData wld;
DirListCI dirEpisode;

ClearWorld();

@@ -53,8 +55,9 @@ void OpenWorld(std::string FilePath)
// break;
// }

dirEpisode.setCurDir(wld.meta.path);
FileNameFull = Files::basename(FilePath);
FileName = wld.meta.filename; //FilePath.substr(FilePath.length() - (FilePath.length() - A));
FileName = dirEpisode.resolveDirCase(wld.meta.filename); //FilePath.substr(FilePath.length() - (FilePath.length() - A));
FileNamePath = wld.meta.path + "/"; //FilePath.substr(0, (A));

if(wld.meta.RecentFormat == LevelData::SMBX64)
@@ -75,7 +78,7 @@ void OpenWorld(std::string FilePath)
blockCharacter[3] = wld.nocharacter3;
blockCharacter[4] = wld.nocharacter4;
blockCharacter[5] = wld.nocharacter5;
StartLevel = wld.IntroLevel_file;
StartLevel = dirEpisode.resolveFileCase(wld.IntroLevel_file);
NoMap = wld.HubStyledWorld;
RestartLevel = wld.restartlevel;

@@ -198,7 +201,7 @@ void OpenWorld(std::string FilePath)
ll.Location.X = l.x;
ll.Location.Y = l.y;
ll.Type = int(l.id);
ll.FileName = l.lvlfile;
ll.FileName = dirEpisode.resolveFileCase(l.lvlfile);
ll.LevelName = l.title;
ll.LevelExit[1] = l.top_exit;
ll.LevelExit[2] = l.left_exit;
@@ -238,7 +241,7 @@ void OpenWorld(std::string FilePath)
box.Location.X = m.x;
box.Location.Y = m.y;
box.Type = int(m.id);
box.MusicFile = m.music_file;
box.MusicFile = dirEpisode.resolveFileCase(m.music_file);

// In game they are smaller (30x30), in world they are 32x32
box.Location.Width = 30;

0 comments on commit 6edef83

Please sign in to comment.