From a526c7febb2fbf4aa9d71ca84199befa602453c9 Mon Sep 17 00:00:00 2001 From: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Date: Thu, 3 Apr 2025 19:36:05 -0300 Subject: [PATCH 1/4] Case-insensitive ZIP loading --- polymod/fs/SysZipFileSystem.hx | 11 ++--- polymod/util/InsensitiveMap.hx | 79 ++++++++++++++++++++++++++++++++++ polymod/util/zip/ZipParser.hx | 5 ++- 3 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 polymod/util/InsensitiveMap.hx diff --git a/polymod/fs/SysZipFileSystem.hx b/polymod/fs/SysZipFileSystem.hx index d6b58438..0b26fd58 100644 --- a/polymod/fs/SysZipFileSystem.hx +++ b/polymod/fs/SysZipFileSystem.hx @@ -1,5 +1,6 @@ package polymod.fs; +import polymod.util.InsensitiveMap; import polymod.fs.ZipFileSystem.ZipFileSystemParams; #if !sys class SysZipFileSystem extends polymod.fs.StubFileSystem @@ -33,7 +34,7 @@ class SysZipFileSystem extends SysFileSystem /** * Specifies the name of the ZIP that contains each file. */ - var filesLocations:Map; + var filesLocations:InsensitiveMap; /** * Specifies the names of available directories within the ZIP files. @@ -48,7 +49,7 @@ class SysZipFileSystem extends SysFileSystem public function new(params:ZipFileSystemParams) { super(params); - filesLocations = new Map(); + filesLocations = new InsensitiveMap(); zipParsers = new Map(); fileDirectories = []; @@ -125,7 +126,7 @@ class SysZipFileSystem extends SysFileSystem return super.isDirectory(path); } - public override function readDirectory(path:String) + public override function readDirectory(path:String):Array { // Remove trailing slash if (path.endsWith("/")) @@ -141,14 +142,14 @@ class SysZipFileSystem extends SysFileSystem for (file in filesLocations.keys()) { - if (Path.directory(file) == path) + if (Path.directory(file).toLowerCase() == path.toLowerCase()) { result.push(Path.withoutDirectory(file)); } } for (dir in fileDirectories) { - if (Path.directory(dir) == path) + if (Path.directory(dir).toLowerCase() == path.toLowerCase()) { result.push(Path.withoutDirectory(dir)); } diff --git a/polymod/util/InsensitiveMap.hx b/polymod/util/InsensitiveMap.hx new file mode 100644 index 00000000..e8da0d35 --- /dev/null +++ b/polymod/util/InsensitiveMap.hx @@ -0,0 +1,79 @@ +package polymod.util; + +import haxe.ds.StringMap; +import haxe.Constraints.IMap; + +/** + * A string map which treats any letter cases the same (case insensitive). + * Unlike other maps, if a value already exists it won't be overwritten. + */ +class InsensitiveMap implements IMap { + var data:StringMap = new StringMap(); + var originalKeys:Map = new Map(); + + public function new() {} + + public function set(key:String, value:T):Void { + var lowerKey = key.toLowerCase(); + if (data.exists(lowerKey)) return; + + data.set(lowerKey, value); + originalKeys.set(lowerKey, key); + } + + public inline function get(key:String):Null { + return data.get(key.toLowerCase()); + } + + public inline function exists(key:String):Bool { + return data.exists(key.toLowerCase()); + } + + public function remove(key:String):Bool { + var lowerKey = key.toLowerCase(); + originalKeys.remove(lowerKey); + return data.remove(lowerKey); + } + + public function clear():Void { + data.clear(); + originalKeys.clear(); + } + + public function copy():InsensitiveMap { + var res = new InsensitiveMap(); + res.data = data.copy(); + res.originalKeys = originalKeys.copy(); + return res; + } + + public inline function keys():Iterator { + return originalKeys.iterator(); + } + + public function keyValueIterator() { + return { + var it = originalKeys.keys(); + return { + hasNext: function() return it.hasNext(), + next: function() { + var lowerKey = it.next(); + var originalKey = originalKeys.get(lowerKey); + return { key: originalKey, value: data.get(lowerKey) }; + } + }; + }; + } + + public inline function iterator() { + return data.iterator(); + } + + public function toString():String { + var parts = []; + for (key in keys()) { + parts.push('$key => ${get(key)}'); + } + return '{' + parts.join(', ') + '}'; + } +} diff --git a/polymod/util/zip/ZipParser.hx b/polymod/util/zip/ZipParser.hx index 93e0d1d1..b2fe5cb7 100644 --- a/polymod/util/zip/ZipParser.hx +++ b/polymod/util/zip/ZipParser.hx @@ -3,6 +3,7 @@ package polymod.util.zip; #if sys import haxe.ds.StringMap; import haxe.io.Bytes; +import polymod.util.InsensitiveMap; import sys.io.File; import sys.io.FileInput; @@ -34,7 +35,7 @@ class ZipParser * The central directory records, as parsed from the central directory. * These contain metadata about each file in the archive. */ - public var centralDirectoryRecords:StringMap; + public var centralDirectoryRecords:InsensitiveMap; public function new(fileName:String) { @@ -68,7 +69,7 @@ class ZipParser */ function getAllCentralDirectoryHeaders():Void { - this.centralDirectoryRecords = new StringMap(); + this.centralDirectoryRecords = new InsensitiveMap(); fileHandle.seek(this.endOfCentralDirectoryRecord.cdrOffset, SeekBegin); for (_ in 0...this.endOfCentralDirectoryRecord.cdrsTotal) { From 230f487000ad7b978fca4abe276fdd62cbdcac72 Mon Sep 17 00:00:00 2001 From: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Date: Wed, 9 Apr 2025 20:28:01 -0300 Subject: [PATCH 2/4] Make it optional --- polymod/PolymodConfig.hx | 18 ++++++++++++++++++ polymod/fs/SysZipFileSystem.hx | 21 +++++++++++++++------ polymod/util/zip/ZipParser.hx | 5 +++-- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/polymod/PolymodConfig.hx b/polymod/PolymodConfig.hx index 65865e9e..5a7075de 100644 --- a/polymod/PolymodConfig.hx +++ b/polymod/PolymodConfig.hx @@ -229,4 +229,22 @@ class PolymodConfig modIgnoreFiles = DefineUtil.getDefineStringArray('POLYMOD_MOD_IGNORE', ['LICENSE.txt', 'ASSET_LICENSE.txt', 'CODE_LICENSE.txt']); return modIgnoreFiles; } + + /** + * If true, ZIP mods' assets will be loaded in a case-insensitive way. + * For example, if trying to load an asset called `FOO.txt`, a file called `foo.txt` will be loaded if it exists. + * + * Set this option by settings the `POLYMOD_ZIP_INSENSITIVE` Haxe define at compile time, + * or by settings this value in your code. + * + * @default `true` + */ + public static var caseInsensitiveZipLoading(get, default):Null; + + static function get_caseInsensitiveZipLoading():Bool + { + if (caseInsensitiveZipLoading == null) + caseInsensitiveZipLoading = DefineUtil.getDefineBool('POLYMOD_ZIP_INSENSITIVE', true); + return caseInsensitiveZipLoading; + } } diff --git a/polymod/fs/SysZipFileSystem.hx b/polymod/fs/SysZipFileSystem.hx index 0b26fd58..2208d7f5 100644 --- a/polymod/fs/SysZipFileSystem.hx +++ b/polymod/fs/SysZipFileSystem.hx @@ -1,6 +1,5 @@ package polymod.fs; -import polymod.util.InsensitiveMap; import polymod.fs.ZipFileSystem.ZipFileSystemParams; #if !sys class SysZipFileSystem extends polymod.fs.StubFileSystem @@ -12,10 +11,13 @@ class SysZipFileSystem extends polymod.fs.StubFileSystem } } #else +import haxe.Constraints.IMap; +import haxe.ds.StringMap; import haxe.io.Bytes; import haxe.io.Path; import polymod.Polymod.ModMetadata; import polymod.util.Util; +import polymod.util.InsensitiveMap; import polymod.util.zip.ZipParser; import sys.io.File; import thx.semver.VersionRule; @@ -34,7 +36,7 @@ class SysZipFileSystem extends SysFileSystem /** * Specifies the name of the ZIP that contains each file. */ - var filesLocations:InsensitiveMap; + var filesLocations:IMap; /** * Specifies the names of available directories within the ZIP files. @@ -49,7 +51,7 @@ class SysZipFileSystem extends SysFileSystem public function new(params:ZipFileSystemParams) { super(params); - filesLocations = new InsensitiveMap(); + filesLocations = PolymodConfig.caseInsensitiveZipLoading ? new InsensitiveMap() : new StringMap(); zipParsers = new Map(); fileDirectories = []; @@ -137,19 +139,26 @@ class SysZipFileSystem extends SysFileSystem if (fileDirectories.contains(path)) { + final insensitive:Bool = PolymodConfig.caseInsensitiveZipLoading; + if (insensitive) + path = path.toLowerCase(); + // We check if directory ==, because // we don't want to read the directory recursively. - for (file in filesLocations.keys()) { - if (Path.directory(file).toLowerCase() == path.toLowerCase()) + var filePath = Path.directory(file); + if (insensitive) filePath = filePath.toLowerCase(); + if (filePath == path) { result.push(Path.withoutDirectory(file)); } } for (dir in fileDirectories) { - if (Path.directory(dir).toLowerCase() == path.toLowerCase()) + var dirPath = Path.directory(dir); + if (insensitive) dirPath = dirPath.toLowerCase(); + if (dirPath == path) { result.push(Path.withoutDirectory(dir)); } diff --git a/polymod/util/zip/ZipParser.hx b/polymod/util/zip/ZipParser.hx index b2fe5cb7..48afd0f0 100644 --- a/polymod/util/zip/ZipParser.hx +++ b/polymod/util/zip/ZipParser.hx @@ -1,6 +1,7 @@ package polymod.util.zip; #if sys +import haxe.Constraints.IMap; import haxe.ds.StringMap; import haxe.io.Bytes; import polymod.util.InsensitiveMap; @@ -35,7 +36,7 @@ class ZipParser * The central directory records, as parsed from the central directory. * These contain metadata about each file in the archive. */ - public var centralDirectoryRecords:InsensitiveMap; + public var centralDirectoryRecords:IMap; public function new(fileName:String) { @@ -69,7 +70,7 @@ class ZipParser */ function getAllCentralDirectoryHeaders():Void { - this.centralDirectoryRecords = new InsensitiveMap(); + this.centralDirectoryRecords = PolymodConfig.caseInsensitiveZipLoading ? new InsensitiveMap() : new StringMap(); fileHandle.seek(this.endOfCentralDirectoryRecord.cdrOffset, SeekBegin); for (_ in 0...this.endOfCentralDirectoryRecord.cdrsTotal) { From 2d4215e31fa979d83a116f0aa4ca64b702d9c3cc Mon Sep 17 00:00:00 2001 From: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Date: Wed, 9 Apr 2025 21:56:25 -0300 Subject: [PATCH 3/4] Turn it off by default as it didn't make sense with the define enabling it as well --- polymod/PolymodConfig.hx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/polymod/PolymodConfig.hx b/polymod/PolymodConfig.hx index 5a7075de..22d16bf2 100644 --- a/polymod/PolymodConfig.hx +++ b/polymod/PolymodConfig.hx @@ -234,17 +234,17 @@ class PolymodConfig * If true, ZIP mods' assets will be loaded in a case-insensitive way. * For example, if trying to load an asset called `FOO.txt`, a file called `foo.txt` will be loaded if it exists. * - * Set this option by settings the `POLYMOD_ZIP_INSENSITIVE` Haxe define at compile time, + * Disable this option by settings the `POLYMOD_ZIP_INSENSITIVE` Haxe define at compile time, * or by settings this value in your code. * - * @default `true` + * @default `false` */ public static var caseInsensitiveZipLoading(get, default):Null; static function get_caseInsensitiveZipLoading():Bool { if (caseInsensitiveZipLoading == null) - caseInsensitiveZipLoading = DefineUtil.getDefineBool('POLYMOD_ZIP_INSENSITIVE', true); + caseInsensitiveZipLoading = DefineUtil.getDefineBool('POLYMOD_ZIP_INSENSITIVE', false); return caseInsensitiveZipLoading; } } From 81ad4de7809ee40dc94a34f62d3921febb64f2ab Mon Sep 17 00:00:00 2001 From: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Date: Wed, 9 Apr 2025 22:43:45 -0300 Subject: [PATCH 4/4] Fix documentation --- polymod/PolymodConfig.hx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polymod/PolymodConfig.hx b/polymod/PolymodConfig.hx index 22d16bf2..3591301d 100644 --- a/polymod/PolymodConfig.hx +++ b/polymod/PolymodConfig.hx @@ -234,8 +234,8 @@ class PolymodConfig * If true, ZIP mods' assets will be loaded in a case-insensitive way. * For example, if trying to load an asset called `FOO.txt`, a file called `foo.txt` will be loaded if it exists. * - * Disable this option by settings the `POLYMOD_ZIP_INSENSITIVE` Haxe define at compile time, - * or by settings this value in your code. + * Enable this option by setting the `POLYMOD_ZIP_INSENSITIVE` Haxe define at compile time, + * or by setting this value in your code. * * @default `false` */