LZX archive support

I don't know if the plugin already handles it, but as well as the character encoding issue, Amiga filesystems allow (and often have) files with names like con which still cause problems on Windows and need to be mapped to something else.

If you need it, I can share the relevant code from the Archives plugin that adjusts names to make them OK for Windows.

working with C++ for 20 years I am pretty confident you can. but yeah, crash is a real concern here.

Ok, you won. I only worked with C++ for 10 years :slight_smile:

Anyway, even though I'm a regular peasant, let me share my opinion:

I am pretty confident you can.

I did 2 projects with C++ without exceptions. Thus, yes, you absolutely can. However, it is a huge pain. For example, for one of the projects, we had to come up with our own custom STL library because, without exceptions, you can't even do std::string combinedStr = str1 + str2. Actually, it is the same problem with the Rust language, which is why they had to cave in and implement an exception mechanism (panics).

The uses of standard library are all within what should never throw an exception

This is technically not true based on your current implementation. By the way, your code is very high quality. But there are many places where the code can throw (based on your cxx-unlzx and cxx-dopus-lzx code):

  • std::filesystem - pretty much every call that you use can throw.
  • Every STL container throws at least an out of memory exception
  • std::map::at throws
  • std::regex, std::print throws

@Leo I'd love that - please! Thank you very much!
@PolarGoose Thanks. I am not planning a debate on C++ and exceptions. Heterogeneous execution is a hard no for me, but your feedback is noted. I'll look into erasing some of that code from the plugin soon. Your observations re: filesystem and regex align with mine.
@MiDWaN I looked at how TC UnLZX implements date parsing and.. I think implementing workarounds in packer bugs is just not worth it. I addressed empty folders, added file protection bits and comments for now - i think this should be good enough for now :).

2 Likes

How is it related to exceptions? Or I misunderstand what heterogeneous means.

Dropped std::filesystem - it was a bit wonky anyways, so that's a net improvement. The other std:: concerns raised by @PolarGoose should not apply (i think these are all removed from the plugin already, but will double check later).

I've prepared a way to sub any unwanted entry names, too, so @Leo if you can share what names should be corrected - i'd appreciate the help. for now I'm replacing \ (anywhere), and . + .. (as exact matches) with their unicode look-alikes.

1 Like

for now I'm replacing \ (anywhere), and . + .. (as exact matches) with their unicode look-alikes.

very smart.

@Leo if you can share what names should be corrected

Just in case, here are the naming rules on Windows, and it contains the list of forbidden names
Naming Files, Paths, and Namespaces
Hope @Leo gives more details.

working with C++ for 20 years I am pretty confident you can.

You write very good code. Just in case, by You can't use the std library with this flag I meant these things too:

std::vector<std::wstring_view> components;
components.emplace_back(...)

and

return new Plugin();

If you choose to ignore exceptions from such code because "it should never happen", then it is fine. I didn't argue about it. Just seems wrong. As a DOpus plugin, it is fine.

It's certainly good enough for my needs now. I'll let you know if I run into anything. Minor note: You have it up to v0.3 in the Releases, but the version string in Opus VFS plugins still mentions 0.1 :wink:

The main functions are:

  • CheckLegalSubPathStr -- Give it a relative path inside the archive (e.g. Subdir\\File.txt) and it'll tell you if it is legal or not.

  • MakeLegalSubPathStr -- Give it the same thing, and it'll try to convert it to a legal path. Returns true if the input was already legal or was successfully made legal. You don't need to call CheckLegalSubPathStr first (or at all, if you don't need it).

Example usage:

int wmain(int argc, const wchar_t **argv)
{
	for(std::wstring strPath : {
		L"Program Files\\GPSoftware",
		L"C:\\Absolute Path",
		L"Subdir/Aux/Test/Con/Example" })
	{
		if (!CheckLegalSubPathStr(strPath.c_str()))
		{
			wprintf(L"%s : Not legal\n", strPath.c_str());
		}
		else
		{
			wprintf(L"%s : OK\n", strPath.c_str());
		}

		bool fChanged{};

		if (!MakeLegalSubPathStr(strPath, &fChanged))
		{
			wprintf(L"%s : Failed to make legal\n", strPath.c_str());
		}
		else if (fChanged)
		{
			wprintf(L"%s : Legal version\n", strPath.c_str());
		}

		wprintf(L"\n");
	}

	return 0;
}

Running that will output this:

Program Files\GPSoftware : OK

C:\Absolute Path : Not legal
C_\Absolute Path : Legal version

Subdir/Aux/Test/Con/Example : Not legal
Subdir/_Aux/Test/_Con/Example : Legal version

Source that can be compiled into a simple test command-prompt exe using Visual Studio:

MakeLegalArchivePath.7z (3.6 KB)

Since it has to loop through potentially thousands of paths, the code favours speed over readability to some extent, which is why it checks upper/lower versions of characters directly instead of uppercasing or lowercasing the inputs.

Thanks @Leo - your shared file helped me understand the space a bit better and I learned that it's not just the CON/LPT#/NUL but also CON.exe/LPT1.txt/NUL.tar.gz..

I added some custom columns to v0.4 too - relying on windows attributes seems somewhat insufficient, but custom columns can reveal the true protection bits.

Other than somewhat problematic date encoding the plugin should work well. Let me know if anything is still missing - thank you!