IDictionary <string, string> ReadNameMap(ArcFile file, Arc3Entry entry) { byte[] table = new byte[entry.UnpackedSize]; using (var input = OpenEntry(file, entry)) { input.Read(table, 0, table.Length); } int count = LittleEndian.ToInt32(table, 0); if (!IsSaneCount(count)) { throw new InvalidFormatException("Invalid longinfo map format"); } var map = new Dictionary <string, string> (count); int index_pos = 4; int names_pos = index_pos + count * 8; for (int i = 0; i < count; ++i) { int key_pos = names_pos + LittleEndian.ToInt32(table, index_pos); int value_pos = names_pos + LittleEndian.ToInt32(table, index_pos + 4); index_pos += 8; string key = Binary.GetCString(table, key_pos, table.Length - key_pos); map[key] = Binary.GetCString(table, value_pos, table.Length - value_pos); } return(map); }
public override ArcFile TryOpen(ArcView file) { int version = Binary.BigEndian(file.View.ReadInt32(4)); uint cluster_size = Binary.BigEndian(file.View.ReadUInt32(0x08)); uint base_offset = Binary.BigEndian(file.View.ReadUInt32(0x0C)); uint index_offset = Binary.BigEndian(file.View.ReadUInt32(0x18)); uint index_size = Binary.BigEndian(file.View.ReadUInt32(0x1C)); if (0 == index_size) { return(null); } bool new_name = false; var dir = new List <Entry>(); var name_buffer = new byte[0x10]; long current_offset = (long)index_offset * cluster_size; long index_end = current_offset + index_size; if (index_end > file.MaxOffset) { return(null); } // --- read index --- uint last_entry_offset = 0x7FFFFFFF; int current_name_length = 0; Arc3Entry prev_entry = null; Arc3Entry long_info = null; while (current_offset < index_end) { byte name_length = file.View.ReadByte(current_offset++); int name_offset = name_length >> 4; name_length &= 0xF; if (name_offset != 0xF) { file.View.Read(current_offset, name_buffer, name_offset, name_length); current_offset += name_length; current_name_length = name_offset + name_length; } else if (0xF == name_length) { name_buffer[current_name_length - 1]++; } else if (name_length != 0) { file.View.Read(current_offset, name_buffer, 0, name_length); current_offset += name_length; current_name_length = name_length; new_name = true; } else { uint offset = BigEndian24(file.View.ReadUInt32(current_offset)); offset = (uint)Math.Abs((int)(offset - index_offset)); if (offset < last_entry_offset) { last_entry_offset = offset; } if (prev_entry != null) { prev_entry.Offset = (long)(last_entry_offset + base_offset) * cluster_size; } } last_entry_offset = BigEndian24(file.View.ReadUInt32(current_offset)); current_offset += 3; if (new_name) { current_offset += 3; new_name = false; } string name; if (current_name_length > 3) { name = Encodings.cp932.GetString(name_buffer, 3, current_name_length - 3); string ext = Encodings.cp932.GetString(name_buffer, 0, 3); name = name + '.' + ext; } else { name = Encodings.cp932.GetString(name_buffer, 0, current_name_length); } var entry = new Arc3Entry { Name = name }; entry.Offset = (long)(last_entry_offset + base_offset) * cluster_size; if (entry.Offset >= file.MaxOffset) { return(null); } dir.Add(entry); prev_entry = entry; if (null == long_info && "longinfo.$$$" == name) { long_info = entry; } } // --- read attributes --- foreach (Arc3Entry entry in dir) { entry.Size = Binary.BigEndian(file.View.ReadUInt32(entry.Offset + 8)); entry.Flags = Binary.BigEndian(file.View.ReadUInt32(entry.Offset + 0x14)); entry.UnpackedSize = entry.Size; entry.IsEncrypted = 2 == entry.Flags; entry.Offset += 0x20; uint signature = file.View.ReadUInt32(entry.Offset); if (entry.IsEncrypted) { signature = ~signature; } entry.IsPacked = (signature & 0xFFFF) == 0x7A6C; // 'lz' if (entry.IsPacked) { entry.UnpackedSize = Binary.BigEndian(file.View.ReadUInt32(entry.Offset + 2)); if (entry.IsEncrypted) { entry.UnpackedSize ^= 0xFFFFFFFF; } entry.Offset += 6; entry.Size -= 6; } else { var res = AutoEntry.DetectFileType(signature); if (res != null) { entry.Type = res.Type; } } } var arc = new ArcFile(file, this, dir); try // read long filenames stored within 'longinfo.$$$', if available { if (version > 1 && long_info != null) { var name_map = ReadNameMap(arc, long_info); foreach (var entry in dir) { string orig_name; if (name_map.TryGetValue(entry.Name, out orig_name)) { entry.Name = orig_name; } } } } catch { /* ignore 'longinfo.$$$' read errors */ } foreach (var entry in dir.Where(e => e.Name.Contains('*'))) { entry.Name = entry.Name.Replace('*', '*'); } return(arc); }