1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-11-25 09:44:02 +00:00

Support _mods/modname/mod-info.xml in addition to the old _mods/modname.xml

Needs directory enumeration; I'm not sure that it builds on linux.
Will fix if it doesn't.
This commit is contained in:
fgenesis 2024-11-04 22:38:29 +01:00
parent b59fb507ca
commit 91b7c0bf3f
6 changed files with 217 additions and 19 deletions

View file

@ -1692,19 +1692,11 @@ int DSQ::getEntityTypeIndexByName(std::string s)
return -1; return -1;
} }
void DSQ::LoadModsCallback(const std::string &filename, void *param) bool DSQ::loadModByName(const std::string &name)
{ {
DSQ *self = (DSQ*)param;
size_t pos = filename.find_last_of('/')+1;
size_t pos2 = filename.find_last_of('.');
if(pos2 < pos)
return;
std::string name = filename.substr(pos, pos2-pos);
ModEntry m; ModEntry m;
m.path = name; m.path = name;
m.id = self->modEntries.size(); m.id = modEntries.size();
XMLDocument d; XMLDocument d;
if(!Mod::loadModXML(&d, name)) if(!Mod::loadModXML(&d, name))
@ -1713,21 +1705,58 @@ void DSQ::LoadModsCallback(const std::string &filename, void *param)
if(!err) if(!err)
err = "<unknown error>"; err = "<unknown error>";
std::ostringstream os; std::ostringstream os;
os << "Failed to load mod xml: " << filename << " -- Error: " << err; os << "Failed to load mod xml: " << name << " -- Error: " << err;
debugLog(os.str()); debugLog(os.str());
return; return false;
} }
m.type = Mod::getTypeFromXML(d.FirstChildElement("AquariaMod")); m.type = Mod::getTypeFromXML(d.FirstChildElement("AquariaMod"));
self->modEntries.push_back(m); modEntries.push_back(m);
std::ostringstream ss; std::ostringstream ss;
ss << "Loaded ModEntry [" << m.path << "] -> " << m.id << " | type " << m.type; ss << "Loaded ModEntry [" << m.path << "] -> " << m.id << " | type " << m.type;
debugLog(ss.str()); debugLog(ss.str());
return true;
} }
// _mods/themod.xml
static void EnumerateOuterModXMLCallback(const std::string &filename, void *param)
{
std::vector<std::string> *pNames = (std::vector<std::string>*)param;
size_t pos = filename.find_last_of('/')+1;
size_t pos2 = filename.find_last_of('.');
if(pos2 < pos)
return;
std::string name = filename.substr(pos, pos2-pos);
pNames->push_back(name);
}
// _mods/themod/mod-info.xml
static void EnumerateInnerModXMLCallback(const std::string &filename, void *param)
{
std::vector<std::string> *pNames = (std::vector<std::string>*)param;
std::string fn = filename;
if(fn.empty())
return;
assert(!(fn.back() == '/' || fn.back() == '\''));
fn += "/mod-info.xml";
if(!exists(fn))
return;
size_t pos = filename.find_last_of('/')+1;
std::string name = filename.substr(pos);
pNames->push_back(name);
}
void DSQ::LoadModPackagesCallback(const std::string &filename, void *param) void DSQ::LoadModPackagesCallback(const std::string &filename, void *param)
{ {
DSQ *self = (DSQ*)param; DSQ *self = (DSQ*)param;
@ -1772,7 +1801,17 @@ void DSQ::loadMods()
forEachFile(modpath, ".aqmod", LoadModPackagesCallback, this); forEachFile(modpath, ".aqmod", LoadModPackagesCallback, this);
#endif #endif
forEachFile(modpath, ".xml", LoadModsCallback, this); std::vector<std::string> modnames;
forEachFile(modpath, ".xml", EnumerateOuterModXMLCallback, &modnames);
forEachDir(modpath,EnumerateInnerModXMLCallback, &modnames);
std::sort(modnames.begin(), modnames.end());
modnames.erase(std::unique(modnames.begin(), modnames.end()), modnames.end());
for(size_t i = 0; i < modnames.size(); ++i)
loadModByName(modnames[i]);
selectedMod = 0; selectedMod = 0;
std::ostringstream os; std::ostringstream os;

View file

@ -305,7 +305,7 @@ public:
bool mountModPackage(const std::string&); bool mountModPackage(const std::string&);
bool modIsKnown(const std::string& name); bool modIsKnown(const std::string& name);
void unloadMods(); void unloadMods();
static void LoadModsCallback(const std::string &filename, void *param); bool loadModByName(const std::string &filename);
static void LoadModPackagesCallback(const std::string &filename, void *param); static void LoadModPackagesCallback(const std::string &filename, void *param);
AquariaSaveSlot *selectedSaveSlot; AquariaSaveSlot *selectedSaveSlot;

View file

@ -87,7 +87,10 @@ bool Mod::isEditorBlocked() const
bool Mod::loadModXML(XMLDocument *d, std::string modName) bool Mod::loadModXML(XMLDocument *d, std::string modName)
{ {
return readXML((baseModPath + modName + ".xml").c_str(), *d) == XML_SUCCESS; bool ok = readXML((baseModPath + modName + "/mod-info.xml").c_str(), *d) == XML_SUCCESS;
if(!ok)
ok = readXML((baseModPath + modName + ".xml").c_str(), *d) == XML_SUCCESS;
return ok;
} }

View file

@ -527,7 +527,7 @@ void ModDL::NotifyMod(ModRequest *rq, NetEvent ev, size_t recvd, size_t total)
if(!dsq->modIsKnown(localname)) if(!dsq->modIsKnown(localname))
{ {
// yay, got something new! // yay, got something new!
DSQ::LoadModsCallback(archiveFile, dsq); // does not end in ".xml" but thats no problem here dsq->loadModByName(localname); // FIXME: This assumes that the aqmod file, the contained directory, and it's accompanying xml file all have the same base name
if(dsq->modSelectorScr) if(dsq->modSelectorScr)
dsq->modSelectorScr->initModAndPatchPanel(); // HACK dsq->modSelectorScr->initModAndPatchPanel(); // HACK
} }

View file

@ -202,9 +202,15 @@ void forEachFile_vfscallback(VFILE *vf, void *user)
d->callback(*(d->path) + vf->name(), d->param); d->callback(*(d->path) + vf->name(), d->param);
} }
void forEachDir_vfscallback(ttvfs::DirBase *dir, void *user)
{
vfscallback_s *d = (vfscallback_s*)user;
d->callback(*(d->path) + dir->name(), d->param);
}
#endif #endif
void forEachFile(const std::string& inpath, std::string type, void callback(const std::string &filename, void *param), void *param) void forEachFile(const std::string& inpath, std::string type, FileIterationCallback callback, void *param)
{ {
if (inpath.empty()) return; if (inpath.empty()) return;
@ -331,6 +337,152 @@ void forEachFile(const std::string& inpath, std::string type, void callback(cons
#endif #endif
} }
#if defined(BBGE_BUILD_UNIX)
template<typename T> struct Has_d_type
{
struct Fallback { int d_type; };
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::d_type>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
static bool _unixIsDirectory(const std::string& base, const char *name)
{
std::string full = base;
if(full.back() != '/')
full += '/';
full += name;
struct stat st;
int err = ::stat(full.c_str(), &st, 0);
if(err == -1)
return false;
return S_ISDIR(st.st_mode);
}
template<bool has_d_type>
struct Unix_IsDir
{
inline static bool Get(const std::string& base struct dirent *dp)
{
return _unixIsDirectory(base, dp->d_name);
}
};
template<>
struct Unix_IsDir<true>
{
inline static bool Get(const std::string& base, struct dirent *dp)
{
switch(dp->d_type)
{
case DT_DIR:
return true;
case DT_LNK: // dirent doesn't resolve links, gotta do this manually
case DT_UNKNOWN: // file system isn't sure or doesn't support d_type, try this the hard way
return _unixIsDirectory(base, dp->d_name);
default: ; // avoid warnings
}
return false;
}
};
static inline tio_FileType unixIsDirectory(const std::string& base, struct dirent *dp)
{
return Unix_IsDir<Has_d_type<dirent>::value>::Get(pathfd, dp);
}
// skip if "." or ".."
static inline bool dirlistSkip(const char* fn)
{
return fn[0] == '.' && (!fn[1] || (fn[1] == '.' && !fn[2]));
}
#endif
void forEachDir(const std::string& inpath, FileIterationCallback callback, void *param)
{
if (inpath.empty()) return;
#ifdef BBGE_BUILD_VFS
ttvfs::DirView view;
if(!vfs.FillDirView(inpath.c_str(), view))
{
debugLog("Path '" + inpath + "' does not exist");
return;
}
vfscallback_s dat;
dat.path = &inpath;
dat.ext = NULL;
dat.param = param;
dat.callback = callback;
view.forEachDir(forEachDir_vfscallback, &dat, true);
return;
// -------------------------------------
#endif
std::string path = adjustFilenameCase(inpath.c_str());
debugLog("forEachDir - path: " + path);
#if defined(BBGE_BUILD_UNIX)
struct dirent * dp;
DIR *dir=0;
dir = opendir(path.c_str());
if (dir)
{
struct dirent * dp;
while ( (dp=readdir(dir)) != NULL )
{
if(!dirlistSkip(dp->d_name))
if(unixIsDirectory(inpath, dp))
callback(path + std::string(file->d_name), param);
}
closedir(dir);
}
else
{
debugLog("FAILED TO OPEN DIR");
}
#endif
#ifdef BBGE_BUILD_WINDOWS
BOOL fFinished;
HANDLE hList;
TCHAR szDir[MAX_PATH+1];
WIN32_FIND_DATA FileData;
size_t end = path.size()-1;
if (path[end] != '/')
path[end] += '/';
// Get the proper directory path
// \\ %s\\*
sprintf(szDir, "%s\\*", path.c_str());
// Get the first file
hList = FindFirstFile(szDir, &FileData);
if (hList != INVALID_HANDLE_VALUE)
{
// Traverse through the directory structure
for(;;)
{
if(FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
callback(path + FileData.cFileName, param);
if (!FindNextFile(hList, &FileData))
break;
}
}
FindClose(hList);
#endif
}
#if BBGE_BUILD_UNIX #if BBGE_BUILD_UNIX

View file

@ -6,7 +6,11 @@
void initIcon(void *screen); void initIcon(void *screen);
void destroyIcon(); void destroyIcon();
void messageBox(const std::string &title, const std::string& msg); void messageBox(const std::string &title, const std::string& msg);
void forEachFile(const std::string& inpath, std::string type, void callback(const std::string &filename, void *param), void *param = 0);
typedef void (*FileIterationCallback)(const std::string &filename, void *param);
void forEachFile(const std::string& inpath, std::string type, FileIterationCallback callback, void *param = 0);
void forEachDir(const std::string& inpath, FileIterationCallback callback, void *param = 0);
std::string adjustFilenameCase(const char *_buf); std::string adjustFilenameCase(const char *_buf);
std::string adjustFilenameCase(const std::string&); std::string adjustFilenameCase(const std::string&);
bool createDir(const std::string& d); bool createDir(const std::string& d);