Skip to content

Commit 2b4e47a

Browse files
author
Your Name
committed
Look for map files in a "Maps" subfolder, with root fallback
RPG Maker 2000/2003 games traditionally keep MapNNNN.lmu files directly in the project root. This adds support for storing them in a "Maps" subfolder instead, which existing games and tools can adopt for a tidier project layout. RPG_RT.lmt (TreeMap) itself needed no changes: it only stores map IDs, names and hierarchy/metadata, never file paths. The ID-to-filename mapping is a pure Player-side convention, so only the file lookup logic needed updating. To avoid breaking the large existing library of RPG Maker 2000/2003 games that keep maps in the root, lookup prefers "Maps/" but falls back to the root directory when not found there. This affects map loading (Game_Map::LoadMapFile), async map prefetching (Game_Map::RequestMap), the start-map existence check used to trigger non-standard-extension guessing, and the non-standard LMU extension guesser itself.
1 parent dafe17a commit 2b4e47a

5 files changed

Lines changed: 62 additions & 17 deletions

File tree

src/fileext_guesser.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,10 @@ FileExtGuesser::RPG2KNonStandardFilenameGuesser FileExtGuesser::GetRPG2kProjectW
6767
return RPG2KNonStandardFilenameGuesser();
6868
}
6969

70-
void FileExtGuesser::GuessAndAddLmuExtension(const FilesystemView& fs, Meta const& meta, RPG2KFileExtRemap& mapping)
71-
{
72-
// If metadata is provided, rely on that.
73-
std::string metaLmu = meta.GetLmuAlias();
74-
if (!metaLmu.empty()) {
75-
mapping.extMap[SUFFIX_LMU] = metaLmu;
76-
Output::Debug("Metadata-provided non-standard extension for LMU({})", metaLmu);
77-
} else {
78-
// Try to rescue and determine file extensions.
79-
// Without metadata, scan for matching files. Stop after you find a few;
80-
// we can't just pick the first since there may be some backup files on disk.
70+
namespace {
71+
// Scans a single directory for "mapXXXX.???" files and guesses the LMU
72+
// extension from whichever non-standard extension occurs most often.
73+
void GuessLmuExtensionIn(const FilesystemView& fs, FileExtGuesser::RPG2KFileExtRemap& mapping) {
8174
std::unordered_map<std::string, int> extCounts; // ext => count
8275

8376
const auto* entries = fs.ListDirectory();
@@ -94,14 +87,38 @@ void FileExtGuesser::GuessAndAddLmuExtension(const FilesystemView& fs, Meta cons
9487
if (extCounts[ext] >= 5) {
9588
mapping.extMap[SUFFIX_LMU] = ext;
9689
Output::Debug("Guessing non-standard extension for LMU({})", ext);
97-
break;
90+
return;
9891
}
9992
}
10093
}
10194
}
10295
}
10396
}
10497

98+
void FileExtGuesser::GuessAndAddLmuExtension(const FilesystemView& fs, Meta const& meta, RPG2KFileExtRemap& mapping)
99+
{
100+
// If metadata is provided, rely on that.
101+
std::string metaLmu = meta.GetLmuAlias();
102+
if (!metaLmu.empty()) {
103+
mapping.extMap[SUFFIX_LMU] = metaLmu;
104+
Output::Debug("Metadata-provided non-standard extension for LMU({})", metaLmu);
105+
return;
106+
}
107+
108+
// Try to rescue and determine file extensions.
109+
// Without metadata, scan for matching files. Stop after you find a few;
110+
// we can't just pick the first since there may be some backup files on disk.
111+
// Maps are preferably stored in the "Maps" subfolder, but fall back to
112+
// the project root for games that keep their maps there.
113+
FilesystemView maps_fs = fs.Subtree(MAPS_DIR_NAME);
114+
if (maps_fs) {
115+
GuessLmuExtensionIn(maps_fs, mapping);
116+
}
117+
if (mapping.extMap.find(SUFFIX_LMU) == mapping.extMap.end()) {
118+
GuessLmuExtensionIn(fs, mapping);
119+
}
120+
}
121+
105122
FileExtGuesser::RPG2KFileExtRemap FileExtGuesser::RPG2KNonStandardFilenameGuesser::guessExtensions(Meta& meta)
106123
{
107124
RPG2KFileExtRemap res;

src/game_map.cpp

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,11 +330,11 @@ std::unique_ptr<lcf::rpg::Map> Game_Map::LoadMapFile(int map_id) {
330330
// FIXME: Assert map was cached for async platforms
331331
bool map_is_easyrpg_file = Player::player_config.prefer_easyrpg_map_files.Get();
332332
std::string map_name = Game_Map::ConstructMapName(map_id, map_is_easyrpg_file);
333-
std::string map_file = FileFinder::Game().FindFile(map_name);
333+
std::string map_file = Game_Map::FindMapFile(map_name);
334334
if (map_file.empty()) {
335335
map_is_easyrpg_file = !map_is_easyrpg_file;
336336
map_name = Game_Map::ConstructMapName(map_id, map_is_easyrpg_file);
337-
map_file = FileFinder::Game().FindFile(map_name);
337+
map_file = Game_Map::FindMapFile(map_name);
338338

339339
if (map_file.empty()) {
340340
Output::Error("Loading of Map {} failed.\nThe map was not found.", map_name);
@@ -2017,12 +2017,26 @@ std::string Game_Map::ConstructMapName(int map_id, bool is_easyrpg) {
20172017
}
20182018
}
20192019

2020+
std::string Game_Map::FindMapFile(std::string_view map_name) {
2021+
// Maps are preferably stored in the "Maps" subfolder, but fall back to
2022+
// the game's root directory for compatibility with games that keep
2023+
// their maps there.
2024+
std::string map_file = FileFinder::Game().FindFile(MAPS_DIR_NAME, map_name);
2025+
if (map_file.empty()) {
2026+
map_file = FileFinder::Game().FindFile(map_name);
2027+
}
2028+
return map_file;
2029+
}
2030+
20202031
FileRequestAsync* Game_Map::RequestMap(int map_id) {
20212032
#ifdef __EMSCRIPTEN__
20222033
Player::translation.RequestAndAddMap(map_id);
20232034
#endif
20242035

2025-
auto* request = AsyncHandler::RequestFile(Game_Map::ConstructMapName(map_id, false));
2036+
std::string map_name = Game_Map::ConstructMapName(map_id, false);
2037+
bool in_maps_dir = !FileFinder::Game().FindFile(MAPS_DIR_NAME, map_name).empty();
2038+
2039+
auto* request = AsyncHandler::RequestFile(in_maps_dir ? MAPS_DIR_NAME : ".", map_name);
20262040
request->SetImportantFile(true);
20272041
return request;
20282042
}

src/game_map.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,16 @@ namespace Game_Map {
710710
*/
711711
std::string ConstructMapName(int map_id, bool is_easyrpg);
712712

713+
/**
714+
* Searches for a map file on disk. Maps are preferably stored in the
715+
* "Maps" subfolder, but the game's root directory is also searched for
716+
* compatibility with existing games that were never reorganized.
717+
*
718+
* @param map_name The filename to search for, as returned by ConstructMapName
719+
* @return Path to the found file, or an empty string when not found
720+
*/
721+
std::string FindMapFile(std::string_view map_name);
722+
713723
FileRequestAsync* RequestMap(int map_id);
714724

715725
namespace Caching {

src/options.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@
8282
#define TREEMAP_NAME RPG_RT_PREFIX "." SUFFIX_LMT
8383
#define TREEMAP_NAME_EASYRPG EASY_RT_PREFIX "." SUFFIX_EMT
8484

85+
/** Subfolder that map files (.lmu/.emu) are preferably stored in.
86+
* Games that still keep them in the project root are also supported. */
87+
#define MAPS_DIR_NAME "Maps"
88+
8589
/** File name for additional metadata, such as multi-game save imports. */
8690
#define META_NAME "Meta.ini"
8791

src/player.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -995,8 +995,8 @@ static bool DefaultLmuStartFileExists(const FilesystemView& fs) {
995995
int map_id = Player::start_map_id == -1 ? lcf::Data::treemap.start.party_map_id : Player::start_map_id;
996996
std::string mapName = Game_Map::ConstructMapName(map_id, false);
997997

998-
// Now see if the file exists.
999-
return !fs.FindFile(mapName).empty();
998+
// Now see if the file exists, preferring the "Maps" subfolder.
999+
return !fs.FindFile(MAPS_DIR_NAME, mapName).empty() || !fs.FindFile(mapName).empty();
10001000
}
10011001

10021002
void Player::GuessNonStandardExtensions() {

0 commit comments

Comments
 (0)