diff --git a/OpenRA.Mods.Common/FileFormats/InstallShieldCABCompression.cs b/OpenRA.Mods.Common/FileFormats/InstallShieldCABCompression.cs index dc0bd3660a..f3498150c8 100644 --- a/OpenRA.Mods.Common/FileFormats/InstallShieldCABCompression.cs +++ b/OpenRA.Mods.Common/FileFormats/InstallShieldCABCompression.cs @@ -42,10 +42,14 @@ namespace OpenRA.Mods.Common.FileFormats public readonly uint FirstFile; public readonly uint LastFile; - public FileGroup(Stream stream, long offset) + public FileGroup(Stream stream, long offset, uint version) { var nameOffset = stream.ReadUInt32(); stream.Position += 18; + + if (version <= 5) + stream.Position += 54; + FirstFile = stream.ReadUInt32(); LastFile = stream.ReadUInt32(); @@ -105,7 +109,7 @@ namespace OpenRA.Mods.Common.FileFormats public readonly byte[] MD5; public readonly uint NameOffset; - public readonly ushort DirectoryIndex; + public readonly uint DirectoryIndex; public readonly uint LinkToPrevious; public readonly uint LinkToNext; @@ -113,33 +117,63 @@ namespace OpenRA.Mods.Common.FileFormats public readonly ushort Volume; public readonly string Filename; - public FileDescriptor(Stream stream, uint index, long tableOffset) + public FileDescriptor(Stream stream, uint index, long tableOffset, uint version) { Index = index; - Flags = (CABFlags)stream.ReadUInt16(); - ExpandedSize = stream.ReadUInt32(); - stream.Position += 4; - CompressedSize = stream.ReadUInt32(); - stream.Position += 4; - DataOffset = stream.ReadUInt32(); - stream.Position += 4; - MD5 = stream.ReadBytes(16); + if (version <= 5) + { + NameOffset = stream.ReadUInt32(); + DirectoryIndex = stream.ReadUInt32(); + Flags = (CABFlags)stream.ReadUInt16(); + ExpandedSize = stream.ReadUInt32(); + CompressedSize = stream.ReadUInt32(); + stream.Position += 20; + DataOffset = stream.ReadUInt32(); - stream.Position += 16; - NameOffset = stream.ReadUInt32(); - DirectoryIndex = stream.ReadUInt16(); - stream.Position += 12; - LinkToPrevious = stream.ReadUInt32(); - LinkToNext = stream.ReadUInt32(); + MD5 = new byte[16]; + LinkToPrevious = 0; + LinkToNext = 0; + LinkFlags = 0; + Volume = 0; - LinkFlags = (LinkFlags)stream.ReadUInt8(); - Volume = stream.ReadUInt16(); + if ((Flags & CABFlags.FileInvalid) == 0) + { + var pos = stream.Position; + stream.Position = tableOffset + NameOffset; + Filename = stream.ReadASCIIZ(); + stream.Position = pos; + } + else + Filename = ""; + } + else + { + Flags = (CABFlags)stream.ReadUInt16(); + ExpandedSize = stream.ReadUInt32(); + stream.Position += 4; + CompressedSize = stream.ReadUInt32(); - var pos = stream.Position; - stream.Position = tableOffset + NameOffset; - Filename = stream.ReadASCIIZ(); - stream.Position = pos; + stream.Position += 4; + DataOffset = stream.ReadUInt32(); + stream.Position += 4; + MD5 = stream.ReadBytes(16); + + stream.Position += 16; + NameOffset = stream.ReadUInt32(); + DirectoryIndex = stream.ReadUInt16(); + stream.Position += 12; + LinkToPrevious = stream.ReadUInt32(); + LinkToNext = stream.ReadUInt32(); + + LinkFlags = (LinkFlags)stream.ReadUInt8(); + Volume = stream.ReadUInt16(); + + var pos = stream.Position; + stream.Position = tableOffset + NameOffset; + Filename = stream.ReadASCIIZ(); + stream.Position = pos; + } } } @@ -340,6 +374,7 @@ namespace OpenRA.Mods.Common.FileFormats readonly Dictionary index = new Dictionary(); readonly Dictionary volumes; + readonly uint version; public InstallShieldCABCompression(Stream header, Dictionary volumes) { @@ -348,7 +383,20 @@ namespace OpenRA.Mods.Common.FileFormats if (header.ReadUInt32() != 0x28635349) throw new InvalidDataException("Not an Installshield CAB package"); - header.Position += 8; + var versionTmp = header.ReadUInt32(); + + // Logic taken from UnShield + // https://github.com/twogood/unshield/blob/1.5.1/lib/libunshield.c#L277-L288 + if (versionTmp >> 24 == 1) + version = (versionTmp >> 12) & 0xf; + else if (versionTmp >> 24 == 2 || versionTmp >> 24 == 4) + { + version = versionTmp & 0xffff; + if (version != 0) + version /= 100; + } + + header.Position += 4; var cabDescriptorOffset = header.ReadUInt32(); header.Position = cabDescriptorOffset + 12; var cabDescriptor = new CabDescriptor(header); @@ -374,7 +422,7 @@ namespace OpenRA.Mods.Common.FileFormats nextOffset = header.ReadUInt32(); header.Position = cabDescriptorOffset + descriptorOffset; - fileGroups.Add(new FileGroup(header, cabDescriptorOffset)); + fileGroups.Add(new FileGroup(header, cabDescriptorOffset, version)); } } @@ -383,8 +431,15 @@ namespace OpenRA.Mods.Common.FileFormats { for (var i = fileGroup.FirstFile; i <= fileGroup.LastFile; i++) { - header.Position = cabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2 + i * 0x57; - var file = new FileDescriptor(header, i, cabDescriptorOffset + cabDescriptor.FileTableOffset); + if (version <= 5) + { + header.Position = cabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2 + i * 4; + header.Position = cabDescriptorOffset + cabDescriptor.FileTableOffset + header.ReadUInt32(); + } + else + header.Position = cabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2 + i * 0x57; + + var file = new FileDescriptor(header, i, cabDescriptorOffset + cabDescriptor.FileTableOffset, version); var path = $"{fileGroup.Name}\\{directories[file.DirectoryIndex].Name}\\{file.Filename}"; index[path] = file; }