/// <summary> /// Create IMG archive from directory /// </summary> /// <param name="sourceDirectoryName">Source directory name</param> /// <param name="destinationArchiveFileName">Destination aechive file name</param> /// <param name="includeBaseDirectory">Include base directory into archive</param> /// <param name="entryNameEncoding">Entry name encoding</param> public static void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, bool includeBaseDirectory, Encoding entryNameEncoding) { try { if ((sourceDirectoryName != null) && (destinationArchiveFileName != null) && (entryNameEncoding != null)) { string source_directory_name = Path.GetFullPath(sourceDirectoryName.TrimEnd('\\', '/')); string destination_archive_file_name = Path.GetFullPath(destinationArchiveFileName); if (Directory.Exists(source_directory_name)) { string[] files = Directory.GetFiles(source_directory_name, "*", SearchOption.AllDirectories); string[] relative_files = new string[files.Length]; bool file_name_lenghts_ok = true; for (int i = 0; i < files.Length; i++) { relative_files[i] = IMGUtils.GetRelativePath(files[i], includeBaseDirectory ? Directory.GetParent(source_directory_name).FullName : source_directory_name).Replace('\\', '/'); if (relative_files[i].Length > 24) { file_name_lenghts_ok = false; break; } } if (file_name_lenghts_ok) { using (FileStream archive_stream = File.Open(destination_archive_file_name, FileMode.Create)) { using (BinaryWriter archive_writer = new BinaryWriter(archive_stream)) { int first_entry_offset = ((((files.Length * 32) % 2048) == 0) ? ((files.Length * 32) / 2048) : (((files.Length * 32) / 2048) + 1)); int current_entry_offset = first_entry_offset; List <KeyValuePair <string, int> > entries = new List <KeyValuePair <string, int> >(); archive_writer.Write(new byte[] { 0x56, 0x45, 0x52, 0x32, (byte)(files.Length & 0xFF), (byte)((files.Length >> 8) & 0xFF), 0x0, 0x0 }); for (int i = 0; i < files.Length; i++) { long file_length = (new FileInfo(files[i])).Length; int entry_length = (int)(((file_length % 2048L) == 0L) ? (file_length / 2048L) : ((file_length / 2048L) + 1L)); byte[] name_bytes_raw = entryNameEncoding.GetBytes(relative_files[i]); byte[] name_bytes = new byte[24]; Array.Copy(name_bytes_raw, name_bytes, Math.Min(name_bytes_raw.Length, name_bytes.Length)); archive_writer.Write(current_entry_offset); archive_writer.Write(new byte[] { (byte)(entry_length & 0xFF), (byte)((entry_length >> 8) & 0xFF), 0x0, 0x0 }); archive_writer.Write(name_bytes); entries.Add(new KeyValuePair <string, int>(files[i], current_entry_offset)); current_entry_offset += entry_length; } foreach (KeyValuePair <string, int> entry in entries) { using (FileStream stream = File.Open(entry.Key, FileMode.Open, FileAccess.Read)) { while ((archive_stream.Length / 2048) < entry.Value) { archive_stream.WriteByte(0); } byte[] data = new byte[stream.Length]; stream.Read(data, 0, (int)(stream.Length)); archive_writer.Write(data); } } while ((archive_stream.Length / 2048) < current_entry_offset) { archive_stream.WriteByte(0); } } } } } } } catch (Exception e) { Console.Error.WriteLine(e); } }
/// <summary> /// Open IMG archive /// </summary> /// <param name="archiveFileName">Archive file name</param> /// <param name="archiveMode">Archive mode</param> /// <param name="entryNameEncoding">Entry name encoding</param> /// <returns>IMG archive</returns> /// <exception cref="InvalidOperationException">Return value is null</exception> /// <exception cref="ArgumentNullException">Archive file name is null</exception> /// <exception cref="FileNotFoundException">Archive file not found</exception> /// <exception cref="InvalidDataException">Archive error</exception> public static IMGArchive Open(string archiveFileName, EIMGArchiveMode archiveMode, Encoding entryNameEncoding) { IMGArchive ret = null; try { Encoding encoding = ((entryNameEncoding == null) ? Encoding.UTF8 : entryNameEncoding); if (archiveFileName != null) { if ((archiveMode == EIMGArchiveMode.Create) || File.Exists(archiveFileName)) { ret = new IMGArchive(File.Open(archiveFileName, (archiveMode == EIMGArchiveMode.Create) ? FileMode.Create : FileMode.Open, (archiveMode != EIMGArchiveMode.Read) ? FileAccess.ReadWrite : FileAccess.Read), archiveMode, entryNameEncoding); if (archiveMode == EIMGArchiveMode.Create) { byte[] header_bytes = new byte[] { 0x56, 0x45, 0x52, 0x32, 0x0, 0x0, 0x0, 0x0 }; ret.Stream.Write(header_bytes, 0, header_bytes.Length); while (ret.Stream.Length < 2048) { ret.Stream.WriteByte(0); } } else { byte[] version_bytes = new byte[4]; if (ret.Stream.Read(version_bytes, 0, version_bytes.Length) == version_bytes.Length) { string version = Encoding.UTF8.GetString(version_bytes); byte[] int_bytes = new byte[4]; if (ret.Stream.Read(int_bytes, 0, int_bytes.Length) == int_bytes.Length) { uint num_entries = int_bytes[0] | (((uint)(int_bytes[1])) << 8) | (((uint)(int_bytes[2])) << 16) | (((uint)(int_bytes[3])) << 24); if ((version == "VER2") && (ret.Stream.Length >= (num_entries * 8))) { for (uint num_entry = 0U; num_entry != num_entries; num_entry++) { if (ret.Stream.Read(int_bytes, 0, int_bytes.Length) == int_bytes.Length) { long offset = (int_bytes[0] | (((long)(int_bytes[1])) << 8) | (((long)(int_bytes[2])) << 16) | (((long)(int_bytes[3])) << 24)) * 2048L; byte[] short_bytes = new byte[2]; if (ret.Stream.Read(short_bytes, 0, short_bytes.Length) == short_bytes.Length) { int length = (short_bytes[0] | (short_bytes[1] << 8)) * 2048; if (ret.Stream.Read(short_bytes, 0, short_bytes.Length) == short_bytes.Length) { byte[] full_name_bytes_raw = new byte[24]; if (ret.Stream.Read(full_name_bytes_raw, 0, full_name_bytes_raw.Length) == full_name_bytes_raw.Length) { int full_name_bytes_count = IMGUtils.GetNullTerminatedByteStringLength(full_name_bytes_raw); if (full_name_bytes_count > 0) { string full_name = encoding.GetString(full_name_bytes_raw, 0, full_name_bytes_count); ret.entries.Add(full_name.ToLower(), new IMGArchiveEntry(ret, offset, length, full_name)); } else { throw new InvalidDataException("IMG entry name can't be empty."); } } else { throw new InvalidDataException("IMG entry name is missing."); } } else { throw new InvalidDataException("IMG entry length is missing."); } } else { throw new InvalidDataException("IMG entry length is missing."); } } else { throw new InvalidDataException("IMG entry offset is missing."); } } } else { throw new InvalidDataException("\"" + archiveFileName + "\" is not an IMG file"); } } else { throw new InvalidDataException("\"" + archiveFileName + "\" is not an IMG file"); } } else { throw new InvalidDataException("\"" + archiveFileName + "\" is not an IMG file"); } } } else { throw new FileNotFoundException("Archive not found", archiveFileName); } } else { throw new ArgumentNullException("archiveFileName"); } if (ret == null) { throw new InvalidOperationException("Return value is null"); } } catch (Exception e) { if (ret != null) { ret.Dispose(); ret = null; } throw e; } return(ret); }