Skip to content

Conversation

@notimaginative
Copy link
Contributor

Allow indexing of files in subfolders of known cfile path types as if the files in those subfolders were directly part of the known path. Essentially a cosmetic change allowing mod makers to organize their files in a better way without requiring any direct content changes or special code handling to take advantage of the feature.

@notimaginative
Copy link
Contributor Author

This has yet to be tested on Windows and may have unintended side effects for FRED2 as well! Leaving as draft until sufficient testing and feedback has been done.

To take advantage of this feature you just need to put files in folders. Any subfolder of an existing cfile path type is treated as if it was directly in that base path.

For instance:

data/effects/wepanim.eff
data/effects/webanim_....dds

could instead be organized like:

data/effects/weapons/wepanim/wepanim.eff
data/effects/weapons/wepanim/wepanim_....dds

or like:

data/effects/weapons/wepanim.eff
data/effects/weapons/wepanim/wepanim_....dds

and everything would just work. Models, maps, sounds, tables, scripts, ... can all be organized the same way: however you like!

Scripts also now support requiring other scripts directly out of subfolders too. This allows you to have scripts with the same name, organized in different subfolders, and load the specific script that you want.

For instance, you have a starfield.lua script that is different between missions. You can organize it like so:

data/scripts/missions/sm01-01/starfield.lua
data/scripts/missions/sm01-02/starfield.lua

and use require("missions.sm01-01.starfield") to get your desired script.

@notimaginative
Copy link
Contributor Author

notimaginative commented Oct 10, 2022

Known quirks/issues:

  • Double indexing: cfile paths with known subfolders will have the files in those subfolders indexed twice. Currently this is only affecting data/interface as workarounds exist for the other instances
  • Lack of list specificity: For direct file requests (cfopen()) it's possible to get a file directly from a subpath as opposed to any file with that name. cf_get_file_list() does not currently support this ability
  • Load/sort ordering of scripts and tables: Files in subfolders are indexed in place, meaning that the files in those subfolders are ordered in a list by the order of the parent folder. This may not behave as expected in some cases

For the sort order issue, an example would be where you have a zLoadFirst-sct.tbm and a folder named zLoadFirst containing the files aaa-sct.tbm and zzz-sct.tbm. Although the files are all sorted in alphabetical order, the folder will be sorted after the similarly named script tbm. So when processed into the file list for parsing it will actually be zzz-sct.tbm which is loaded first.

Essentially, this file structure as seen in a directory listing:

data/scripts/last-sct.tbm
data/scripts/zLoadFirst/aaa-sct.tbm
data/scripts/zLoadFirst/zzz-sct.tbm
data/scripts/zLoadFirst-sct.tbm

will actually be indexed in the following order: last-sct.tbm, zLoadFirst-sct.tbm, aaa-sct.tbm, zzz-sct.tbm
and when the file list is returned for parsing it looks like: zzz-sct.tbm, aaa-sct.tbm, zLoadFirst-sct.tbm, last-sct.tbm

@notimaginative
Copy link
Contributor Author

Testing and feedback would be appreciated from @Goober5000, @EatThePath, and @wookieejedi

@notimaginative notimaginative added the enhancement A new feature or upgrade of an existing feature to add additional functionality. label Oct 11, 2022
@notimaginative
Copy link
Contributor Author

Obsoletes #449. May provide a sufficient alternative to #3391.

They aren't directly referenced anywhere by pathtype and the new
subfolder code will continue to index and load files from them
An alternative is left in place for old fonts, as that was the only place
it appeared to be used.

This method of file localization conflicts with subfolder support however
so if it's needed in the future then changes to the subfolder code will
be necessary.
@EatThePath
Copy link
Contributor

Hopefully will be able to test soon, but some comments from reading the description:
The quirk of subfolder load order seems potentially confusing, but also useful. Probably good on a whole.

Just to check my understanding...
As I read it, if I have a mod stack of -mod a,b, and files
a:data\tables\blah-shp.tbm
b:data\tables\ships\blah-shp.tbm
then only one of the blah-shp.tbm files will be parsed, just as if both were in the tables dir as before?
But if I have
a:data\scripts\a\blah.lua
b:data\scripts\b\blah.lua
Then both blah.lua files will be loaded and accessible with the right paths?

In that last case, would both blah.lua files be able to potentially load the other, if the right path is used?

@notimaginative
Copy link
Contributor Author

For your first example, that's correct. Right now the list functions have no concept of subfolders so as far as that code goes those two files are just the same thing in different roots and the second one is filtered out. You would have the same problem if they were in the same mod too, although the indexing order would come in to play then.

The plan is to modify those list functions to allow something like looking for ships/*-shp.tbm (or even just ships/*.tbm) and just return the files which are in a ships subfolder. The code for that is just a bit messier than I originally thought so I haven't managed to get it working yet.

For your second example, that's also correct. You would be able to directly reference a/blah.lua in another script, and reference b/blah.lua from a/blah.lua. But if you just tried to load blah.lua then it would grab a/blah.lua because it was indexed first.

So you wouldn't want to use the same file names for texture maps or effects just because they are in subfolders, for example. When opening files like that it only searches for the file name and doesn't take folders into account. So having data/maps/fighter/hull.png and data/maps/capship/hull.png would be a really bad idea since you couldn't choose which to load and it would just grab the first one found.

@EatThePath
Copy link
Contributor

sounds fairly good all around. It might be useful for me to have the lua behavior apply to config files too, but not crucial. Being able to list with a subfolder path might be more important, but the lack of it wouldn't be a merge blocker to my mind.

Some of the motivation behind #3391 is me chafing under the 31 character limit, and if I'm making a lot of individual tables I get the desire to not type those four obligatory characters every time. But those are relatively minor things, and the bigger organizational goals of that FR would be served by this arrangement. I wouldn't object to that issue being closed with this.

I'll try to give this some practical testing. Now that I've refreshed my memory I definitely have some use cases to try.

@notimaginative
Copy link
Contributor Author

Both cfopen() and cf_find_file_location() support subfolders, so it should work with config files too as far as I know. Within scripts you should be able to just write out the full path to the file. For other code you just use a path relative to the cfile pathtype in the filename. So if the file is data/config/folder/file.cfg you set CF_TYPE_CONFIG as the dir_type/pathtype and folder/file.cfg as the filename. It will parse the filename, extract the path, and go from there. And it will convert directory separators as well so you don't have to worry about which to use.

If it doesn't work let me know and I'll make sure to get it fixed.

Subfolders in file lists is just a convoluted process but I do intend to get it working before this is merged. For other files like textures, effects, and sounds, that code does technically support subfolders but there isn't really a good way to modify the code to use them. I suppose that including a path with the filename in a table entry or model to load textures/effects might work, but I haven't tried it.

I should also note that in all of this new code I'm removing the 31 character limit as I go. VP files will still have the limit, and I can't fix that without a new archive format, but at the cfile level at least the limit is largely gone. We just need to start converting other references in the code to use SCP_string instead of a fixed length.

@EatThePath
Copy link
Contributor

Both cfopen() and cf_find_file_location() support subfolders, so it should work with config files too as far as I know. Within scripts you should be able to just write out the full path to the file. For other code you just use a path relative to the cfile pathtype in the filename. So if the file is data/config/folder/file.cfg you set CF_TYPE_CONFIG as the dir_type/pathtype and folder/file.cfg as the filename. It will parse the filename, extract the path, and go from there. And it will convert directory separators as well so you don't have to worry about which to use.

Handy!

To be specific about my goals. I want to be able to get a list of list all of the config files in data/config/modulename/ in all roots, so I can read them all. This is to facilitate tbm-like features for script configs.

I should also note that in all of this new code I'm removing the 31 character limit as I go. VP files will still have the limit, and I can't fix that without a new archive format, but at the cfile level at least the limit is largely gone. We just need to start converting other references in the code to use SCP_string instead of a fixed length.

Good to hear!

@notimaginative
Copy link
Contributor Author

To be specific about my goals. I want to be able to get a list of list all of the config files in data/config/modulename/ in all roots, so I can read them all. This is to facilitate tbm-like features for script configs.

Gotcha. That should be doable once I get subfolders working in the list functions (probably next weekend).

@notimaginative
Copy link
Contributor Author

Got it partially working with lists now and I'll finish it up over the next week as time allows. It has three different search modes:

*-shp.tbm: default behavior, basically just how it's always worked

folder/*-shp.tbm: similar to default behavior but only matching files in folder subpath

*/*-shp.tbm: matches files with filter in all paths, allowing for duplicate filenames if the path is different. So for your previous blah-shp.tbm example, this search would include both of the files

@EatThePath
Copy link
Contributor

those search methods seem to cover the bases at a glance, if I think of any issues I'll let you know.

I've reorganized dev warmachine to heavily use this and it works best I can tell. I'm around 10 fps on the machine I currently have access to, so I can't stresstest it much, but it launches without evident issue.

One thought: It seems extra easy now to accidentally duplicate a filename in this system. It would probably be useful to have a warning if a file 'shadows' another file in the same root.

@notimaginative
Copy link
Contributor Author

One thought: It seems extra easy now to accidentally duplicate a filename in this system. It would probably be useful to have a warning if a file 'shadows' another file in the same root.

Yeah, that's an issue I haven't figured out a good way to deal with yet. It's a primary issue with file lists as a list with both file.cfg and folder/file.cfg can open the one in folder when asking for just file.cfg if the folder version was indexed first. There is currently no way to work around that and I'm not even sure if it's possible without breaking something.

I'll be thinking on it this week, so maybe I'll have a solution by the weekend.

@EatThePath
Copy link
Contributor

I was thinking more like your example of a hull.dds texture. If I have mymod:data/maps/gtcbig/hull.dds and mymod:data/maps/gtfsmall/hull.dds, I'd want to have a warning on startup. But having one of those plus theirmod:data/maps/hull.dds would be fine. That seems like something you could check during indexing, but it might be expensive...

@notimaginative
Copy link
Contributor Author

I'm hesitant to do that since the performance hit could vary quite wildly and we'd surely get complaints. But it is possible and we could limit it to just debug builds. Maybe I'll try adding something and we can just experiment with it a bit.

As an alternative, what do you think about a comprehensive check via cmdline option? Sort of like -pofspew but with a report detailing whatever cfile stuff might be of interest. Not as helpful as an auto check during indexing at startup, but perhaps more useful long term as a diagnostic tool. That would be outside the scope of this PR though.

@notimaginative
Copy link
Contributor Author

I've gotten most of the kinks worked out on lists and so far all of my tests are getting predictable results. I'm going to sit on it another day or two before pushing just in case additional tweaks are required. It's going to take someone else having a go at it to give things a proper test.

The two versions of cf_get_file_list() should work the same. However cf_get_file_list_preallocated() will not support subfolders due to the limited string length. I don't believe that's going to be an issue though. Plus, reworking the code to only use the newer, truly dynamic version of cf_get_file_list() is next on my cfile agenda once this PR clears.

The big problem is shadowing, and I'm just not sure how to deal with it. For testing I have a mod with a VP containing data/tables/test-sct.tbm and a loose file of data/tables/test/test-sct.tbm. With a standard search of *-sct.tbm a single test-sct.tbm is returned and the loose file is executed. With a glob search of */*-sct.tbm both files are returned correctly, however the loose file is executed twice since it shadows the one in the VP. Obviously this shadowing is across roots. I've thought of a little hack to deal with that (prepending ./ to the filename), though it would break sorting the list by name.

But as that test shows, warning about shadowed files will need to take all roots into account to really be effective.

@EatThePath
Copy link
Contributor

I'll try to get more testing of a reorganized mod in this weekend, the situation keeping me from it is now resolved.

When it comes to warnings, I think it's probably something that this feature can be merged without, we can always work more warnings in later and I think the game is better off with subfolders and no new warnings than it is without subfolders.

@notimaginative
Copy link
Contributor Author

I have worked up a simple shadow detection check which might do the trick. Two issues are that it can't work as a Warning() due to some weird SDL windowing issue that I'm not keen to track down at the moment, and depending on the file layout it can trigger a lot of false positives.

It's easy enough to just print it to the debug log to deal with issue one. That will make the log a bit messy though. The windowing issue is that, during this stage of the init process, creating a Warning() dialog appears to prevent the main game window from being created. At least on Linux.

I'm a little concerned with the false positives though. It triggers on possible shadowing, but it's the actual usage which determines whether it's a problem. So having this check default to enabled may be a huge nuisance if there are more than a couple of shadowed files.

@notimaginative
Copy link
Contributor Author

Test for shadowed files added with a warning to the debug log. Give it a try and let me know if you think that is good enough or not. But again, the false positives once the subfolder setup is fully in use may just annoy people. I made sure that it's easy enough to disable though.

@EatThePath
Copy link
Contributor

I pulled that and gave it a spin and my dev copy of warmachine has 270 shadowing warnings. Looks like about half of them are because BP actually has a subfolder in their voice/personas folder with duplicated files, so those are "real" shadowings, but the rest look to be intentional/desirable shadowings.

@EatThePath
Copy link
Contributor

Hit this one by going to the tech room mission simulator.

ASSERTION: "rc == Num_campaigns" at missioncampaign.cpp:312

Assert: "rc == Num_campaigns"
File: missioncampaign.cpp
Line: 312

ntdll.dll! NtWaitForSingleObject + 20 bytes
KERNELBASE.dll! WaitForSingleObjectEx + 142 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! dump_stacktrace + 283 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! os::dialogs::AssertMessage + 608 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! mission_campaign_build_list + 408 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! sim_room_init + 3923 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! game_enter_state + 1593 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! gameseq_set_state + 299 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! game_process_event + 121 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! game_main + 952 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! SDL_main + 343 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! main_getcmdline + 245 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! invoke_main + 50 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! __scrt_common_main_seh + 302 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! __scrt_common_main + 14 bytes
fs2_open_22_3_0_x64_AVX2-FASTDBG.exe! WinMainCRTStartup + 14 bytes
KERNEL32.DLL! BaseThreadInitThunk + 20 bytes
ntdll.dll! RtlUserThreadStart + 33 bytes

Also fixes discrepancy in older cf_get_file_list() when a sub path
is specified (should be used in all cases, not just glob).
@notimaginative
Copy link
Contributor Author

notimaginative commented Oct 31, 2022

Nothing to be done about the BP issue from a code perspective. And I think there are some similar cases with other mods as well. It's just going to have to be dealt with on the content side.

Sim room bug should be fixed now. A misguided attempt to optimize things messed it up.

@notimaginative
Copy link
Contributor Author

notimaginative commented Nov 6, 2022

I haven't found any other issues in my own testing, or further areas of improvement. Setting this as ready for review and hopefully some additional testing will be done by others as well.

@notimaginative notimaginative marked this pull request as ready for review November 6, 2022 23:46
Copy link
Member

@BMagnu BMagnu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good, especially with all the apparent testing that's already happened.
Some questions and comments

@EatThePath
Copy link
Contributor

EatThePath commented Nov 8, 2022

I need to circle back to this and retest the newest version, I've been wrapped up in my own branches a lot.

Nothing to be done about the BP issue from a code perspective. And I think there are some similar cases with other mods as well. It's just going to have to be dealt with on the content side.

I agree with that for sure. So long as it's a log print and not a full Warning, the shadowing messages seem appropriate. I'm tempted to make a new default-on "load order" filter category, but that can be a followup if it feels appropriate once people get some experince with the messages.

Copy link
Member

@BMagnu BMagnu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good now.

@wookieejedi wookieejedi removed the request for review from asarium November 14, 2022 18:56
@wookieejedi wookieejedi added this to the Release 22.4 milestone Nov 14, 2022
@wookieejedi
Copy link
Member

Per Discord Discussion:
I'll merge tomorrow, and any other potential merges tomorrow can be evaluated on a case-by-case basis

@wookieejedi wookieejedi merged commit 3e57da1 into scp-fs2open:master Nov 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement A new feature or upgrade of an existing feature to add additional functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants