/// <summary> /// Create IMG archive from directory /// </summary> /// <param name="sourceDirectoryPath">Source directory path</param> /// <param name="destinationIMGArchiveFilePath">Destination IMG archive file path</param> /// <param name="includeBaseDirectory">Include base directory in IMG archive</param> /// <param name="entryNameEncoding">Entry name encoding</param> public static void CreateFromDirectory(string sourceDirectoryPath, string destinationIMGArchiveFilePath, bool includeBaseDirectory, Encoding entryNameEncoding) { if (sourceDirectoryPath == null) { throw new ArgumentNullException(nameof(sourceDirectoryPath)); } if (destinationIMGArchiveFilePath == null) { throw new ArgumentNullException(nameof(destinationIMGArchiveFilePath)); } if (entryNameEncoding == null) { throw new ArgumentNullException(nameof(entryNameEncoding)); } try { string source_directory_name = Path.GetFullPath(sourceDirectoryPath.TrimEnd('\\', '/')); string destination_archive_file_name = Path.GetFullPath(destinationIMGArchiveFilePath); 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] = IMGUtilities.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 img_archive_file_stream = File.Open(destination_archive_file_name, FileMode.Create)) { using (BinaryWriter img_archive_file_stream_binary_writer = new BinaryWriter(img_archive_file_stream, entryNameEncoding, true)) { 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> >(); img_archive_file_stream_binary_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)); img_archive_file_stream_binary_writer.Write(current_entry_offset); img_archive_file_stream_binary_writer.Write(new byte[] { (byte)(entry_length & 0xFF), (byte)((entry_length >> 8) & 0xFF), 0x0, 0x0 }); img_archive_file_stream_binary_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 ((img_archive_file_stream.Length / 2048) < entry.Value) { img_archive_file_stream.WriteByte(0); } byte[] data = new byte[stream.Length]; stream.Read(data, 0, (int)(stream.Length)); img_archive_file_stream_binary_writer.Write(data); } } while ((img_archive_file_stream.Length / 2048) < current_entry_offset) { img_archive_file_stream.WriteByte(0); } } } } } } catch (Exception e) { Console.Error.WriteLine(e); } }
/// <summary> /// Open IMG archive /// </summary> /// <param name="imgArchiveFilePath">IMG archive file path</param> /// <param name="accessMode">IMG archive access mode</param> /// <param name="entryNameEncoding">IMG archive 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 IIMGArchive Open(string imgArchiveFilePath, EIMGArchiveAccessMode accessMode, Encoding entryNameEncoding) { if (imgArchiveFilePath == null) { throw new ArgumentNullException(nameof(imgArchiveFilePath)); } if (entryNameEncoding == null) { throw new ArgumentNullException(nameof(entryNameEncoding)); } IMGArchive ret = null; try { if ((accessMode != EIMGArchiveAccessMode.Create) && (!File.Exists(imgArchiveFilePath))) { throw new FileNotFoundException("Archive not found", imgArchiveFilePath); } FileStream archive_file_stream = File.Open(imgArchiveFilePath, (accessMode == EIMGArchiveAccessMode.Create) ? FileMode.Create : FileMode.Open, (accessMode != EIMGArchiveAccessMode.Read) ? FileAccess.ReadWrite : FileAccess.Read); if (archive_file_stream == null) { throw new FileNotFoundException("Archive not found", imgArchiveFilePath); } Dictionary <string, IIMGArchiveEntry> img_archive_entries = new Dictionary <string, IIMGArchiveEntry>(); ret = new IMGArchive(archive_file_stream, accessMode, entryNameEncoding, img_archive_entries); if (accessMode == EIMGArchiveAccessMode.Create) { byte[] new_data = new byte[2048]; new_data[0] = 0x56; new_data[1] = 0x45; new_data[2] = 0x52; new_data[3] = 0x32; archive_file_stream.Write(new_data, 0, new_data.Length); } else { using (BinaryReader archive_file_stream_binary_reader = new BinaryReader(archive_file_stream, entryNameEncoding, true)) { int archive_version = archive_file_stream_binary_reader.ReadInt32(); if (archive_version != SupportedIMGArchiveVersion) { throw new InvalidDataException($"Invalid IMG archive version { archive_version }."); } uint num_entries = archive_file_stream_binary_reader.ReadUInt32(); if (archive_file_stream.Length < (num_entries * 8)) { throw new InvalidDataException($"IMG archive file size is smaller than expected."); } for (uint num_entry = 0U; num_entry != num_entries; num_entry++) { long offset = archive_file_stream_binary_reader.ReadInt32() * 2048L; int length = archive_file_stream_binary_reader.ReadInt16() * 2048; short dummy = archive_file_stream_binary_reader.ReadInt16(); byte[] full_name_bytes_raw = new byte[24]; if (archive_file_stream_binary_reader.Read(full_name_bytes_raw, 0, full_name_bytes_raw.Length) != full_name_bytes_raw.Length) { throw new InvalidDataException("IMG entry name is missing."); } int full_name_bytes_count = IMGUtilities.GetNullTerminatedByteStringLength(full_name_bytes_raw); if (full_name_bytes_count <= 0) { throw new InvalidDataException("IMG entry name can't be empty."); } string full_name = entryNameEncoding.GetString(full_name_bytes_raw, 0, full_name_bytes_count); img_archive_entries.Add(full_name.ToLower(), new IMGArchiveEntry(ret, offset, length, full_name)); } } } if (ret == null) { throw new InvalidOperationException("Return value is null"); } } catch (Exception e) { ret?.Dispose(); throw e; } return(ret); }