├── .github └── workflows │ ├── Build the App.CI.yml │ └── Release the App.CI.yml ├── .gitignore ├── ALDExplorer.sln ├── ALDExplorer ├── AJPFormat.cs ├── ALDExplorer.csproj ├── AldFile.cs ├── BitStream.cs ├── DisposableCollection.cs ├── Extensions.cs ├── FLATFormat.cs ├── FileFormatSelectionForm.Designer.cs ├── FileFormatSelectionForm.cs ├── FileFormatSelectionForm.resx ├── Form1.Designer.cs ├── Form1.cs ├── Form1.resx ├── HexDump.cs ├── ImageConverter.cs ├── ImportFileTypeForm.Designer.cs ├── ImportFileTypeForm.cs ├── ImportFileTypeForm.resx ├── Interfaces.cs ├── PMSFormat.cs ├── PrefixForm.Designer.cs ├── PrefixForm.cs ├── PrefixForm.resx ├── Program.cs ├── Properties │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── PropertiesForm.Designer.cs ├── PropertiesForm.cs ├── PropertiesForm.resx ├── QNTFormat.cs ├── Resources │ ├── alicefile.png │ ├── alicefile2.png │ ├── folder.png │ ├── icon.ico │ ├── minusicon.png │ ├── music.png │ ├── plusicon.png │ └── script.png ├── TempFileManager.cs ├── Utility.cs ├── VSPFormat.cs ├── XCFFormat.cs └── lib │ ├── zlib32.dll │ └── zlibnet.dll ├── LICENSE └── README.md /.github/workflows/Build the App.CI.yml: -------------------------------------------------------------------------------- 1 | name: Build the App 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | tags-ignore: 8 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 9 | 10 | jobs: 11 | build: 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | name: Checkout Code 17 | 18 | - name: Setup MSBuild 19 | uses: microsoft/setup-msbuild@v1 20 | 21 | - name: Install dependencies 22 | run: dotnet restore 23 | 24 | - name: Build 25 | run: msbuild ALDExplorer.sln /p:Configuration=Release 26 | 27 | - name: Upload artifact 28 | uses: actions/upload-artifact@v2 29 | with: 30 | name: ALDExplorer.zip 31 | path: ALDExplorer/bin/x86/Release/net40/* 32 | -------------------------------------------------------------------------------- /.github/workflows/Release the App.CI.yml: -------------------------------------------------------------------------------- 1 | name: Release the App 2 | 3 | on: 4 | push: 5 | # Sequence of patterns matched against refs/tags 6 | tags: 7 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | name: Checkout Code 16 | 17 | - name: Setup MSBuild 18 | uses: microsoft/setup-msbuild@v1 19 | 20 | - name: Install dependencies 21 | run: dotnet restore 22 | 23 | - name: Build 24 | run: msbuild ALDExplorer.sln /p:Configuration=Release 25 | 26 | - name: Get the versioned name 27 | id: get_version 28 | run: echo ::set-output name=VERSION::ALDExplorer_${GITHUB_REF/refs\/tags\//}.zip 29 | shell: bash 30 | 31 | - name: Pack release 32 | run: Compress-Archive -Path ALDExplorer/bin/x86/Release/net40/* -DestinationPath ${{ steps.get_version.outputs.VERSION }} -CompressionLevel Optimal 33 | 34 | - name: Upload binaries to release 35 | uses: svenstaro/upload-release-action@v2 36 | with: 37 | repo_token: ${{ secrets.GITHUB_TOKEN }} 38 | tag: ${{ github.ref }} 39 | file: ${{ steps.get_version.outputs.VERSION }} 40 | overwrite: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Coverlet is a free, cross platform Code Coverage Tool 145 | coverage*.json 146 | coverage*.xml 147 | coverage*.info 148 | 149 | # Visual Studio code coverage results 150 | *.coverage 151 | *.coveragexml 152 | 153 | # Installshield output folder 154 | [Ee]xpress/ 155 | 156 | # DocProject is a documentation generator add-in 157 | DocProject/buildhelp/ 158 | DocProject/Help/*.HxT 159 | DocProject/Help/*.HxC 160 | DocProject/Help/*.hhc 161 | DocProject/Help/*.hhk 162 | DocProject/Help/*.hhp 163 | DocProject/Help/Html2 164 | DocProject/Help/html 165 | 166 | # Click-Once directory 167 | publish/ 168 | 169 | # Publish Web Output 170 | *.[Pp]ublish.xml 171 | *.azurePubxml 172 | # Note: Comment the next line if you want to checkin your web deploy settings, 173 | # but database connection strings (with potential passwords) will be unencrypted 174 | *.pubxml 175 | *.publishproj 176 | 177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 178 | # checkin your Azure Web App publish settings, but sensitive information contained 179 | # in these scripts will be unencrypted 180 | PublishScripts/ 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # NuGet Symbol Packages 185 | *.snupkg 186 | # The packages folder can be ignored because of Package Restore 187 | **/[Pp]ackages/* 188 | # except build/, which is used as an MSBuild target. 189 | !**/[Pp]ackages/build/ 190 | # Uncomment if necessary however generally it will be regenerated when needed 191 | #!**/[Pp]ackages/repositories.config 192 | # NuGet v3's project.json files produces more ignorable files 193 | *.nuget.props 194 | *.nuget.targets 195 | 196 | # Microsoft Azure Build Output 197 | csx/ 198 | *.build.csdef 199 | 200 | # Microsoft Azure Emulator 201 | ecf/ 202 | rcf/ 203 | 204 | # Windows Store app package directories and files 205 | AppPackages/ 206 | BundleArtifacts/ 207 | Package.StoreAssociation.xml 208 | _pkginfo.txt 209 | *.appx 210 | *.appxbundle 211 | *.appxupload 212 | 213 | # Visual Studio cache files 214 | # files ending in .cache can be ignored 215 | *.[Cc]ache 216 | # but keep track of directories ending in .cache 217 | !?*.[Cc]ache/ 218 | 219 | # Others 220 | ClientBin/ 221 | ~$* 222 | *~ 223 | *.dbmdl 224 | *.dbproj.schemaview 225 | *.jfm 226 | *.pfx 227 | *.publishsettings 228 | 229 | # Backup & report files from converting an old project file 230 | # to a newer Visual Studio version. Backup files are not needed, 231 | # because we have git ;-) 232 | _UpgradeReport_Files/ 233 | Backup*/ 234 | UpgradeLog*.XML 235 | UpgradeLog*.htm 236 | ServiceFabricBackup/ 237 | *.rptproj.bak 238 | 239 | # SQL Server files 240 | *.mdf 241 | *.ldf 242 | *.ndf 243 | 244 | # Microsoft Fakes 245 | FakesAssemblies/ 246 | 247 | # Visual Studio 6 build log 248 | *.plg 249 | 250 | # Visual Studio 6 workspace options file 251 | *.opt 252 | 253 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 254 | *.vbw 255 | 256 | # Python Tools for Visual Studio (PTVS) 257 | __pycache__/ 258 | *.pyc 259 | 260 | # Local History for Visual Studio 261 | .localhistory/ 262 | *.cmd 263 | *.lnk 264 | -------------------------------------------------------------------------------- /ALDExplorer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ALDExplorer", "ALDExplorer\ALDExplorer.csproj", "{87AA2383-2CA1-427F-A501-50EED3828B30}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x86 = Debug|x86 11 | Release|x86 = Release|x86 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {87AA2383-2CA1-427F-A501-50EED3828B30}.Debug|x86.ActiveCfg = Debug|Any CPU 15 | {87AA2383-2CA1-427F-A501-50EED3828B30}.Debug|x86.Build.0 = Debug|Any CPU 16 | {87AA2383-2CA1-427F-A501-50EED3828B30}.Release|x86.ActiveCfg = Release|x86 17 | {87AA2383-2CA1-427F-A501-50EED3828B30}.Release|x86.Build.0 = Release|x86 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {F8DE08A7-24D2-4230-986A-BABD430F963C} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /ALDExplorer/AJPFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using FreeImageAPI; 5 | using ZLibNet; 6 | using System.Globalization; 7 | 8 | namespace ALDExplorer.Formats 9 | { 10 | public class AjpHeader : ICloneable 11 | { 12 | public int signature; 13 | public int version; 14 | public int headerSize1; //0x38 15 | public int width; 16 | public int height; 17 | public int imageLocation; //0x38 18 | public int jpegDataLength; 19 | public int alphaLocation; 20 | public int alphaDataLength; 21 | public int alphaUnpacked; 22 | public byte[] unknown1; 23 | public int unknown2; 24 | public byte[] unknown3; 25 | //public byte[] jpegFooter; 26 | public static int BPP = 32; 27 | 28 | public bool HasAlpha 29 | { 30 | get { return alphaLocation != 0 && alphaDataLength != 0; } 31 | } 32 | 33 | 34 | public string GetComment() 35 | { 36 | return ImageConverter.GetComment(this); 37 | } 38 | 39 | public bool ParseComment(string comment) 40 | { 41 | return ImageConverter.ParseComment(this, comment); 42 | } 43 | 44 | public bool Validate() 45 | { 46 | if (signature != 0x00504a41) // AJP 47 | { 48 | return false; 49 | } 50 | if (width < 0 || height < 0 || width > 65535 || height > 65535 || width * height > 64 * 1024 * 1024) 51 | { 52 | return false; 53 | } 54 | if (headerSize1 < 0 || headerSize1 > 1024 * 1024) 55 | { 56 | return false; 57 | } 58 | if (imageLocation < 0 || imageLocation > 1024 * 1024) 59 | { 60 | return false; 61 | } 62 | if (alphaDataLength < 0 || alphaDataLength > 1024 * 1024 * 64) 63 | { 64 | return false; 65 | } 66 | return true; 67 | } 68 | 69 | //header before jpeg: 1F DB C2 B6 03 7B C6 01 E1 FF 1A A7 FB 70 | //common header before jpeg? 7A C6 01 00 00 00 00 A2 49 51 67 4A 46 0B 8B CA AA 4C 93 B7 CA 16 7C 71 | //footer after jpeg: 0D DC AC 87 0A 56 49 CD 83 EC 4C 92 B5 CB 16 34 72 | 73 | public AjpHeader Clone() 74 | { 75 | var clone = (AjpHeader)this.MemberwiseClone(); 76 | if (clone.unknown1 != null) clone.unknown1 = (byte[])(clone.unknown1.Clone()); 77 | if (clone.unknown3 != null) clone.unknown3 = (byte[])(clone.unknown3.Clone()); 78 | //if (clone.jpegFooter != null) clone.jpegFooter = (byte[])(clone.jpegFooter.Clone()); 79 | return clone; 80 | } 81 | 82 | #region ICloneable Members 83 | 84 | object ICloneable.Clone() 85 | { 86 | return Clone(); 87 | } 88 | 89 | #endregion 90 | } 91 | 92 | static class Ajp 93 | { 94 | internal static byte[] ajpKey = GetBytes("5D91AE874A5641CD83EC4C92B5CB1634"); 95 | 96 | public static byte[] GetBytes(string token) 97 | { 98 | if ((token.Length & 1) == 1) 99 | { 100 | return null; 101 | } 102 | List bytes = new List(token.Length / 2); 103 | for (int i = 0; i < token.Length; i += 2) 104 | { 105 | string hexPair = token.Substring(i, 2); 106 | byte b; 107 | if (byte.TryParse(hexPair, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out b)) 108 | bytes.Add(b); 109 | else 110 | return null; 111 | } 112 | return bytes.ToArray(); 113 | } 114 | 115 | public static FreeImageBitmap LoadImage(byte[] bytes) 116 | { 117 | var ms = new MemoryStream(bytes); 118 | return LoadImage(ms); 119 | } 120 | 121 | public static void LoadImage(byte[] bytes, out byte[] jpegBytes, out FreeImageBitmap alpha, out AjpHeader ajpHeader) 122 | { 123 | var ms = new MemoryStream(bytes); 124 | LoadImage(ms, out jpegBytes, out alpha, out ajpHeader); 125 | } 126 | 127 | public static void LoadImage(Stream ajpStream, out byte[] jpegBytes, out FreeImageBitmap alpha, out AjpHeader ajpHeader) 128 | { 129 | MemoryStream jpegFile; 130 | MemoryStream pmsFile; 131 | 132 | LoadImage(ajpStream, out jpegFile, out pmsFile, out ajpHeader); 133 | jpegBytes = jpegFile.ToArray(); 134 | jpegFile.Dispose(); 135 | 136 | alpha = null; 137 | if (pmsFile != null) 138 | { 139 | FreeImageBitmap pmsImage = Pms.LoadImage(pmsFile.ToArray()); 140 | pmsFile.Dispose(); 141 | alpha = pmsImage; 142 | } 143 | } 144 | 145 | public static void ScrambleStream (Stream input, int size, long offset = 0) 146 | { 147 | var pos = input.Position; 148 | var header = new byte[ajpKey.Length]; 149 | input.Position = offset; 150 | 151 | input.Read (header, (int)offset, header.Length); 152 | //HexDump.HexView.Debugger(header); 153 | 154 | for (int i = 0; i < header.Length && i < size; ++i) 155 | header[i] ^= ajpKey[i]; 156 | 157 | input.Position = offset; 158 | //HexDump.HexView.Debugger(header); 159 | 160 | input.Write(header, (int)offset, header.Length); 161 | input.Position = pos; 162 | } 163 | 164 | public static FreeImageBitmap LoadImage(Stream ajpStream) 165 | { 166 | MemoryStream jpegFile; 167 | MemoryStream pmsFile; 168 | AjpHeader ajpHeader; 169 | 170 | LoadImage(ajpStream, out jpegFile, out pmsFile, out ajpHeader); 171 | if (jpegFile == null) 172 | return null; 173 | 174 | jpegFile.Position = 0; 175 | FreeImageBitmap jpegImage = new FreeImageBitmap(jpegFile, FREE_IMAGE_FORMAT.FIF_JPEG); 176 | jpegImage.Tag = ajpHeader; 177 | jpegImage.Comment = ajpHeader.GetComment(); 178 | 179 | if (pmsFile != null && pmsFile.Length > 0) 180 | { 181 | pmsFile.Position = 0; 182 | using (var pmsImage = Pms.LoadImage(pmsFile.ToArray())) 183 | { 184 | if (pmsImage == null) 185 | return jpegImage; 186 | jpegImage.ConvertColorDepth(FREE_IMAGE_COLOR_DEPTH.FICD_32_BPP); 187 | jpegImage.SetChannel(pmsImage, FREE_IMAGE_COLOR_CHANNEL.FICC_ALPHA); 188 | } 189 | 190 | } 191 | 192 | return jpegImage; 193 | } 194 | 195 | private static void LoadImage(Stream ajpStream, out MemoryStream jpegFile, out MemoryStream pmsFile, out AjpHeader ajpHeader) 196 | { 197 | jpegFile = null; 198 | pmsFile = null; 199 | 200 | var brAjp = new BinaryReader(ajpStream); 201 | ajpHeader = ReadAjpHeader(brAjp); 202 | if (ajpHeader == null) 203 | return; 204 | 205 | jpegFile = new MemoryStream(ajpHeader.jpegDataLength); 206 | ajpStream.Position = ajpHeader.imageLocation; 207 | jpegFile.WriteFromStream(ajpStream, ajpHeader.jpegDataLength); 208 | ScrambleStream(jpegFile, ajpHeader.jpegDataLength); 209 | 210 | var br1 = new BinaryReader(jpegFile); 211 | var b = br1.ReadBytes((int)jpegFile.Length); 212 | HexDump.HexView.Debugger(b.Slice(0, 30)); 213 | 214 | int pmsSize = ajpHeader.alphaDataLength; 215 | if (pmsSize <= 0) 216 | return; 217 | 218 | ajpStream.Position = ajpHeader.alphaLocation; 219 | if (ajpHeader.alphaUnpacked != 0) 220 | { 221 | pmsFile = new MemoryStream(ajpHeader.alphaUnpacked); 222 | using (var packed = new MemoryStream(ajpHeader.alphaUnpacked)) 223 | { 224 | packed.WriteFromStream(ajpStream, pmsSize); 225 | packed.Position = 0; 226 | ScrambleStream(packed, pmsSize); 227 | using (var mask = new ZLibStream(packed, CompressionMode.Decompress)) 228 | using (var br = new BinaryReader(mask)) 229 | { 230 | var bytes = br.ReadBytes(ajpHeader.alphaUnpacked); 231 | pmsFile.Write(bytes, 0, bytes.Length); 232 | } 233 | } 234 | } else 235 | { 236 | pmsFile = new MemoryStream(pmsSize); 237 | pmsFile.WriteFromStream(ajpStream, pmsSize); 238 | ScrambleStream(pmsFile, ajpHeader.alphaDataLength); 239 | } 240 | 241 | pmsFile.Position = 0; 242 | return; 243 | } 244 | 245 | static bool GetJpegSize(byte[] data, out int width, out int height) 246 | { 247 | width = -1; 248 | height = -1; 249 | int dataSize = data.Length; 250 | //Check for valid JPEG image 251 | int i = 0; // Keeps track of the position within the file 252 | if (data[i] == 0xFF && data[i + 1] == 0xD8 && data[i + 2] == 0xFF && data[i + 3] == 0xE0) 253 | { 254 | i += 4; 255 | // Check for valid JPEG header (null terminated JFIF) 256 | if (data[i + 2] == 'J' && data[i + 3] == 'F' && data[i + 4] == 'I' && data[i + 5] == 'F' && data[i + 6] == 0x00) 257 | { 258 | //Retrieve the block length of the first block since the first block will not contain the size of file 259 | int blockLength = data[i] * 256 + data[i + 1]; 260 | while (i < dataSize) 261 | { 262 | i += blockLength; //Increase the file index to get to the next block 263 | if (i >= dataSize) return false; //Check to protect against segmentation faults 264 | if (data[i] != 0xFF) return false; //Check that we are truly at the start of another block 265 | if (data[i + 1] == 0xC0) 266 | { //0xFFC0 is the "Start of frame" marker which contains the file size 267 | //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y] 268 | height = data[i + 5] * 256 + data[i + 6]; 269 | width = data[i + 7] * 256 + data[i + 8]; 270 | return true; 271 | } 272 | else 273 | { 274 | i += 2; //Skip the block marker 275 | blockLength = data[i] * 256 + data[i + 1]; //Go to the next block 276 | } 277 | } 278 | return false; //If this point is reached then no size was found 279 | } 280 | else 281 | { 282 | //Not a valid JFIF string 283 | return false; 284 | } 285 | } 286 | else 287 | { 288 | //Not a valid SOI header 289 | return false; 290 | } 291 | } 292 | 293 | public static void SaveImage(Stream stream, byte[] jpegFile, FreeImageBitmap alpha, AjpHeader ajpHeader) 294 | { 295 | byte[] pmsFile = null; 296 | if (alpha != null) 297 | { 298 | using( var ms = new MemoryStream()) { 299 | alpha.Comment = "signature = 19792, version = 2, headerSize = 64"; 300 | Pms.SaveImage(ms, alpha); 301 | pmsFile = ms.ToArray(); 302 | } 303 | } 304 | 305 | SaveImage(stream, jpegFile, pmsFile, ajpHeader); 306 | } 307 | 308 | private static void SaveImage(Stream deststream, byte[] jpegFile, byte[] pmsFile, AjpHeader ajpHeader) 309 | { 310 | if (ajpHeader == null) 311 | { 312 | ajpHeader = new AjpHeader(); 313 | } 314 | if (ajpHeader.signature == 0) 315 | { 316 | ajpHeader.signature = 0x00504a41; 317 | } 318 | if (ajpHeader.headerSize1 == 0) 319 | { 320 | ajpHeader.headerSize1 = 0x38; 321 | } 322 | if (ajpHeader.imageLocation == 0) 323 | { 324 | ajpHeader.imageLocation = 0x38; 325 | } 326 | if (ajpHeader.alphaUnpacked == 0) 327 | { 328 | //ajpHeader.alphaUnpacked = 0; 329 | } 330 | if (ajpHeader.unknown1 == null || ajpHeader.unknown1.Length == 0) 331 | { 332 | if (ajpHeader.version > 0) 333 | ajpHeader.unknown1 = GetBytes("037BC601E1FF1AA7FB7AC601"); 334 | else 335 | ajpHeader.unknown1 = GetBytes("1FDBC2B6037BC601E1FF1AA7FB7AC601"); 336 | } 337 | if (ajpHeader.unknown3 == null || ajpHeader.unknown3.Length == 0) 338 | { 339 | ajpHeader.unknown3 = GetBytes("A24951674A460B8BCAAA4C93B7CA167C"); 340 | } 341 | 342 | if (ajpHeader.width == 0 && ajpHeader.height == 0) 343 | { 344 | int width, height; 345 | if (GetJpegSize(jpegFile, out width, out height)) 346 | { 347 | ajpHeader.width = width; 348 | ajpHeader.height = height; 349 | } 350 | else 351 | { 352 | var ms = new MemoryStream(jpegFile); 353 | using (FreeImageBitmap jpegImage = new FreeImageBitmap(ms, FREE_IMAGE_FORMAT.FIF_JPEG)) 354 | { 355 | ajpHeader.width = jpegImage.Width; 356 | ajpHeader.height = jpegImage.Height; 357 | } 358 | } 359 | } 360 | 361 | ajpHeader.jpegDataLength = jpegFile.Length; 362 | 363 | bool hasAlpha = pmsFile != null && pmsFile.Length > 0; 364 | ZLibStream pmsFileCompressed = null; 365 | 366 | if (!hasAlpha) 367 | { 368 | ajpHeader.alphaLocation = 0; 369 | ajpHeader.alphaDataLength = 0; 370 | } 371 | else 372 | { 373 | using (var pmsStream = new MemoryStream(pmsFile)) { 374 | pmsFileCompressed = new ZLibStream(pmsStream, CompressionMode.Compress); 375 | if (ajpHeader.alphaUnpacked == 0) 376 | ajpHeader.alphaDataLength = pmsFile.Length; 377 | else 378 | ajpHeader.alphaDataLength = (int)pmsFileCompressed.Length; 379 | } 380 | ajpHeader.alphaLocation = ajpHeader.jpegDataLength + ajpHeader.headerSize1; 381 | } 382 | 383 | WriteAjpHeader(deststream, ajpHeader); 384 | var ajpPos = deststream.Position; 385 | using (var bw = new BinaryWriter(deststream)) 386 | { 387 | bw.Write(jpegFile); 388 | 389 | 390 | var pmsPos = deststream.Position; 391 | if (pmsFile != null && pmsFile.Length > 16) 392 | { 393 | if (ajpHeader.alphaUnpacked == 0) 394 | { 395 | bw.Write(pmsFile); 396 | } 397 | else 398 | { 399 | using (var br = new BinaryReader(pmsFileCompressed)) 400 | pmsFile = br.ReadBytes((int)pmsFileCompressed.Length); 401 | pmsFileCompressed.Dispose(); 402 | } 403 | } 404 | ScrambleStream(deststream, jpegFile.Length, ajpPos); 405 | ScrambleStream(deststream, pmsFile.Length, pmsPos); 406 | } 407 | } 408 | 409 | public static void SaveImage(Stream stream, FreeImageBitmap bitmap) 410 | { 411 | AjpHeader ajpHeaderFromBitmap = bitmap.Tag as AjpHeader; 412 | AjpHeader ajpHeaderFromComment = null; 413 | if (!string.IsNullOrEmpty(bitmap.Comment)) 414 | { 415 | ajpHeaderFromComment = new AjpHeader(); 416 | if (!ajpHeaderFromComment.ParseComment(bitmap.Comment)) 417 | { 418 | ajpHeaderFromComment = null; 419 | } 420 | } 421 | 422 | var ms = new MemoryStream(); 423 | var ms2 = new MemoryStream(); 424 | bitmap.Save(ms, FREE_IMAGE_FORMAT.FIF_JPEG, FREE_IMAGE_SAVE_FLAGS.JPEG_PROGRESSIVE | FREE_IMAGE_SAVE_FLAGS.JPEG_QUALITYGOOD); 425 | using (var alpha = bitmap.GetChannel(FREE_IMAGE_COLOR_CHANNEL.FICC_ALPHA)) 426 | { 427 | if (alpha != null) 428 | { 429 | alpha.Comment = "signature = 19792, version = 2, headerSize = 64, colorDepth = 8"; 430 | Pms.SaveImage(ms2, alpha); 431 | } 432 | } 433 | 434 | AjpHeader ajpHeader = ajpHeaderFromBitmap; 435 | if (ajpHeader == null) ajpHeader = ajpHeaderFromComment; 436 | SaveImage(stream, ms.ToArray(), ms2.ToArray(), ajpHeader); 437 | 438 | } 439 | 440 | private static void WriteAjpHeader(Stream stream, AjpHeader ajp) 441 | { 442 | var bw = new BinaryWriter(stream); 443 | bw.Write(ajp.signature); 444 | bw.Write(ajp.version); 445 | bw.Write(ajp.headerSize1); 446 | bw.Write(ajp.width); 447 | bw.Write(ajp.height); 448 | bw.Write(ajp.imageLocation); 449 | bw.Write(ajp.jpegDataLength); 450 | bw.Write(ajp.alphaLocation); 451 | bw.Write(ajp.alphaDataLength); 452 | if (ajp.version > 0) 453 | bw.Write(ajp.alphaUnpacked); 454 | bw.Write(ajp.unknown1); 455 | bw.Write(ajp.unknown2); 456 | bw.Write(ajp.unknown3); 457 | } 458 | 459 | public static AjpHeader ReadAjpHeader(BinaryReader br) 460 | { 461 | var ajp = new AjpHeader(); 462 | try 463 | { 464 | ajp.signature = br.ReadInt32(); 465 | ajp.version = br.ReadInt32(); 466 | if (ajp.version < 0) 467 | return null; 468 | ajp.headerSize1 = br.ReadInt32(); 469 | ajp.width = br.ReadInt32(); 470 | ajp.height = br.ReadInt32(); 471 | ajp.imageLocation = br.ReadInt32(); 472 | ajp.jpegDataLength = br.ReadInt32(); 473 | ajp.alphaLocation = br.ReadInt32(); 474 | ajp.alphaDataLength = br.ReadInt32(); 475 | 476 | if (ajp.version > 0) 477 | { 478 | 479 | ajp.alphaUnpacked = br.ReadInt32(); 480 | ajp.unknown1 = br.ReadBytes(12); 481 | } 482 | else 483 | { 484 | ajp.unknown1 = br.ReadBytes(16); 485 | } 486 | ajp.unknown2 = br.ReadInt32(); 487 | ajp.unknown3 = br.ReadBytes(16); 488 | } 489 | catch (IOException) 490 | { 491 | return null; 492 | } 493 | 494 | return ajp; 495 | } 496 | } 497 | 498 | } -------------------------------------------------------------------------------- /ALDExplorer/ALDExplorer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Debug 4 | AnyCPU 5 | 9.0.30729 6 | 2.0 7 | {87AA2383-2CA1-427F-A501-50EED3828B30} 8 | WinExe 9 | Properties 10 | ALDExplorer 11 | ALDExplorer 12 | net40 13 | 512 14 | Resources\icon.ico 15 | 3.0.1 16 | 17 | 18 | true 19 | bin\x86\Debug\ 20 | DEBUG;TRACE 21 | full 22 | x86 23 | prompt 24 | true 25 | 26 | 27 | bin\x86\Release\ 28 | TRACE 29 | true 30 | pdbonly 31 | x86 32 | prompt 33 | true 34 | 35 | 36 | true 37 | 38 | 39 | 40 | 41 | 4.0 42 | 43 | 44 | 4.0 45 | 46 | 47 | 4.0 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | False 56 | lib\zlibnet.dll 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | all 65 | runtime; build; native; contentfiles; analyzers 66 | 67 | 68 | 69 | 70 | {6BF52A50-394A-11D3-B153-00C04F79FAA6} 71 | 1 72 | 0 73 | 0 74 | aximp 75 | False 76 | 77 | 78 | {6BF52A50-394A-11D3-B153-00C04F79FAA6} 79 | 1 80 | 0 81 | 0 82 | tlbimp 83 | False 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | True 93 | True 94 | Resources.resx 95 | 96 | 97 | True 98 | True 99 | Settings.settings 100 | 101 | 102 | 103 | 104 | SettingsSingleFileGenerator 105 | Settings.Designer.cs 106 | 107 | 108 | 109 | 110 | PreserveNewest 111 | zlib32.dll 112 | 113 | 114 | 115 | 116 | ResXFileCodeGenerator 117 | Resources.Designer.cs 118 | 119 | 120 | -------------------------------------------------------------------------------- /ALDExplorer/BitStream.cs: -------------------------------------------------------------------------------- 1 | //! \file BitStream.cs 2 | //! \date Sat Aug 22 21:33:39 2015 3 | //! \brief Bit stream on top of the IO.Stream 4 | // 5 | // Copyright (C) 2015 by morkt 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to 9 | // deal in the Software without restriction, including without limitation the 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | // sell copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | // IN THE SOFTWARE. 24 | // 25 | 26 | using System; 27 | using System.Diagnostics; 28 | using System.IO; 29 | 30 | namespace ALDExplorer 31 | { 32 | public class BitStream : IDisposable 33 | { 34 | protected Stream m_input; 35 | private bool m_should_dispose; 36 | 37 | protected int m_bits = 0; 38 | protected int m_cached_bits = 0; 39 | 40 | public Stream Input { get { return m_input; } } 41 | public int CacheSize { get { return m_cached_bits; } } 42 | 43 | protected BitStream (Stream file, bool leave_open) 44 | { 45 | m_input = file; 46 | m_should_dispose = !leave_open; 47 | } 48 | 49 | public void Reset () 50 | { 51 | m_cached_bits = 0; 52 | } 53 | 54 | #region IDisposable Members 55 | bool m_disposed = false; 56 | 57 | public void Dispose () 58 | { 59 | Dispose (true); 60 | GC.SuppressFinalize (this); 61 | } 62 | 63 | protected virtual void Dispose (bool disposing) 64 | { 65 | if (!m_disposed) 66 | { 67 | if (disposing && m_should_dispose && null != m_input) 68 | m_input.Dispose(); 69 | m_disposed = true; 70 | } 71 | } 72 | #endregion 73 | } 74 | 75 | public interface IBitStream 76 | { 77 | int GetBits (int count); 78 | int GetNextBit (); 79 | void Reset (); 80 | } 81 | 82 | public class MsbBitStream : BitStream, IBitStream 83 | { 84 | public MsbBitStream (Stream file, bool leave_open = false) 85 | : base (file, leave_open) 86 | { 87 | } 88 | 89 | public int GetBits (int count) 90 | { 91 | Debug.Assert (count <= 24, "MsbBitStream does not support sequences longer than 24 bits"); 92 | while (m_cached_bits < count) 93 | { 94 | int b = m_input.ReadByte(); 95 | if (-1 == b) 96 | return -1; 97 | m_bits = (m_bits << 8) | b; 98 | m_cached_bits += 8; 99 | } 100 | int mask = (1 << count) - 1; 101 | m_cached_bits -= count; 102 | return (m_bits >> m_cached_bits) & mask; 103 | } 104 | 105 | public int GetNextBit () 106 | { 107 | return GetBits (1); 108 | } 109 | } 110 | 111 | public class LsbBitStream : BitStream, IBitStream 112 | { 113 | public LsbBitStream (Stream file, bool leave_open = false) 114 | : base (file, leave_open) 115 | { 116 | } 117 | 118 | public int GetBits (int count) 119 | { 120 | Debug.Assert (count <= 32, "LsbBitStream does not support sequences longer than 32 bits"); 121 | int value; 122 | if (m_cached_bits >= count) 123 | { 124 | int mask = (1 << count) - 1; 125 | value = m_bits & mask; 126 | m_bits = (int)((uint)m_bits >> count); 127 | m_cached_bits -= count; 128 | } 129 | else 130 | { 131 | value = m_bits & ((1 << m_cached_bits) - 1); 132 | count -= m_cached_bits; 133 | int shift = m_cached_bits; 134 | m_cached_bits = 0; 135 | while (count >= 8) 136 | { 137 | int b = m_input.ReadByte(); 138 | if (-1 == b) 139 | return -1; 140 | value |= b << shift; 141 | shift += 8; 142 | count -= 8; 143 | } 144 | if (count > 0) 145 | { 146 | int b = m_input.ReadByte(); 147 | if (-1 == b) 148 | return -1; 149 | value |= (b & ((1 << count) - 1)) << shift; 150 | m_bits = b >> count; 151 | m_cached_bits = 8 - count; 152 | } 153 | } 154 | return value; 155 | } 156 | 157 | public int GetNextBit () 158 | { 159 | return GetBits (1); 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /ALDExplorer/DisposableCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | using System.Collections.ObjectModel; 7 | 8 | namespace ALDExplorer 9 | { 10 | class DisposableCollection : Collection, IDisposable where T : class, IDisposable 11 | { 12 | public DisposableCollection() 13 | : base() 14 | { 15 | 16 | } 17 | 18 | public DisposableCollection(IEnumerable sequence) 19 | : this() 20 | { 21 | AddRange(sequence); 22 | } 23 | 24 | public void AddRange(IEnumerable sequence) 25 | { 26 | var list = this.Items as List; 27 | if (list != null) 28 | { 29 | list.AddRange(sequence); 30 | } 31 | else 32 | { 33 | foreach (var item in sequence) 34 | { 35 | Add(item); 36 | } 37 | } 38 | } 39 | 40 | public void Dispose() 41 | { 42 | foreach (var item in this) 43 | { 44 | if (item != null) 45 | { 46 | item.Dispose(); 47 | } 48 | } 49 | } 50 | 51 | protected override void ClearItems() 52 | { 53 | foreach (var item in this) 54 | { 55 | if (item != null) 56 | { 57 | item.Dispose(); 58 | } 59 | } 60 | base.ClearItems(); 61 | } 62 | 63 | protected override void RemoveItem(int index) 64 | { 65 | var oldItem = this[index]; 66 | if (oldItem != null) 67 | { 68 | oldItem.Dispose(); 69 | } 70 | base.RemoveItem(index); 71 | } 72 | 73 | protected override void SetItem(int index, T item) 74 | { 75 | var oldItem = this[index]; 76 | if (oldItem != null) 77 | { 78 | oldItem.Dispose(); 79 | } 80 | base.SetItem(index, item); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ALDExplorer/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace ALDExplorer 8 | { 9 | public static partial class Extensions 10 | { 11 | public static void WriteToStream(this Stream streamToReadFrom, Stream streamToWriteTo) 12 | { 13 | WriteToStream(streamToReadFrom, streamToWriteTo, streamToReadFrom.Length - streamToReadFrom.Position); 14 | } 15 | 16 | public static void WriteToStream(this Stream streamToReadFrom, Stream streamToWriteTo, long byteCount) 17 | { 18 | int bufferSize = 65536; 19 | byte[] buffer = new byte[bufferSize]; 20 | 21 | long bytesRemaining = byteCount; 22 | while (bytesRemaining > 0) 23 | { 24 | int bytesToRead = bufferSize; 25 | if (bytesToRead > bytesRemaining) 26 | { 27 | bytesToRead = (int)bytesRemaining; 28 | } 29 | int bytesRead = streamToReadFrom.Read(buffer, 0, bytesToRead); 30 | if (bytesRead != bytesToRead) 31 | { 32 | //??? 33 | if (bytesRead == 0) 34 | { 35 | throw new IOException("Failed to read from the stream"); 36 | } 37 | } 38 | streamToWriteTo.Write(buffer, 0, bytesRead); 39 | bytesRemaining -= bytesRead; 40 | } 41 | 42 | } 43 | 44 | public static void WriteFromStream(this Stream streamToWriteTo, Stream streamToReadFrom) 45 | { 46 | WriteToStream(streamToReadFrom, streamToWriteTo, streamToReadFrom.Length - streamToReadFrom.Position); 47 | } 48 | 49 | public static void WriteFromStream(this Stream streamToWriteTo, Stream streamToReadFrom, long byteCount) 50 | { 51 | WriteToStream(streamToReadFrom, streamToWriteTo, byteCount); 52 | } 53 | 54 | public static int PeekByte(this Stream stream) 55 | { 56 | long position = stream.Position; 57 | int b = stream.ReadByte(); 58 | stream.Position = position; 59 | return b; 60 | } 61 | 62 | 63 | /// 64 | /// Adds the elements of the specified sequence to the collection. 65 | /// 66 | /// The type of the elements. 67 | /// The collection to add items to. 68 | /// 69 | /// The sequence whose elements should be added to the end of the collection. 70 | /// 71 | public static void AddRange(this ICollection collection, IEnumerable sequence) 72 | { 73 | foreach (var item in sequence) 74 | { 75 | collection.Add(item); 76 | } 77 | } 78 | 79 | /// 80 | /// Returns a string array that contains the substrings in this string that are 81 | /// delimited by a specified string. 82 | /// 83 | /// The string to split. 84 | /// A string that delimits the substrings in this string. 85 | /// 86 | /// An array whose elements contain the substrings in this string that are delimited 87 | /// by the separator string. 88 | /// 89 | public static string[] Split(this string str, string separator) 90 | { 91 | return str.Split(new string[] { separator }, StringSplitOptions.None); 92 | } 93 | 94 | /// 95 | /// Performs the specified action on each element of the sequence. 96 | /// 97 | /// The type of the elements. 98 | /// The sequence to perform the actions on. 99 | /// The delegate to perform on each element of the sequence. 100 | public static void ForEach(this IEnumerable sequence, Action action) 101 | { 102 | foreach (var element in sequence) 103 | { 104 | action(element); 105 | } 106 | } 107 | 108 | /// 109 | /// Performs the specified action on each element of the sequence. 110 | /// 111 | /// The type of the elements. 112 | /// The sequence to perform the actions on. 113 | /// The delegate to perform on each element of the sequence. 114 | /// The second int parameter is the position of the element. 115 | public static void ForEach(this IEnumerable sequence, Action action) 116 | { 117 | int i = 0; 118 | foreach (var element in sequence) 119 | { 120 | action(element, i); 121 | i++; 122 | } 123 | } 124 | 125 | /// 126 | /// Joins substrings together with separator strings. 127 | /// 128 | /// The sequence of strings to join together. 129 | /// The string to separate the strings by 130 | /// A string which is the result of joining the strings together. 131 | public static string Join(this IEnumerable strArr, string joinString) 132 | { 133 | StringBuilder sb = new StringBuilder(); 134 | int count = strArr.Count(); 135 | int i = 0; 136 | foreach (var str in strArr) 137 | { 138 | sb.Append(str); 139 | if (i < count - 1) 140 | { 141 | sb.Append(joinString); 142 | } 143 | i++; 144 | } 145 | return sb.ToString(); 146 | } 147 | 148 | /// 149 | /// Joins substrings together without separator strings. 150 | /// 151 | /// The sequence of strings to join together. 152 | /// A string which is the result of joining the strings together. 153 | public static string Join(this IList strArr) 154 | { 155 | StringBuilder sb = new StringBuilder(); 156 | foreach (var str in strArr) 157 | { 158 | sb.Append(str); 159 | } 160 | return sb.ToString(); 161 | } 162 | 163 | public static TValue GetOrNull(this IDictionary dictionary, TKey key) 164 | { 165 | return dictionary.GetOrDefault(key, default(TValue)); 166 | } 167 | 168 | public static TValue GetOrDefault(this IDictionary dictionary, TKey key, TValue defaultValue) 169 | { 170 | if (dictionary.ContainsKey(key)) 171 | { 172 | return dictionary[key]; 173 | } 174 | else 175 | { 176 | return defaultValue; 177 | } 178 | } 179 | 180 | public static T GetOrDefault(this IList list, int index, T defaultValue) 181 | { 182 | if (index >= 0 && index < list.Count) 183 | { 184 | return list[index]; 185 | } 186 | else 187 | { 188 | return defaultValue; 189 | } 190 | } 191 | 192 | public static bool Contains(this string str, string lookFor, StringComparison comparisonType) 193 | { 194 | return str.IndexOf(lookFor, comparisonType) >= 0; 195 | } 196 | 197 | static Encoding shiftJis = Encoding.GetEncoding("shift-jis"); 198 | 199 | public static int IndexOf(this IList array, string lookForString, int startIndex) 200 | { 201 | var lookFor = shiftJis.GetBytes(lookForString); 202 | for (int i = startIndex; i < array.Count - lookFor.Length; i++) 203 | { 204 | if (array[i] == lookFor[0]) 205 | { 206 | int i2; 207 | for (i2 = 1; i2 < lookFor.Length; i2++) 208 | { 209 | if (array[i + i2] != lookFor[i2]) 210 | { 211 | break; 212 | } 213 | } 214 | if (i2 == lookFor.Length) 215 | { 216 | return i; 217 | } 218 | } 219 | } 220 | return -1; 221 | } 222 | 223 | public static void Set(this HashSet set, T value) 224 | { 225 | if (set.Contains(value)) 226 | { 227 | 228 | } 229 | else 230 | { 231 | set.Add(value); 232 | } 233 | } 234 | 235 | public static void Set(this IDictionary dic, TKey key, TValue value) 236 | { 237 | if (dic.ContainsKey(key)) 238 | { 239 | dic[key] = value; 240 | } 241 | else 242 | { 243 | dic.Add(key, value); 244 | } 245 | } 246 | 247 | public static void Set(this IDictionary> dic, TKey key, TValue value) 248 | { 249 | var set = dic.GetOrNew(key); 250 | set.Set(value); 251 | dic.Set(key, set); 252 | } 253 | 254 | public static void SetBit(this IDictionary dic, TKey key, TValue value) 255 | { 256 | if (dic.ContainsKey(key)) 257 | { 258 | int oldValue = (int)((object)dic[key]); 259 | oldValue |= (int)(object)value; 260 | var newValue = (TValue)(object)oldValue; 261 | dic[key] = newValue; 262 | } 263 | else 264 | { 265 | dic.Add(key, value); 266 | } 267 | } 268 | 269 | public static string ReadStringNullTerminated(this BinaryReader br) 270 | { 271 | List bytes = new List(); 272 | //try 273 | //{ 274 | while (true) 275 | { 276 | var b = br.ReadByte(); 277 | if (b == 0) 278 | { 279 | return shiftJis.GetString(bytes.ToArray()); 280 | } 281 | bytes.Add(b); 282 | } 283 | //} 284 | //catch (EndOfStreamException) 285 | //{ 286 | // return shiftJis.GetString(bytes.ToArray()); 287 | //} 288 | } 289 | 290 | public static string ReadStringFixedSize(this BinaryReader br, int length) 291 | { 292 | byte[] bytes = br.ReadBytes(length); 293 | int zeroIndex = Array.IndexOf(bytes, 0); 294 | if (zeroIndex == -1) 295 | { 296 | zeroIndex = bytes.Length; 297 | } 298 | return shiftJis.GetString(bytes, 0, zeroIndex); 299 | } 300 | 301 | public static void WriteStringNullTerminated(this BinaryWriter bw, string str) 302 | { 303 | byte[] bytes = shiftJis.GetBytes(str); 304 | bw.Write(bytes); 305 | bw.Write((byte)0); 306 | } 307 | 308 | public static void WriteStringFixedSize(this BinaryWriter bw, string str, int length) 309 | { 310 | byte[] bytes = new byte[length]; 311 | shiftJis.GetBytes(str, 0, str.Length, bytes, 0); 312 | bw.Write(bytes); 313 | } 314 | 315 | public static TValue GetOrAddNew(this IDictionary dictionary, TKey key) where TValue : class, new() 316 | { 317 | if (dictionary.ContainsKey(key)) 318 | { 319 | return dictionary[key]; 320 | } 321 | else 322 | { 323 | var newValue = new TValue(); 324 | dictionary.Add(key, newValue); 325 | return newValue; 326 | } 327 | } 328 | 329 | public static TValue GetOrNew(this IDictionary dictionary, TKey key) where TValue : new() 330 | { 331 | if (dictionary.ContainsKey(key)) 332 | { 333 | return dictionary[key]; 334 | } 335 | else 336 | { 337 | return new TValue(); 338 | } 339 | } 340 | 341 | public static T GetOrNull(this IList list, int index) 342 | { 343 | if (index >= list.Count) 344 | { 345 | return default(T); 346 | } 347 | else 348 | { 349 | var value = list[index]; 350 | return value; 351 | } 352 | } 353 | 354 | public static T GetOrNew(this IList list, int index) where T : new() 355 | { 356 | if (index >= list.Count) 357 | { 358 | return new T(); 359 | } 360 | else 361 | { 362 | var value = list[index]; 363 | if (value == null) 364 | { 365 | return new T(); 366 | } 367 | else 368 | { 369 | return value; 370 | } 371 | } 372 | } 373 | 374 | public static void SetOrAdd(this IList list, int index, T value) 375 | { 376 | if (index >= list.Count) 377 | { 378 | while (index > list.Count) 379 | { 380 | list.Add(default(T)); 381 | } 382 | list.Add(value); 383 | } 384 | else 385 | { 386 | list[index] = value; 387 | } 388 | } 389 | 390 | public static bool AllEqualTo(this IEnumerable sequence, T valueToCompareTo) where T : IEquatable 391 | { 392 | return sequence.All(b => b.Equals(valueToCompareTo)); 393 | } 394 | 395 | public static bool AnyEqualTo(this IEnumerable sequence, T valueToCompareTo) where T : IEquatable 396 | { 397 | return sequence.Any(b => b.Equals(valueToCompareTo)); 398 | } 399 | 400 | public static int Max(this IEnumerable sequence, out int index) 401 | { 402 | int maxIndex = 0; 403 | int maxValue = sequence.FirstOrDefault(); 404 | 405 | int i = 0; 406 | 407 | foreach (int current in sequence) 408 | { 409 | if (current > maxValue) 410 | { 411 | maxIndex = i; 412 | maxValue = current; 413 | } 414 | i++; 415 | } 416 | index = maxIndex; 417 | return maxValue; 418 | } 419 | 420 | public static int Min(this IEnumerable sequence, out int index) 421 | { 422 | int minIndex = 0; 423 | int minValue = sequence.FirstOrDefault(); 424 | 425 | int i = 0; 426 | 427 | foreach (int current in sequence) 428 | { 429 | if (current < minValue) 430 | { 431 | minIndex = i; 432 | minValue = current; 433 | } 434 | i++; 435 | } 436 | index = minIndex; 437 | return minValue; 438 | } 439 | 440 | public static string ToHexString(this byte[] byteArray) 441 | { 442 | StringBuilder sb = new StringBuilder(byteArray.Length * 2); 443 | for (int i = 0; i < byteArray.Length; i++) 444 | { 445 | sb.Append(byteArray[i].ToString("X2")); 446 | } 447 | return sb.ToString(); 448 | } 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /ALDExplorer/FLATFormat.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Text; 4 | using HexDump; 5 | 6 | namespace ALDExplorer 7 | { 8 | using Node = AldFileSubimages.SubImageFinder.Node; 9 | 10 | static class FLAT 11 | { 12 | static Encoding shiftJis = Encoding.GetEncoding("shift-jis"); 13 | 14 | public static byte RotByteL(byte v, int count) 15 | { 16 | count &= 7; 17 | return (byte)(v << count | v >> (8 - count)); 18 | } 19 | 20 | public static T[] Slice(this T[] arr, long indexFrom, long indexTo) 21 | { 22 | if (indexFrom > indexTo) 23 | throw new System.ArgumentOutOfRangeException("indexFrom is bigger than indexTo!"); 24 | 25 | if (indexFrom > arr.Length - 1) 26 | return new T[0]; 27 | 28 | if (indexTo > arr.Length - 1) 29 | indexTo = arr.Length - 1; 30 | 31 | long length = indexTo - indexFrom; 32 | T[] result = new T[length]; 33 | System.Array.Copy(arr, indexFrom, result, 0, length); 34 | 35 | return result; 36 | } 37 | 38 | enum DataType 39 | { 40 | FLAG_CG = 2, 41 | FLAT_ZLIB = 5, 42 | }; 43 | 44 | public static Node[] GetNodes(byte[] bytes, string parent_file, AldFileEntry parent) 45 | { 46 | List list = new List(); 47 | using (var ms1 = new MemoryStream(bytes)) 48 | using (var brfile = new BinaryReader(ms1)) 49 | while (brfile.BaseStream.Position < brfile.BaseStream.Length) 50 | { 51 | long tag_start_offset = brfile.BaseStream.Position; 52 | var tag = (new Tag()).ReadTag(brfile); 53 | if (tag.TagName == "LIBL" && tag.TagLength > 16) 54 | { 55 | var dataBytes = tag.TagData; 56 | using (var ms = new MemoryStream(dataBytes)) 57 | using (var brtag = new BinaryReader(ms)) 58 | { 59 | int fileCount = brtag.ReadInt32(); //9 60 | int dataLength = 0; 61 | string fileName = "alt_image.ajp"; 62 | for (int fileNumber = 0; fileNumber < fileCount; fileNumber++) 63 | { 64 | HexView.Debugger(dataBytes.Slice(brtag.BaseStream.Position, brtag.BaseStream.Position + 50)); 65 | uint fileNameLength = brtag.ReadUInt32(); //8 66 | if (fileNameLength > 255) continue; 67 | 68 | var fileNameBytes = brtag.ReadBytes((int)fileNameLength); 69 | brtag.BaseStream.Position = ((brtag.BaseStream.Position - 1) | 3) + 1; 70 | 71 | fileName = shiftJis.GetString(fileNameBytes); 72 | if (parent_file != null) 73 | fileName = Path.GetFileNameWithoutExtension(parent_file) + 74 | $"_image{fileNumber.ToString().PadLeft(1 + (int)System.Math.Log(fileCount, 10), '0')}"; 75 | 76 | 77 | int type = brtag.ReadInt32(); 78 | dataLength = brtag.ReadInt32(); 79 | 80 | long libl_img_off = brtag.BaseStream.Position + tag_start_offset + 8; // 8 is (tag + size) 81 | HexView.Debugger(dataBytes.Slice(libl_img_off, libl_img_off + 20)); 82 | 83 | if (type == (int)DataType.FLAG_CG) 84 | { 85 | int unknown1 = brtag.ReadInt32(); // XXX: special case: CG usually (not always!) has extra int32 86 | int maybe_head; 87 | if (unknown1 == 0x00544E51 || unknown1 == 0x00504A41) // QNT or AJP 88 | { 89 | maybe_head = unknown1; 90 | } 91 | else 92 | { 93 | maybe_head = brtag.ReadInt32(); 94 | } 95 | 96 | byte[] imageBytes = null; 97 | brtag.BaseStream.Position -= 4; 98 | if (maybe_head == 0x00544E51) //QNT needs the tag 99 | { 100 | libl_img_off -= 4; 101 | imageBytes = brtag.ReadBytes(dataLength); 102 | fileName += ".qnt"; 103 | } 104 | else if (maybe_head == 0x00504A41) //AJP needs the tag 105 | { 106 | libl_img_off -= 4; 107 | imageBytes = brtag.ReadBytes(dataLength); 108 | fileName += ".ajp"; 109 | } 110 | else { // unknown image 111 | //imageBytes = brtag.ReadBytes(dataLength); 112 | brtag.BaseStream.Position += dataLength; 113 | fileName += ".dat"; 114 | } 115 | 116 | if (imageBytes != null) 117 | list.Add(new Node() { Bytes = imageBytes, FileName = fileName, Offset = libl_img_off, Parent = parent }); 118 | } 119 | else 120 | { 121 | brtag.BaseStream.Position += dataLength; 122 | } 123 | brtag.BaseStream.Position = ((brtag.BaseStream.Position - 1) | 3) + 1 - 4; 124 | } // for 125 | } 126 | } // LIBL 127 | else if (tag.TagName == "TALT" && tag.TagLength > 16) 128 | { 129 | var dataBytes = tag.TagData; 130 | using (var ms = new MemoryStream(dataBytes)) 131 | using (var brtag = new BinaryReader(ms)) 132 | { 133 | int fileCount = brtag.ReadInt32(); //9 134 | int dataLength = 0; 135 | string fileName = "alt_image.ajp"; 136 | for (int fileNumber = 0; fileNumber < fileCount; fileNumber++) 137 | { 138 | if (parent_file != null) 139 | fileName = Path.GetFileNameWithoutExtension(parent_file) + 140 | $"_alt_image{fileNumber.ToString().PadLeft(System.Math.Max(0, (int)System.Math.Log(10, fileCount)) + 1, '0')}"; 141 | 142 | dataLength = brtag.ReadInt32(); //9 143 | //brtag.BaseStream.Position += dataLength; 144 | brtag.BaseStream.Position = ((brtag.BaseStream.Position - 1) | 3) + 1; 145 | 146 | int maybe_head = brtag.ReadInt32(); // 147 | long libl_img_off = brtag.BaseStream.Position + tag_start_offset + 8; 148 | HexView.Debugger(bytes.Slice(libl_img_off, libl_img_off + 20)); 149 | 150 | byte[] imageBytes = null; 151 | brtag.BaseStream.Position -= 4; 152 | if (maybe_head == 0x00544E51) //QNT needs the tag 153 | { 154 | libl_img_off -= 4; 155 | imageBytes = brtag.ReadBytes(dataLength); 156 | fileName += ".qnt"; 157 | } 158 | if (maybe_head == 0x00504A41) //AJP needs the tag 159 | { 160 | libl_img_off -= 4; 161 | imageBytes = brtag.ReadBytes(dataLength); 162 | fileName += ".ajp"; 163 | } 164 | else { // unknown image 165 | //imageBytes = brtag.ReadBytes(dataLength); 166 | brtag.BaseStream.Position += dataLength; 167 | fileName += ".dat"; 168 | } 169 | 170 | if (imageBytes != null) 171 | list.Add(new Node() { Bytes = imageBytes, FileName = fileName, Offset = libl_img_off, Parent = parent }); 172 | brtag.BaseStream.Position = ((brtag.BaseStream.Position - 1) | 3) + 1 - 4; 173 | } // for 174 | } 175 | } // TALT 176 | } // EndOfStream 177 | return list.ToArray(); 178 | } 179 | 180 | static private Node FindNodeByNumber(Node[] nodes, int i, int total) { 181 | foreach (var n in nodes) { 182 | var expected = $"_image{i.ToString().PadLeft(1 + (int)System.Math.Log(total, 10), '0')}."; 183 | if (n.FileName.Contains(expected)) return n; 184 | } 185 | return null; 186 | } 187 | 188 | static public byte[] ReplaceNodes(byte[] bytes, Node[] nodes) 189 | { 190 | var ms1 = new MemoryStream(bytes); 191 | var br1 = new BinaryReader(ms1); 192 | 193 | var msOutput = new MemoryStream(); 194 | var bw = new BinaryWriter(msOutput); 195 | while (br1.BaseStream.Position < br1.BaseStream.Length) 196 | { 197 | var tag = (new Tag()).ReadTag(br1); 198 | if (tag.TagName == "LIBL") 199 | { 200 | bw.WriteStringFixedSize("LIBL", 4); 201 | long tagLengthPos = bw.BaseStream.Position; 202 | bw.Write((int)0); 203 | 204 | var dataBytes = tag.TagData; 205 | var ms = new MemoryStream(dataBytes); 206 | var br = new BinaryReader(ms); 207 | int fileCount = br.ReadInt32(); 208 | bw.Write((int)fileCount); 209 | for (int fileNumber = 0; fileNumber < fileCount; fileNumber++) 210 | { 211 | int fileNameLength = br.ReadInt32(); 212 | var fileNameBytes = br.ReadBytes(fileNameLength); 213 | //string fileName = shiftJis.GetString(fileNameBytes); 214 | long lastPosition = br.BaseStream.Position; 215 | br.BaseStream.Position = ((br.BaseStream.Position - 1) | 3) + 1; 216 | int paddingCount1 = (int)(br.BaseStream.Position - lastPosition); 217 | 218 | int type = br.ReadInt32(); 219 | int dataLength = br.ReadInt32(); 220 | 221 | int maybe_head = 0; 222 | if (type == (int)DataType.FLAG_CG) 223 | { 224 | int unknown1 = br.ReadInt32(); // XXX: special case: CG usually (not always!) has extra int32 225 | if (unknown1 == 0x00544E51 || unknown1 == 0x00504A41) // QNT or AJP 226 | { 227 | maybe_head = unknown1; 228 | } 229 | else 230 | { 231 | maybe_head = br.ReadInt32(); 232 | bw.Write(maybe_head); 233 | } 234 | br.BaseStream.Position -= 4; 235 | } 236 | 237 | dataBytes = null; 238 | if (maybe_head == 0x00544E51 || maybe_head == 0x00504A41) //QNT, AJP 239 | br.BaseStream.Position += dataLength; // Don't care about previous content 240 | else 241 | dataBytes = br.ReadBytes(dataLength); 242 | 243 | lastPosition = br.BaseStream.Position; 244 | br.BaseStream.Position = ((br.BaseStream.Position - 1) | 3) + 1; 245 | int paddingCount2 = (int)(br.BaseStream.Position - lastPosition); 246 | 247 | var node = FindNodeByNumber(nodes, fileNumber, fileCount); 248 | var newFileNameBytes = fileNameBytes;//shiftJis.GetBytes(node.FileName); 249 | 250 | long outputPosition = bw.BaseStream.Position; 251 | 252 | bw.Write((int)newFileNameBytes.Length); 253 | bw.Write(newFileNameBytes); 254 | 255 | int outputPadding1 = (int)(((((bw.BaseStream.Position - outputPosition) - 1) | 3) + 1) - 256 | (bw.BaseStream.Position - outputPosition)); 257 | for (int i = 0; i < outputPadding1; i++) 258 | bw.Write((byte)0); 259 | 260 | bw.Write((int)type); 261 | if (node != null) { 262 | bw.Write((int)node.Bytes.Length); 263 | bw.Write(node.Bytes); 264 | } else if (dataBytes != null) { 265 | bw.Write((int)dataBytes.Length); 266 | bw.Write(dataBytes); 267 | } 268 | 269 | int outputPadding2 = (int)(((((bw.BaseStream.Position - outputPosition) - 1) | 3) + 1) - 270 | (bw.BaseStream.Position - outputPosition)); 271 | for (int i = 0; i < outputPadding2; i++) 272 | bw.Write((byte)0); 273 | } 274 | 275 | long lastPos = bw.BaseStream.Position; 276 | int tagLength = (int)(lastPos - (tagLengthPos + 4)); 277 | bw.BaseStream.Position = tagLengthPos; 278 | bw.Write((int)tagLength); 279 | bw.BaseStream.Position = lastPos; 280 | 281 | ms.Dispose(); 282 | br.Dispose(); 283 | } 284 | else 285 | tag.WriteTag(bw); 286 | } 287 | 288 | ms1.Dispose(); 289 | br1.Dispose(); 290 | 291 | var ret = msOutput.ToArray(); 292 | 293 | bw.Dispose(); 294 | msOutput.Dispose(); 295 | 296 | return ret; 297 | } 298 | } 299 | } -------------------------------------------------------------------------------- /ALDExplorer/FileFormatSelectionForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ALDExplorer 2 | { 3 | partial class FileFormatSelectionForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.aldRadioButton = new System.Windows.Forms.RadioButton(); 32 | this.datRadioButton = new System.Windows.Forms.RadioButton(); 33 | this.alkRadioButton = new System.Windows.Forms.RadioButton(); 34 | this.afa1RadioButton = new System.Windows.Forms.RadioButton(); 35 | this.afa2RadioButton = new System.Windows.Forms.RadioButton(); 36 | this.button1 = new System.Windows.Forms.Button(); 37 | this.button2 = new System.Windows.Forms.Button(); 38 | this.SuspendLayout(); 39 | // 40 | // aldRadioButton 41 | // 42 | this.aldRadioButton.AutoSize = true; 43 | this.aldRadioButton.Location = new System.Drawing.Point(12, 12); 44 | this.aldRadioButton.Name = "aldRadioButton"; 45 | this.aldRadioButton.Size = new System.Drawing.Size(65, 17); 46 | this.aldRadioButton.TabIndex = 0; 47 | this.aldRadioButton.Text = "ALD File"; 48 | this.aldRadioButton.UseVisualStyleBackColor = true; 49 | // 50 | // datRadioButton 51 | // 52 | this.datRadioButton.AutoSize = true; 53 | this.datRadioButton.Location = new System.Drawing.Point(12, 35); 54 | this.datRadioButton.Name = "datRadioButton"; 55 | this.datRadioButton.Size = new System.Drawing.Size(118, 17); 56 | this.datRadioButton.TabIndex = 1; 57 | this.datRadioButton.Text = "System 3.0 DAT file"; 58 | this.datRadioButton.UseVisualStyleBackColor = true; 59 | // 60 | // alkRadioButton 61 | // 62 | this.alkRadioButton.AutoSize = true; 63 | this.alkRadioButton.Location = new System.Drawing.Point(12, 58); 64 | this.alkRadioButton.Name = "alkRadioButton"; 65 | this.alkRadioButton.Size = new System.Drawing.Size(64, 17); 66 | this.alkRadioButton.TabIndex = 2; 67 | this.alkRadioButton.Text = "ALK File"; 68 | this.alkRadioButton.UseVisualStyleBackColor = true; 69 | // 70 | // afa1RadioButton 71 | // 72 | this.afa1RadioButton.AutoSize = true; 73 | this.afa1RadioButton.Location = new System.Drawing.Point(12, 81); 74 | this.afa1RadioButton.Name = "afa1RadioButton"; 75 | this.afa1RadioButton.Size = new System.Drawing.Size(116, 17); 76 | this.afa1RadioButton.TabIndex = 3; 77 | this.afa1RadioButton.Text = "AFA File (version 1)"; 78 | this.afa1RadioButton.UseVisualStyleBackColor = true; 79 | // 80 | // afa2RadioButton 81 | // 82 | this.afa2RadioButton.AutoSize = true; 83 | this.afa2RadioButton.Checked = true; 84 | this.afa2RadioButton.Location = new System.Drawing.Point(12, 104); 85 | this.afa2RadioButton.Name = "afa2RadioButton"; 86 | this.afa2RadioButton.Size = new System.Drawing.Size(116, 17); 87 | this.afa2RadioButton.TabIndex = 4; 88 | this.afa2RadioButton.TabStop = true; 89 | this.afa2RadioButton.Text = "AFA File (version 2)"; 90 | this.afa2RadioButton.UseVisualStyleBackColor = true; 91 | // 92 | // button1 93 | // 94 | this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; 95 | this.button1.Location = new System.Drawing.Point(12, 136); 96 | this.button1.Name = "button1"; 97 | this.button1.Size = new System.Drawing.Size(75, 23); 98 | this.button1.TabIndex = 5; 99 | this.button1.Text = "OK"; 100 | this.button1.UseVisualStyleBackColor = true; 101 | // 102 | // button2 103 | // 104 | this.button2.DialogResult = System.Windows.Forms.DialogResult.Cancel; 105 | this.button2.Location = new System.Drawing.Point(93, 136); 106 | this.button2.Name = "button2"; 107 | this.button2.Size = new System.Drawing.Size(75, 23); 108 | this.button2.TabIndex = 6; 109 | this.button2.Text = "Cancel"; 110 | this.button2.UseVisualStyleBackColor = true; 111 | // 112 | // FileFormatSelectionForm 113 | // 114 | this.AcceptButton = this.button1; 115 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 116 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 117 | this.CancelButton = this.button2; 118 | this.ClientSize = new System.Drawing.Size(180, 169); 119 | this.Controls.Add(this.button2); 120 | this.Controls.Add(this.button1); 121 | this.Controls.Add(this.afa2RadioButton); 122 | this.Controls.Add(this.afa1RadioButton); 123 | this.Controls.Add(this.alkRadioButton); 124 | this.Controls.Add(this.datRadioButton); 125 | this.Controls.Add(this.aldRadioButton); 126 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 127 | this.MaximizeBox = false; 128 | this.MinimizeBox = false; 129 | this.Name = "FileFormatSelectionForm"; 130 | this.Text = "Select File Format:"; 131 | this.Load += new System.EventHandler(this.FileFormatSelectionForm_Load); 132 | this.ResumeLayout(false); 133 | this.PerformLayout(); 134 | 135 | } 136 | 137 | #endregion 138 | 139 | private System.Windows.Forms.RadioButton aldRadioButton; 140 | private System.Windows.Forms.RadioButton datRadioButton; 141 | private System.Windows.Forms.RadioButton alkRadioButton; 142 | private System.Windows.Forms.RadioButton afa1RadioButton; 143 | private System.Windows.Forms.RadioButton afa2RadioButton; 144 | private System.Windows.Forms.Button button1; 145 | private System.Windows.Forms.Button button2; 146 | } 147 | } -------------------------------------------------------------------------------- /ALDExplorer/FileFormatSelectionForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.IO; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | namespace ALDExplorer 12 | { 13 | public partial class FileFormatSelectionForm : Form 14 | { 15 | public FileFormatSelectionForm() 16 | { 17 | InitializeComponent(); 18 | } 19 | 20 | public static AldFileType SelectFileType() 21 | { 22 | using (var form = new FileFormatSelectionForm()) 23 | { 24 | var dialogResult = form.ShowDialog(); 25 | if (dialogResult == DialogResult.OK) 26 | { 27 | if (form.aldRadioButton.Checked) 28 | { 29 | return AldFileType.AldFile; 30 | } 31 | if (form.datRadioButton.Checked) 32 | { 33 | return AldFileType.DatFile; 34 | } 35 | if (form.alkRadioButton.Checked) 36 | { 37 | return AldFileType.AlkFile; 38 | } 39 | if (form.afa1RadioButton.Checked) 40 | { 41 | return AldFileType.AFA1File; 42 | } 43 | if (form.afa2RadioButton.Checked) 44 | { 45 | return AldFileType.AFA2File; 46 | } 47 | } 48 | } 49 | return AldFileType.Invalid; 50 | } 51 | 52 | private void FileFormatSelectionForm_Load(object sender, EventArgs e) 53 | { 54 | 55 | } 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /ALDExplorer/FileFormatSelectionForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /ALDExplorer/HexDump.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace HexDump 5 | { 6 | static class HexView 7 | { 8 | public static void Debugger(byte[] bytes, int bytesPerLine = 16) 9 | { 10 | System.Diagnostics.Debug.Print(Dump(bytes, bytesPerLine)); 11 | } 12 | 13 | public static string Dump(byte[] bytes, int bytesPerLine = 16) 14 | { 15 | if (bytes == null) return ""; 16 | int bytesLength = bytes.Length; 17 | 18 | char[] HexChars = "0123456789ABCDEF".ToCharArray(); 19 | 20 | int firstHexColumn = 21 | 8 // 8 characters for the address 22 | + 3; // 3 spaces 23 | 24 | int firstCharColumn = firstHexColumn 25 | + bytesPerLine * 3 // - 2 digit for the hexadecimal value and 1 space 26 | + (bytesPerLine - 1) / 8 // - 1 extra space every 8 characters from the 9th 27 | + 2; // 2 spaces 28 | 29 | int lineLength = firstCharColumn 30 | + bytesPerLine // - characters to show the ascii value 31 | + Environment.NewLine.Length; // Carriage return and line feed (should normally be 2) 32 | 33 | char[] line = (new String(' ', lineLength - 2) + Environment.NewLine).ToCharArray(); 34 | int expectedLines = (bytesLength + bytesPerLine - 1) / bytesPerLine; 35 | StringBuilder result = new StringBuilder(expectedLines * lineLength); 36 | 37 | for (int i = 0; i < bytesLength; i += bytesPerLine) 38 | { 39 | line[0] = HexChars[(i >> 28) & 0xF]; 40 | line[1] = HexChars[(i >> 24) & 0xF]; 41 | line[2] = HexChars[(i >> 20) & 0xF]; 42 | line[3] = HexChars[(i >> 16) & 0xF]; 43 | line[4] = HexChars[(i >> 12) & 0xF]; 44 | line[5] = HexChars[(i >> 8) & 0xF]; 45 | line[6] = HexChars[(i >> 4) & 0xF]; 46 | line[7] = HexChars[(i >> 0) & 0xF]; 47 | 48 | int hexColumn = firstHexColumn; 49 | int charColumn = firstCharColumn; 50 | 51 | for (int j = 0; j < bytesPerLine; j++) 52 | { 53 | if (j > 0 && (j & 7) == 0) hexColumn++; 54 | if (i + j >= bytesLength) 55 | { 56 | line[hexColumn] = ' '; 57 | line[hexColumn + 1] = ' '; 58 | line[charColumn] = ' '; 59 | } 60 | else 61 | { 62 | byte b = bytes[i + j]; 63 | line[hexColumn] = HexChars[(b >> 4) & 0xF]; 64 | line[hexColumn + 1] = HexChars[b & 0xF]; 65 | line[charColumn] = asciiSymbol( b ); 66 | } 67 | hexColumn += 3; 68 | charColumn++; 69 | } 70 | result.Append(line); 71 | } 72 | return result.ToString(); 73 | } 74 | static char asciiSymbol( byte val ) 75 | { 76 | if( val < 32 ) return '.'; // Non-printable ASCII 77 | if( val < 127 ) return (char)val; // Normal ASCII 78 | // Handle the hole in Latin-1 79 | if( val == 127 ) return '.'; 80 | if( val < 0x90 ) return "€.‚ƒ„…†‡ˆ‰Š‹Œ.Ž."[ val & 0xF ]; 81 | if( val < 0xA0 ) return ".‘’“”•–—˜™š›œ.žŸ"[ val & 0xF ]; 82 | if( val == 0xAD ) return '.'; // Soft hyphen: this symbol is zero-width even in monospace fonts 83 | return (char)val; // Normal Latin-1 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /ALDExplorer/ImportFileTypeForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ALDExplorer 2 | { 3 | partial class ImportFileTypeForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.comboBox1 = new System.Windows.Forms.ComboBox(); 32 | this.label1 = new System.Windows.Forms.Label(); 33 | this.button1 = new System.Windows.Forms.Button(); 34 | this.SuspendLayout(); 35 | // 36 | // comboBox1 37 | // 38 | this.comboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 39 | this.comboBox1.FormattingEnabled = true; 40 | this.comboBox1.Items.AddRange(new object[] { 41 | ".qnt", 42 | ".vsp", 43 | ".pms", 44 | ".ajp", 45 | ".png", 46 | ".jpg", 47 | }); 48 | this.comboBox1.Location = new System.Drawing.Point(15, 25); 49 | this.comboBox1.Name = "comboBox1"; 50 | this.comboBox1.Size = new System.Drawing.Size(166, 21); 51 | this.comboBox1.TabIndex = 0; 52 | // 53 | // label1 54 | // 55 | this.label1.AutoSize = true; 56 | this.label1.Location = new System.Drawing.Point(12, 9); 57 | this.label1.Name = "label1"; 58 | this.label1.Size = new System.Drawing.Size(130, 13); 59 | this.label1.TabIndex = 1; 60 | this.label1.Text = "File Format for added files:"; 61 | // 62 | // button1 63 | // 64 | this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel; 65 | this.button1.Location = new System.Drawing.Point(106, 52); 66 | this.button1.Name = "button1"; 67 | this.button1.Size = new System.Drawing.Size(75, 23); 68 | this.button1.TabIndex = 2; 69 | this.button1.Text = "OK"; 70 | this.button1.UseVisualStyleBackColor = true; 71 | // 72 | // ImportFileTypeForm 73 | // 74 | this.AcceptButton = this.button1; 75 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 76 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 77 | this.CancelButton = this.button1; 78 | this.ClientSize = new System.Drawing.Size(193, 86); 79 | this.Controls.Add(this.button1); 80 | this.Controls.Add(this.label1); 81 | this.Controls.Add(this.comboBox1); 82 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 83 | this.MaximizeBox = false; 84 | this.MinimizeBox = false; 85 | this.Name = "ImportFileTypeForm"; 86 | this.Text = "File Format"; 87 | this.Load += new System.EventHandler(this.ImportFileTypeForm_Load); 88 | this.ResumeLayout(false); 89 | this.PerformLayout(); 90 | 91 | } 92 | 93 | #endregion 94 | 95 | private System.Windows.Forms.ComboBox comboBox1; 96 | private System.Windows.Forms.Label label1; 97 | private System.Windows.Forms.Button button1; 98 | } 99 | } -------------------------------------------------------------------------------- /ALDExplorer/ImportFileTypeForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.IO; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | namespace ALDExplorer 12 | { 13 | public partial class ImportFileTypeForm : Form 14 | { 15 | public string FileType 16 | { 17 | get 18 | { 19 | return this.comboBox1.SelectedItem.ToString(); 20 | } 21 | } 22 | 23 | public ImportFileTypeForm() 24 | { 25 | InitializeComponent(); 26 | } 27 | 28 | public ImportFileTypeForm(params string[] fileTypes) : this() 29 | { 30 | this.comboBox1.Items.Clear(); 31 | this.comboBox1.Items.AddRange(fileTypes); 32 | } 33 | 34 | 35 | private void ImportFileTypeForm_Load(object sender, EventArgs e) 36 | { 37 | this.comboBox1.SelectedIndex = 0; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ALDExplorer/ImportFileTypeForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /ALDExplorer/Interfaces.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace ALDExplorer 8 | { 9 | /// 10 | /// An object that has an array Index property. 11 | /// 12 | public interface IWithIndex 13 | { 14 | /// 15 | /// The array index of the object. 16 | /// 17 | int Index { get; set; } 18 | } 19 | 20 | /// 21 | /// An object that has a parent 22 | /// 23 | public interface IWithParent 24 | { 25 | /// 26 | /// The parent of this object. 27 | /// 28 | object Parent { get; set; } 29 | } 30 | 31 | /// 32 | /// An object that has a parent 33 | /// 34 | /// The type of the parent 35 | public interface IWithParent : IWithParent 36 | { 37 | /// 38 | /// The parent of this object. 39 | /// 40 | new T Parent { get; set; } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /ALDExplorer/PrefixForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ALDExplorer 2 | { 3 | partial class PrefixForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.DirectoryNameTextBox = new System.Windows.Forms.TextBox(); 32 | this.lstDirectories = new System.Windows.Forms.ListBox(); 33 | this.lblPrompt = new System.Windows.Forms.Label(); 34 | this.okButton = new System.Windows.Forms.Button(); 35 | this.panel1 = new System.Windows.Forms.Panel(); 36 | this.panel2 = new System.Windows.Forms.Panel(); 37 | this.cancelButton = new System.Windows.Forms.Button(); 38 | this.panel3 = new System.Windows.Forms.Panel(); 39 | this.panel1.SuspendLayout(); 40 | this.panel2.SuspendLayout(); 41 | this.panel3.SuspendLayout(); 42 | this.SuspendLayout(); 43 | // 44 | // DirectoryNameTextBox 45 | // 46 | this.DirectoryNameTextBox.Dock = System.Windows.Forms.DockStyle.Fill; 47 | this.DirectoryNameTextBox.Location = new System.Drawing.Point(12, 20); 48 | this.DirectoryNameTextBox.Name = "DirectoryNameTextBox"; 49 | this.DirectoryNameTextBox.Size = new System.Drawing.Size(268, 20); 50 | this.DirectoryNameTextBox.TabIndex = 0; 51 | // 52 | // lstDirectories 53 | // 54 | this.lstDirectories.Dock = System.Windows.Forms.DockStyle.Fill; 55 | this.lstDirectories.FormattingEnabled = true; 56 | this.lstDirectories.Location = new System.Drawing.Point(12, 4); 57 | this.lstDirectories.Name = "lstDirectories"; 58 | this.lstDirectories.ScrollAlwaysVisible = true; 59 | this.lstDirectories.Size = new System.Drawing.Size(268, 95); 60 | this.lstDirectories.TabIndex = 1; 61 | this.lstDirectories.SelectedIndexChanged += new System.EventHandler(this.lstDirectories_SelectedIndexChanged); 62 | // 63 | // lblPrompt 64 | // 65 | this.lblPrompt.Dock = System.Windows.Forms.DockStyle.Top; 66 | this.lblPrompt.Location = new System.Drawing.Point(12, 7); 67 | this.lblPrompt.Margin = new System.Windows.Forms.Padding(3); 68 | this.lblPrompt.Name = "lblPrompt"; 69 | this.lblPrompt.Size = new System.Drawing.Size(268, 13); 70 | this.lblPrompt.TabIndex = 2; 71 | this.lblPrompt.Text = "Select file prefix (directoy name) for files:"; 72 | // 73 | // okButton 74 | // 75 | this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 76 | this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; 77 | this.okButton.Location = new System.Drawing.Point(124, 3); 78 | this.okButton.Name = "okButton"; 79 | this.okButton.Size = new System.Drawing.Size(75, 23); 80 | this.okButton.TabIndex = 3; 81 | this.okButton.Text = "OK"; 82 | this.okButton.UseVisualStyleBackColor = true; 83 | // 84 | // panel1 85 | // 86 | this.panel1.Controls.Add(this.DirectoryNameTextBox); 87 | this.panel1.Controls.Add(this.lblPrompt); 88 | this.panel1.Dock = System.Windows.Forms.DockStyle.Top; 89 | this.panel1.Location = new System.Drawing.Point(0, 0); 90 | this.panel1.Name = "panel1"; 91 | this.panel1.Padding = new System.Windows.Forms.Padding(12, 7, 12, 0); 92 | this.panel1.Size = new System.Drawing.Size(292, 47); 93 | this.panel1.TabIndex = 4; 94 | // 95 | // panel2 96 | // 97 | this.panel2.Controls.Add(this.cancelButton); 98 | this.panel2.Controls.Add(this.okButton); 99 | this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; 100 | this.panel2.Location = new System.Drawing.Point(0, 151); 101 | this.panel2.Name = "panel2"; 102 | this.panel2.Size = new System.Drawing.Size(292, 35); 103 | this.panel2.TabIndex = 5; 104 | // 105 | // cancelButton 106 | // 107 | this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 108 | this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; 109 | this.cancelButton.Location = new System.Drawing.Point(205, 3); 110 | this.cancelButton.Name = "cancelButton"; 111 | this.cancelButton.Size = new System.Drawing.Size(75, 23); 112 | this.cancelButton.TabIndex = 7; 113 | this.cancelButton.Text = "Cancel"; 114 | this.cancelButton.UseVisualStyleBackColor = true; 115 | // 116 | // panel3 117 | // 118 | this.panel3.Controls.Add(this.lstDirectories); 119 | this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; 120 | this.panel3.Location = new System.Drawing.Point(0, 47); 121 | this.panel3.Name = "panel3"; 122 | this.panel3.Padding = new System.Windows.Forms.Padding(12, 4, 12, 4); 123 | this.panel3.Size = new System.Drawing.Size(292, 104); 124 | this.panel3.TabIndex = 6; 125 | // 126 | // PrefixForm 127 | // 128 | this.AcceptButton = this.okButton; 129 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 130 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 131 | this.CancelButton = this.cancelButton; 132 | this.ClientSize = new System.Drawing.Size(292, 186); 133 | this.Controls.Add(this.panel3); 134 | this.Controls.Add(this.panel1); 135 | this.Controls.Add(this.panel2); 136 | this.Name = "PrefixForm"; 137 | this.Text = "Select Directory Name"; 138 | this.Load += new System.EventHandler(this.PrefixForm_Load); 139 | this.panel1.ResumeLayout(false); 140 | this.panel1.PerformLayout(); 141 | this.panel2.ResumeLayout(false); 142 | this.panel3.ResumeLayout(false); 143 | this.ResumeLayout(false); 144 | 145 | } 146 | 147 | #endregion 148 | 149 | private System.Windows.Forms.TextBox DirectoryNameTextBox; 150 | private System.Windows.Forms.ListBox lstDirectories; 151 | private System.Windows.Forms.Label lblPrompt; 152 | private System.Windows.Forms.Button okButton; 153 | private System.Windows.Forms.Panel panel1; 154 | private System.Windows.Forms.Panel panel2; 155 | private System.Windows.Forms.Panel panel3; 156 | private System.Windows.Forms.Button cancelButton; 157 | } 158 | } -------------------------------------------------------------------------------- /ALDExplorer/PrefixForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.IO; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | namespace ALDExplorer 12 | { 13 | public partial class PrefixForm : Form 14 | { 15 | string _path = ""; 16 | public PrefixForm(string path) : this() 17 | { 18 | _path = path; 19 | } 20 | 21 | public PrefixForm() 22 | { 23 | InitializeComponent(); 24 | } 25 | 26 | private void PrefixForm_Load(object sender, EventArgs e) 27 | { 28 | SetPath(_path); 29 | } 30 | 31 | private void SetPath(string path) 32 | { 33 | _path = path; 34 | string[] directories = path.Split(new char[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries).Reverse().ToArray(); 35 | string prefix = ""; 36 | for (int i = 0; i < directories.Length; i++) 37 | { 38 | string dir = directories[i]; 39 | prefix = dir + "\\" + prefix; 40 | lstDirectories.Items.Add(prefix); 41 | } 42 | } 43 | 44 | public static string GetPrefix(string path) 45 | { 46 | using (var prefixForm = new PrefixForm(Path.GetFullPath(path))) 47 | { 48 | if (prefixForm.ShowDialog() == DialogResult.OK) 49 | { 50 | return prefixForm.DirectoryNameTextBox.Text; 51 | } 52 | else 53 | { 54 | return ""; 55 | } 56 | } 57 | } 58 | 59 | private void lstDirectories_SelectedIndexChanged(object sender, EventArgs e) 60 | { 61 | string selectedText = lstDirectories.SelectedItem as string; 62 | if (selectedText != null) 63 | { 64 | DirectoryNameTextBox.Text = selectedText; 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ALDExplorer/PrefixForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /ALDExplorer/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace ALDExplorer 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new Form1()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ALDExplorer/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ALDExplorer.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ALDExplorer.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap minusicon { 67 | get { 68 | object obj = ResourceManager.GetObject("minusicon", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap plusicon { 77 | get { 78 | object obj = ResourceManager.GetObject("plusicon", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ALDExplorer/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\minusicon.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\plusicon.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | -------------------------------------------------------------------------------- /ALDExplorer/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ALDExplorer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.8.1.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ALDExplorer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ALDExplorer/PropertiesForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ALDExplorer 2 | { 3 | partial class PropertiesForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.lblFileName = new System.Windows.Forms.Label(); 32 | this.FileNameTextBox = new System.Windows.Forms.TextBox(); 33 | this.lblFileNumber = new System.Windows.Forms.Label(); 34 | this.FileNumberTextBox = new System.Windows.Forms.TextBox(); 35 | this.ApplyButton = new System.Windows.Forms.Button(); 36 | this.cancelButton = new System.Windows.Forms.Button(); 37 | this.OkButton = new System.Windows.Forms.Button(); 38 | this.lblFileSize = new System.Windows.Forms.Label(); 39 | this.fileSizeLabel = new System.Windows.Forms.Label(); 40 | this.fileAddressLabel = new System.Windows.Forms.Label(); 41 | this.lblFileAddress = new System.Windows.Forms.Label(); 42 | this.lblFileLetter = new System.Windows.Forms.Label(); 43 | this.fileLetterTextBox = new System.Windows.Forms.TextBox(); 44 | this.SuspendLayout(); 45 | // 46 | // lblFileName 47 | // 48 | this.lblFileName.AutoSize = true; 49 | this.lblFileName.Location = new System.Drawing.Point(26, 9); 50 | this.lblFileName.Name = "lblFileName"; 51 | this.lblFileName.Size = new System.Drawing.Size(52, 13); 52 | this.lblFileName.TabIndex = 0; 53 | this.lblFileName.Text = "&Filename:"; 54 | // 55 | // FileNameTextBox 56 | // 57 | this.FileNameTextBox.Location = new System.Drawing.Point(84, 6); 58 | this.FileNameTextBox.MaxLength = 256; 59 | this.FileNameTextBox.Name = "FileNameTextBox"; 60 | this.FileNameTextBox.Size = new System.Drawing.Size(268, 20); 61 | this.FileNameTextBox.TabIndex = 1; 62 | // 63 | // lblFileNumber 64 | // 65 | this.lblFileNumber.AutoSize = true; 66 | this.lblFileNumber.Location = new System.Drawing.Point(12, 32); 67 | this.lblFileNumber.Name = "lblFileNumber"; 68 | this.lblFileNumber.Size = new System.Drawing.Size(66, 13); 69 | this.lblFileNumber.TabIndex = 2; 70 | this.lblFileNumber.Text = "File &Number:"; 71 | // 72 | // FileNumberTextBox 73 | // 74 | this.FileNumberTextBox.Location = new System.Drawing.Point(84, 29); 75 | this.FileNumberTextBox.MaxLength = 5; 76 | this.FileNumberTextBox.Name = "FileNumberTextBox"; 77 | this.FileNumberTextBox.Size = new System.Drawing.Size(66, 20); 78 | this.FileNumberTextBox.TabIndex = 3; 79 | // 80 | // ApplyButton 81 | // 82 | this.ApplyButton.Location = new System.Drawing.Point(277, 108); 83 | this.ApplyButton.Name = "ApplyButton"; 84 | this.ApplyButton.Size = new System.Drawing.Size(75, 23); 85 | this.ApplyButton.TabIndex = 12; 86 | this.ApplyButton.Text = "&Apply"; 87 | this.ApplyButton.UseVisualStyleBackColor = true; 88 | this.ApplyButton.Click += new System.EventHandler(this.ApplyButton_Click); 89 | // 90 | // cancelButton 91 | // 92 | this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; 93 | this.cancelButton.Location = new System.Drawing.Point(196, 108); 94 | this.cancelButton.Name = "cancelButton"; 95 | this.cancelButton.Size = new System.Drawing.Size(75, 23); 96 | this.cancelButton.TabIndex = 11; 97 | this.cancelButton.Text = "Cancel"; 98 | this.cancelButton.UseVisualStyleBackColor = true; 99 | // 100 | // OkButton 101 | // 102 | this.OkButton.DialogResult = System.Windows.Forms.DialogResult.OK; 103 | this.OkButton.Location = new System.Drawing.Point(115, 108); 104 | this.OkButton.Name = "OkButton"; 105 | this.OkButton.Size = new System.Drawing.Size(75, 23); 106 | this.OkButton.TabIndex = 10; 107 | this.OkButton.Text = "OK"; 108 | this.OkButton.UseVisualStyleBackColor = true; 109 | this.OkButton.Click += new System.EventHandler(this.OkButton_Click); 110 | // 111 | // lblFileSize 112 | // 113 | this.lblFileSize.AutoSize = true; 114 | this.lblFileSize.Location = new System.Drawing.Point(48, 56); 115 | this.lblFileSize.Name = "lblFileSize"; 116 | this.lblFileSize.Size = new System.Drawing.Size(30, 13); 117 | this.lblFileSize.TabIndex = 6; 118 | this.lblFileSize.Text = "Size:"; 119 | // 120 | // fileSizeLabel 121 | // 122 | this.fileSizeLabel.AutoSize = true; 123 | this.fileSizeLabel.Location = new System.Drawing.Point(81, 56); 124 | this.fileSizeLabel.Name = "fileSizeLabel"; 125 | this.fileSizeLabel.Size = new System.Drawing.Size(40, 13); 126 | this.fileSizeLabel.TabIndex = 7; 127 | this.fileSizeLabel.Text = "fileSize"; 128 | // 129 | // fileAddressLabel 130 | // 131 | this.fileAddressLabel.AutoSize = true; 132 | this.fileAddressLabel.Location = new System.Drawing.Point(81, 77); 133 | this.fileAddressLabel.Name = "fileAddressLabel"; 134 | this.fileAddressLabel.Size = new System.Drawing.Size(58, 13); 135 | this.fileAddressLabel.TabIndex = 9; 136 | this.fileAddressLabel.Text = "fileAddress"; 137 | // 138 | // lblFileAddress 139 | // 140 | this.lblFileAddress.AutoSize = true; 141 | this.lblFileAddress.Location = new System.Drawing.Point(30, 77); 142 | this.lblFileAddress.Name = "lblFileAddress"; 143 | this.lblFileAddress.Size = new System.Drawing.Size(48, 13); 144 | this.lblFileAddress.TabIndex = 8; 145 | this.lblFileAddress.Text = "Address:"; 146 | // 147 | // lblFileLetter 148 | // 149 | this.lblFileLetter.AutoSize = true; 150 | this.lblFileLetter.Location = new System.Drawing.Point(154, 32); 151 | this.lblFileLetter.Name = "lblFileLetter"; 152 | this.lblFileLetter.Size = new System.Drawing.Size(56, 13); 153 | this.lblFileLetter.TabIndex = 4; 154 | this.lblFileLetter.Text = "File &Letter:"; 155 | // 156 | // fileLetterTextBox 157 | // 158 | this.fileLetterTextBox.Location = new System.Drawing.Point(213, 29); 159 | this.fileLetterTextBox.MaxLength = 1; 160 | this.fileLetterTextBox.Name = "fileLetterTextBox"; 161 | this.fileLetterTextBox.Size = new System.Drawing.Size(37, 20); 162 | this.fileLetterTextBox.TabIndex = 5; 163 | // 164 | // PropertiesForm 165 | // 166 | this.AcceptButton = this.OkButton; 167 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 168 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 169 | this.CancelButton = this.cancelButton; 170 | this.ClientSize = new System.Drawing.Size(359, 139); 171 | this.Controls.Add(this.fileLetterTextBox); 172 | this.Controls.Add(this.lblFileLetter); 173 | this.Controls.Add(this.fileAddressLabel); 174 | this.Controls.Add(this.lblFileAddress); 175 | this.Controls.Add(this.fileSizeLabel); 176 | this.Controls.Add(this.lblFileSize); 177 | this.Controls.Add(this.OkButton); 178 | this.Controls.Add(this.cancelButton); 179 | this.Controls.Add(this.ApplyButton); 180 | this.Controls.Add(this.FileNumberTextBox); 181 | this.Controls.Add(this.lblFileNumber); 182 | this.Controls.Add(this.FileNameTextBox); 183 | this.Controls.Add(this.lblFileName); 184 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 185 | this.MaximizeBox = false; 186 | this.MinimizeBox = false; 187 | this.Name = "PropertiesForm"; 188 | this.Text = "File Properties"; 189 | this.Load += new System.EventHandler(this.PropertiesForm_Load); 190 | this.ResumeLayout(false); 191 | this.PerformLayout(); 192 | 193 | } 194 | 195 | #endregion 196 | 197 | private System.Windows.Forms.Label lblFileName; 198 | private System.Windows.Forms.TextBox FileNameTextBox; 199 | private System.Windows.Forms.Label lblFileNumber; 200 | private System.Windows.Forms.TextBox FileNumberTextBox; 201 | private System.Windows.Forms.Button ApplyButton; 202 | private System.Windows.Forms.Button cancelButton; 203 | private System.Windows.Forms.Button OkButton; 204 | private System.Windows.Forms.Label lblFileSize; 205 | private System.Windows.Forms.Label fileSizeLabel; 206 | private System.Windows.Forms.Label fileAddressLabel; 207 | private System.Windows.Forms.Label lblFileAddress; 208 | private System.Windows.Forms.Label lblFileLetter; 209 | private System.Windows.Forms.TextBox fileLetterTextBox; 210 | 211 | } 212 | } -------------------------------------------------------------------------------- /ALDExplorer/PropertiesForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.IO; 8 | using System.Text; 9 | using System.Windows.Forms; 10 | 11 | namespace ALDExplorer 12 | { 13 | public partial class PropertiesForm : Form 14 | { 15 | public bool Dirty; 16 | 17 | AldFileEntry _fileEntry; 18 | public AldFileEntry FileEntry 19 | { 20 | get 21 | { 22 | return _fileEntry; 23 | } 24 | set 25 | { 26 | _fileEntry = value; 27 | ReadFields(); 28 | } 29 | } 30 | 31 | private int FileLetter 32 | { 33 | get 34 | { 35 | int fileLetter = 1; 36 | string fileLetterString = fileLetterTextBox.Text.ToUpperInvariant(); 37 | 38 | if (fileLetterString.Length > 0) 39 | { 40 | char l = fileLetterString[0]; 41 | if (l >= 'A' && l <= 'Z') 42 | { 43 | fileLetter = l - 'A' + 1; 44 | } 45 | if (l == '@') 46 | { 47 | fileLetter = 0; 48 | } 49 | } 50 | return fileLetter; 51 | } 52 | set 53 | { 54 | this.fileLetterTextBox.Text = ((value == 0) ? "@" : (((char)(value - 1 + 'A' )).ToString())); 55 | } 56 | } 57 | 58 | private void ReadFields() 59 | { 60 | if (FileEntry == null) 61 | { 62 | this.FileNameTextBox.Text = ""; 63 | this.FileNumberTextBox.Text = ""; 64 | this.fileLetterTextBox.Text = ""; 65 | this.fileSizeLabel.Text = ""; 66 | this.fileAddressLabel.Text = ""; 67 | 68 | //this.FileTypeComboBox.Text = ""; 69 | } 70 | else 71 | { 72 | this.FileNameTextBox.Text = FileEntry.FileName; 73 | this.FileNumberTextBox.Text = FileEntry.FileNumber.ToString(); 74 | this.FileLetter = FileEntry.FileLetter; 75 | this.fileSizeLabel.Text = FileEntry.FileSize.ToString(); 76 | this.fileAddressLabel.Text = FileEntry.FileAddress.ToString("X"); 77 | 78 | var parent = FileEntry.Parent; 79 | if (parent != null) 80 | { 81 | var fileType = parent.FileType; 82 | if (fileType == AldFileType.AFA1File || fileType == AldFileType.AFA2File) 83 | { 84 | this.lblFileLetter.Visible = false; 85 | this.lblFileNumber.Visible = false; 86 | this.fileLetterTextBox.Visible = false; 87 | this.FileNumberTextBox.Visible = false; 88 | } 89 | } 90 | //this.FileTypeComboBox.Text = FileEntry.FileType.ToString(); 91 | } 92 | } 93 | 94 | public PropertiesForm() 95 | { 96 | InitializeComponent(); 97 | } 98 | 99 | private void OkButton_Click(object sender, EventArgs e) 100 | { 101 | Apply(); 102 | } 103 | 104 | private void ApplyButton_Click(object sender, EventArgs e) 105 | { 106 | Apply(); 107 | } 108 | 109 | private void Apply() 110 | { 111 | this.Dirty = true; 112 | this.FileEntry.FileName = FileNameTextBox.Text; 113 | int fileNumber = 0; 114 | int.TryParse(FileNumberTextBox.Text, out fileNumber); 115 | this.FileEntry.FileNumber = fileNumber; 116 | this.FileEntry.FileLetter = this.FileLetter; 117 | 118 | //FileType fileType = (FileType)0; 119 | //try 120 | //{ 121 | // fileType = (FileType)Enum.Parse(typeof(FileType), this.FileTypeComboBox.Text, true); 122 | //} 123 | //catch (ArgumentException) 124 | //{ 125 | // int fileTypeInt = 0; 126 | // int.TryParse(this.FileTypeComboBox.Text, out fileTypeInt); 127 | // fileType = (FileType)fileTypeInt; 128 | //} 129 | //this.FileEntry.FileType = fileType; 130 | } 131 | 132 | private void PropertiesForm_Load(object sender, EventArgs e) 133 | { 134 | //var names = Enum.GetNames(typeof(FileType)); 135 | //FileTypeComboBox.BeginUpdate(); 136 | //foreach (var name in names) 137 | //{ 138 | // FileTypeComboBox.Items.Add(name); 139 | //} 140 | //FileTypeComboBox.EndUpdate(); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /ALDExplorer/PropertiesForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /ALDExplorer/Resources/alicefile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/alicefile.png -------------------------------------------------------------------------------- /ALDExplorer/Resources/alicefile2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/alicefile2.png -------------------------------------------------------------------------------- /ALDExplorer/Resources/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/folder.png -------------------------------------------------------------------------------- /ALDExplorer/Resources/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/icon.ico -------------------------------------------------------------------------------- /ALDExplorer/Resources/minusicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/minusicon.png -------------------------------------------------------------------------------- /ALDExplorer/Resources/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/music.png -------------------------------------------------------------------------------- /ALDExplorer/Resources/plusicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/plusicon.png -------------------------------------------------------------------------------- /ALDExplorer/Resources/script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/Resources/script.png -------------------------------------------------------------------------------- /ALDExplorer/TempFileManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Text; 6 | using System.Windows.Forms; 7 | using System.Diagnostics; 8 | 9 | namespace ALDExplorer 10 | { 11 | class TempFileManager 12 | { 13 | List myTempFiles = new List(); 14 | 15 | public static bool DefaultInstanceCreated 16 | { 17 | get 18 | { 19 | return _defaultInstance != null; 20 | } 21 | } 22 | 23 | static TempFileManager _defaultInstance = null; 24 | public static TempFileManager DefaultInstance 25 | { 26 | get 27 | { 28 | if (_defaultInstance == null) 29 | { 30 | _defaultInstance = new TempFileManager(); 31 | } 32 | return _defaultInstance; 33 | } 34 | } 35 | 36 | public TempFileManager() 37 | { 38 | Create(); 39 | 40 | } 41 | 42 | static int GetLastNumberInString(string fileName, out int numberIndex, out int numberLength) 43 | { 44 | numberIndex = fileName.Length; 45 | numberLength = 0; 46 | for (int i = fileName.Length - 1; i >= 0; i--) 47 | { 48 | char c = fileName[i]; 49 | if (c >= '0' && c <= '9') 50 | { 51 | numberIndex = i; 52 | numberLength++; 53 | } 54 | else 55 | { 56 | if (numberLength > 0) 57 | { 58 | break; 59 | } 60 | } 61 | } 62 | 63 | int number = 0; 64 | if (numberIndex >= 0) 65 | { 66 | string substr = fileName.Substring(numberIndex, numberLength); 67 | if (int.TryParse(substr, out number)) 68 | { 69 | 70 | } 71 | else 72 | { 73 | number = 0; 74 | } 75 | } 76 | return number; 77 | } 78 | 79 | static int GetLongestNumberFromString(string fileName, out int numberIndex, out int numberLength) 80 | { 81 | int maxIndex = -1; 82 | int maxLength = 0; 83 | 84 | int currentIndex = -1; 85 | int currentLength = 0; 86 | //find the longest number in the filename 87 | for (int i = 0; i < fileName.Length; i++) 88 | { 89 | char c = fileName[i]; 90 | if (c >= '0' && c <= '9') 91 | { 92 | if (currentLength == 0) 93 | { 94 | currentIndex = i; 95 | currentLength = 1; 96 | } 97 | else 98 | { 99 | currentLength++; 100 | } 101 | if (currentLength > maxLength) 102 | { 103 | maxIndex = currentIndex; 104 | maxLength = currentLength; 105 | } 106 | } 107 | else 108 | { 109 | currentIndex = -1; 110 | currentLength = 0; 111 | } 112 | } 113 | 114 | int number = 0; 115 | if (maxIndex >= 0) 116 | { 117 | string substr = fileName.Substring(maxIndex, maxLength); 118 | if (int.TryParse(substr, out number)) 119 | { 120 | 121 | } 122 | else 123 | { 124 | number = 0; 125 | } 126 | } 127 | numberIndex = maxIndex; 128 | numberLength = maxLength; 129 | if (numberIndex == -1) 130 | { 131 | numberIndex = fileName.Length; 132 | } 133 | return number; 134 | } 135 | 136 | static string IncrementFileName(string fileName) 137 | { 138 | int number; 139 | int numberIndex; 140 | int numberLength; 141 | number = GetLastNumberInString(fileName, out numberIndex, out numberLength); 142 | number++; 143 | 144 | string beforeNumber = fileName.Substring(0, numberIndex); 145 | string afterNumber = fileName.Substring(numberIndex + numberLength); 146 | string newNumber = number.ToString("".PadLeft(numberLength, '0')); 147 | return beforeNumber + newNumber + afterNumber; 148 | } 149 | 150 | public void Destroy() 151 | { 152 | this.DeleteMyTempFiles(); 153 | if (!AnyOtherInstancesRunning()) 154 | { 155 | Directory.Delete(this.TempDirectory, true); 156 | } 157 | } 158 | 159 | public string TempDirectory; 160 | 161 | private void Create() 162 | { 163 | string appName = Application.ProductName; 164 | string tempDirectory = Path.GetTempPath(); 165 | string appTempDirectory = Path.Combine(tempDirectory, appName); 166 | if (File.Exists(appTempDirectory)) 167 | { 168 | appName = appName + ".000"; 169 | do 170 | { 171 | appName = IncrementFileName(appName); 172 | appTempDirectory = Path.Combine(tempDirectory, appName); 173 | } while (File.Exists(appTempDirectory)); 174 | } 175 | if (!Directory.Exists(appTempDirectory)) 176 | { 177 | Directory.CreateDirectory(appTempDirectory); 178 | } 179 | this.TempDirectory = appTempDirectory; 180 | 181 | bool anyOtherRunning = AnyOtherInstancesRunning(); 182 | if (!anyOtherRunning) 183 | { 184 | if (Directory.GetFiles(appTempDirectory, "*", SearchOption.AllDirectories).Length > 0) 185 | { 186 | Directory.Delete(appTempDirectory, true); 187 | Directory.CreateDirectory(appTempDirectory); 188 | } 189 | } 190 | } 191 | 192 | public string CreateTempFile(string fileName) 193 | { 194 | string newBaseName = Path.GetFileName(fileName); 195 | newBaseName = ValidCharsOnly(newBaseName); 196 | if (newBaseName.Length == 0) 197 | { 198 | newBaseName = "temp"; 199 | } 200 | string newFileName = Path.Combine(this.TempDirectory, newBaseName); 201 | if (File.Exists(newFileName)) 202 | { 203 | newBaseName = Path.GetFileNameWithoutExtension(newBaseName); 204 | newBaseName = newBaseName + "_1"; 205 | newBaseName = Path.ChangeExtension(newBaseName, Path.GetExtension(fileName)); 206 | newFileName = Path.Combine(this.TempDirectory, newBaseName); 207 | while (File.Exists(newFileName)) 208 | { 209 | newBaseName = IncrementFileName(newBaseName); 210 | newFileName = Path.Combine(this.TempDirectory, newBaseName); 211 | } 212 | } 213 | File.WriteAllBytes(newFileName, new byte[0]); 214 | this.myTempFiles.Add(newFileName); 215 | return newFileName; 216 | } 217 | 218 | static HashSet invalidChars = new HashSet(Path.GetInvalidFileNameChars()); 219 | 220 | private static string ValidCharsOnly(string newBaseName) 221 | { 222 | StringBuilder sb = new StringBuilder(newBaseName.Length); 223 | foreach (var c in newBaseName) 224 | { 225 | if (!invalidChars.Contains(c)) 226 | { 227 | sb.Append(c); 228 | } 229 | } 230 | string fileName = sb.ToString(); 231 | return sb.ToString(); 232 | } 233 | 234 | public void DeleteMyTempFiles() 235 | { 236 | if (this.myTempFiles.Count > 0) 237 | { 238 | foreach (var fileName in this.myTempFiles) 239 | { 240 | if (File.Exists(fileName)) 241 | { 242 | File.Delete(fileName); 243 | } 244 | } 245 | this.myTempFiles.Clear(); 246 | } 247 | } 248 | 249 | private static bool AnyOtherInstancesRunning() 250 | { 251 | //any other instances running? 252 | int myPid; 253 | string myProcessName; 254 | using (var myProcess = Process.GetCurrentProcess()) 255 | { 256 | myPid = myProcess.Id; 257 | myProcessName = myProcess.ProcessName; 258 | } 259 | bool anyOtherRunning = false; 260 | using (var processes = new DisposableCollection(Process.GetProcessesByName(myProcessName))) 261 | { 262 | if (processes.Count > 1) 263 | { 264 | anyOtherRunning = true; 265 | } 266 | } 267 | return anyOtherRunning; 268 | } 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /ALDExplorer/Utility.cs: -------------------------------------------------------------------------------- 1 | //! \file Utility.cs 2 | //! \date Sat Jul 05 02:47:33 2014 3 | //! \brief utility classes for GameRes assembly. 4 | // 5 | // Copyright (C) 2014 by morkt 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to 9 | // deal in the Software without restriction, including without limitation the 10 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | // sell copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | // IN THE SOFTWARE. 24 | // 25 | 26 | using System.Collections.Generic; 27 | using System.Text; 28 | 29 | namespace ALDExplorer.Utility 30 | { 31 | public static class Binary 32 | { 33 | public static uint BigEndian (uint u) 34 | { 35 | return u << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | u >> 24; 36 | } 37 | public static int BigEndian (int i) 38 | { 39 | return (int)BigEndian ((uint)i); 40 | } 41 | public static ushort BigEndian (ushort u) 42 | { 43 | return (ushort)(u << 8 | u >> 8); 44 | } 45 | public static short BigEndian (short i) 46 | { 47 | return (short)BigEndian ((ushort)i); 48 | } 49 | public static ulong BigEndian (ulong u) 50 | { 51 | return (ulong)BigEndian((uint)(u & 0xffffffff)) << 32 52 | | (ulong)BigEndian((uint)(u >> 32)); 53 | } 54 | public static long BigEndian (long i) 55 | { 56 | return (long)BigEndian ((ulong)i); 57 | } 58 | 59 | public static bool AsciiEqual (byte[] name1, string name2) 60 | { 61 | return AsciiEqual (name1, 0, name2); 62 | } 63 | 64 | public static bool AsciiEqual(this TArray arr, int index, string str) where TArray : IList 65 | { 66 | if (arr.Count - index < str.Length) 67 | return false; 68 | for (int i = 0; i < str.Length; ++i) 69 | if ((char)arr[index + i] != str[i]) 70 | return false; 71 | return true; 72 | } 73 | 74 | /// 75 | /// Check if sequence of ASCII characters in array is equal to string . 76 | /// This methods avoids costly construction of the string object from byte array for a mere purpose of 77 | /// comparison. 78 | /// 79 | public static bool AsciiEqual (byte[] name1, int offset, string name2) 80 | { 81 | return name1.AsciiEqual (offset, name2); 82 | } 83 | 84 | /// 85 | /// Copy potentially overlapping sequence of bytes in array 86 | /// from to . 87 | /// If destination offset resides within source region then sequence will repeat itself. Widely used 88 | /// in various compression techniques. 89 | /// 90 | public static void CopyOverlapped (byte[] data, int src, int dst, int count) 91 | { 92 | if (dst > src) 93 | { 94 | while (count > 0) 95 | { 96 | int preceding = System.Math.Min (dst - src, count); 97 | System.Buffer.BlockCopy (data, src, data, dst, preceding); 98 | dst += preceding; 99 | count -= preceding; 100 | } 101 | } 102 | else 103 | { 104 | System.Buffer.BlockCopy (data, src, data, dst, count); 105 | } 106 | } 107 | 108 | /// 109 | /// Extract null-terminated string (a "C string") from array starting 110 | /// at offset up to bytes long, stored in 111 | /// encoding . 112 | /// 113 | public static string GetCString (byte[] data, int index, int length_limit, Encoding enc) 114 | { 115 | int name_length = 0; 116 | while (name_length < length_limit && 0 != data[index+name_length]) 117 | name_length++; 118 | return enc.GetString (data, index, name_length); 119 | } 120 | 121 | public static string GetCString (byte[] data, int index, int length_limit) 122 | { 123 | return GetCString (data, index, length_limit, Encoding.GetEncoding(932)); 124 | } 125 | 126 | public static string GetCString (byte[] data, int index) 127 | { 128 | return GetCString (data, index, data.Length - index, Encoding.GetEncoding(932)); 129 | } 130 | 131 | public static uint RotR (uint v, int count) 132 | { 133 | count &= 0x1F; 134 | return v >> count | v << (32-count); 135 | } 136 | 137 | public static uint RotL (uint v, int count) 138 | { 139 | count &= 0x1F; 140 | return v << count | v >> (32-count); 141 | } 142 | 143 | public static ulong RotR (ulong v, int count) 144 | { 145 | count &= 0x3F; 146 | return v >> count | v << (64-count); 147 | } 148 | 149 | public static ulong RotL (ulong v, int count) 150 | { 151 | count &= 0x3F; 152 | return v << count | v >> (64-count); 153 | } 154 | 155 | public static byte RotByteR (byte v, int count) 156 | { 157 | count &= 7; 158 | return (byte)(v >> count | v << (8-count)); 159 | } 160 | 161 | public static byte RotByteL (byte v, int count) 162 | { 163 | count &= 7; 164 | return (byte)(v << count | v >> (8-count)); 165 | } 166 | } 167 | 168 | public static class BigEndian 169 | { 170 | public static ushort ToUInt16 (TArray value, int index) where TArray : IList 171 | { 172 | return (ushort)(value[index] << 8 | value[index+1]); 173 | } 174 | 175 | public static short ToInt16 (TArray value, int index) where TArray : IList 176 | { 177 | return (short)(value[index] << 8 | value[index+1]); 178 | } 179 | 180 | public static uint ToUInt32 (TArray value, int index) where TArray : IList 181 | { 182 | return (uint)(value[index] << 24 | value[index+1] << 16 | value[index+2] << 8 | value[index+3]); 183 | } 184 | 185 | public static int ToInt32 (TArray value, int index) where TArray : IList 186 | { 187 | return (int)ToUInt32 (value, index); 188 | } 189 | 190 | public static void Pack (ushort value, byte[] buf, int index) 191 | { 192 | buf[index] = (byte)(value >> 8); 193 | buf[index+1] = (byte)(value); 194 | } 195 | 196 | public static void Pack (uint value, byte[] buf, int index) 197 | { 198 | buf[index] = (byte)(value >> 24); 199 | buf[index+1] = (byte)(value >> 16); 200 | buf[index+2] = (byte)(value >> 8); 201 | buf[index+3] = (byte)(value); 202 | } 203 | 204 | public static void Pack (ulong value, byte[] buf, int index) 205 | { 206 | Pack ((uint)(value >> 32), buf, index); 207 | Pack ((uint)value, buf, index+4); 208 | } 209 | 210 | public static void Pack (short value, byte[] buf, int index) 211 | { 212 | Pack ((ushort)value, buf, index); 213 | } 214 | 215 | public static void Pack (int value, byte[] buf, int index) 216 | { 217 | Pack ((uint)value, buf, index); 218 | } 219 | 220 | public static void Pack (long value, byte[] buf, int index) 221 | { 222 | Pack ((ulong)value, buf, index); 223 | } 224 | } 225 | 226 | public static class LittleEndian 227 | { 228 | public static ushort ToUInt16 (TArray value, int index) where TArray : IList 229 | { 230 | return (ushort)(value[index] | value[index+1] << 8); 231 | } 232 | 233 | public static short ToInt16 (TArray value, int index) where TArray : IList 234 | { 235 | return (short)(value[index] | value[index+1] << 8); 236 | } 237 | 238 | public static uint ToUInt32 (TArray value, int index) where TArray : IList 239 | { 240 | return (uint)(value[index] | value[index+1] << 8 | value[index+2] << 16 | value[index+3] << 24); 241 | } 242 | 243 | public static int ToInt32 (TArray value, int index) where TArray : IList 244 | { 245 | return (int)ToUInt32 (value, index); 246 | } 247 | 248 | public static ulong ToUInt64 (TArray value, int index) where TArray : IList 249 | { 250 | return (ulong)ToUInt32 (value, index) | ((ulong)ToUInt32 (value, index+4) << 32); 251 | } 252 | 253 | public static long ToInt64 (TArray value, int index) where TArray : IList 254 | { 255 | return (long)ToUInt64 (value, index); 256 | } 257 | 258 | public static void Pack (ushort value, byte[] buf, int index) 259 | { 260 | buf[index] = (byte)(value); 261 | buf[index+1] = (byte)(value >> 8); 262 | } 263 | 264 | public static void Pack (uint value, byte[] buf, int index) 265 | { 266 | buf[index] = (byte)(value); 267 | buf[index+1] = (byte)(value >> 8); 268 | buf[index+2] = (byte)(value >> 16); 269 | buf[index+3] = (byte)(value >> 24); 270 | } 271 | 272 | public static void Pack (ulong value, byte[] buf, int index) 273 | { 274 | Pack ((uint)value, buf, index); 275 | Pack ((uint)(value >> 32), buf, index+4); 276 | } 277 | 278 | public static void Pack (short value, byte[] buf, int index) 279 | { 280 | Pack ((ushort)value, buf, index); 281 | } 282 | 283 | public static void Pack (int value, byte[] buf, int index) 284 | { 285 | Pack ((uint)value, buf, index); 286 | } 287 | 288 | public static void Pack (long value, byte[] buf, int index) 289 | { 290 | Pack ((ulong)value, buf, index); 291 | } 292 | } 293 | 294 | public class AsciiString 295 | { 296 | public byte[] Value { get; set; } 297 | public int Length { get { return Value.Length; } } 298 | 299 | public AsciiString (int size) 300 | { 301 | Value = new byte[size]; 302 | } 303 | 304 | public AsciiString (byte[] str) 305 | { 306 | Value = str; 307 | } 308 | 309 | public AsciiString (string str) 310 | { 311 | Value = Encoding.ASCII.GetBytes (str); 312 | } 313 | 314 | public override string ToString () 315 | { 316 | return Encoding.ASCII.GetString (Value); 317 | } 318 | 319 | public override bool Equals (object o) 320 | { 321 | if (null == o) 322 | return false; 323 | var a = o as AsciiString; 324 | if (null == (object)a) 325 | return false; 326 | return this == a; 327 | } 328 | 329 | public override int GetHashCode () 330 | { 331 | int hash = 5381; 332 | for (int i = 0; i < Value.Length; ++i) 333 | { 334 | hash = ((hash << 5) + hash) ^ Value[i]; 335 | } 336 | return hash ^ (hash * 1566083941);; 337 | } 338 | 339 | public static bool operator== (AsciiString a, AsciiString b) 340 | { 341 | if (ReferenceEquals (a, b)) 342 | return true; 343 | if (null == (object)a || null == (object)b) 344 | return false; 345 | if (a.Length != b.Length) 346 | return false; 347 | for (int i = 0; i < a.Length; ++i) 348 | if (a.Value[i] != b.Value[i]) 349 | return false; 350 | return true; 351 | } 352 | 353 | public static bool operator!= (AsciiString a, AsciiString b) 354 | { 355 | return !(a == b); 356 | } 357 | 358 | public static bool operator== (AsciiString a, string b) 359 | { 360 | return Binary.AsciiEqual (a.Value, b); 361 | } 362 | 363 | public static bool operator!= (AsciiString a, string b) 364 | { 365 | return !(a == b); 366 | } 367 | 368 | public static bool operator== (string a, AsciiString b) 369 | { 370 | return b == a; 371 | } 372 | 373 | public static bool operator!= (string a, AsciiString b) 374 | { 375 | return !(b == a); 376 | } 377 | } 378 | 379 | public interface IDataUnpacker 380 | { 381 | byte[] Data { get; } 382 | void Unpack (); 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /ALDExplorer/VSPFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FreeImageAPI; 4 | using System.Drawing; 5 | 6 | namespace ALDExplorer.Formats 7 | { 8 | public class VspHeader : ICloneable 9 | { 10 | public int xLocation; 11 | public int yLocation; 12 | public int width; 13 | public int height; 14 | public int paletteBank; 15 | public int is8Bit; 16 | public int unknownA, unknownC, unknown10, unknown14, unknown18, unknown1C; 17 | 18 | 19 | public VspHeader() 20 | { 21 | 22 | } 23 | 24 | public string GetComment() 25 | { 26 | return ImageConverter.GetComment(this); 27 | } 28 | 29 | public bool ParseComment(string comment) 30 | { 31 | return ImageConverter.ParseComment(this, comment); 32 | } 33 | 34 | public PmsHeader ToPmsHeader() 35 | { 36 | var pmsHeader = new PmsHeader(); 37 | pmsHeader.addressOfData = 0x320; 38 | pmsHeader.addressOfPalette = 0x20; 39 | pmsHeader.colorDepth = 8; 40 | pmsHeader.headerSize = 0x20; 41 | pmsHeader.paletteBank = this.paletteBank; 42 | pmsHeader.height = this.height; 43 | pmsHeader.width = ((this.width + 7) / 8) * 8; 44 | pmsHeader.xLocation = this.xLocation; 45 | pmsHeader.yLocation = this.yLocation; 46 | return pmsHeader; 47 | } 48 | 49 | public VspHeader Clone() 50 | { 51 | return (VspHeader)this.MemberwiseClone(); 52 | } 53 | 54 | #region ICloneable Members 55 | 56 | object ICloneable.Clone() 57 | { 58 | return Clone(); 59 | } 60 | 61 | #endregion 62 | } 63 | 64 | static class Vsp 65 | { 66 | public static FreeImageBitmap LoadImage(byte[] bytes) 67 | { 68 | var vspHeader = GetHeader(bytes); 69 | if (vspHeader.width > 0 && ((vspHeader.is8Bit == 0) ? vspHeader.width : ((vspHeader.width + 7) / 8)) <= 80 && vspHeader.height > 0 && vspHeader.height <= 480) 70 | { 71 | } 72 | else 73 | { 74 | return null; 75 | } 76 | if (vspHeader.is8Bit == 1) 77 | { 78 | var pmsHeader = vspHeader.ToPmsHeader(); 79 | var imageData = Pms.GetImageData8Bit(pmsHeader, bytes); 80 | FreeImageBitmap bitmap = new FreeImageBitmap(pmsHeader.width, pmsHeader.height, pmsHeader.width, 8, FREE_IMAGE_TYPE.FIT_BITMAP, imageData); 81 | Pms.GetPalette(bitmap.Palette, pmsHeader, bytes); 82 | bitmap.Comment = vspHeader.GetComment(); 83 | bitmap.Tag = vspHeader; 84 | return bitmap; 85 | } 86 | else 87 | { 88 | var imageData = GetImage(vspHeader, bytes); 89 | FreeImageBitmap bitmap = new FreeImageBitmap(vspHeader.width * 8, vspHeader.height, vspHeader.width * 8, 8, FREE_IMAGE_TYPE.FIT_BITMAP, imageData); 90 | GetPalette(bitmap.Palette, bytes, vspHeader); 91 | bitmap.Comment = vspHeader.GetComment(); 92 | bitmap.Tag = vspHeader; 93 | return bitmap; 94 | } 95 | } 96 | 97 | public static void SaveImage(Stream stream, FreeImageBitmap bitmap) 98 | { 99 | string comment = bitmap.Comment; 100 | var vspHeader = new VspHeader(); 101 | if (!String.IsNullOrEmpty(comment)) 102 | { 103 | vspHeader.ParseComment(comment); 104 | } 105 | if (bitmap.ColorDepth != 4 && bitmap.ColorDepth != 8) 106 | { 107 | if (bitmap.ColorDepth > 8) 108 | { 109 | if (bitmap.ColorDepth == 32) 110 | { 111 | bitmap.ConvertColorDepth(FREE_IMAGE_COLOR_DEPTH.FICD_24_BPP); 112 | } 113 | if (vspHeader.is8Bit == 0) 114 | { 115 | bitmap.Quantize(FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, 16); 116 | } 117 | else 118 | { 119 | bitmap.Quantize(FREE_IMAGE_QUANTIZE.FIQ_WUQUANT, 256); 120 | } 121 | } 122 | //throw new ArgumentException("image must be 4-bit or 8-bit"); 123 | } 124 | if ((bitmap.Width & 7) != 0) 125 | { 126 | int slackPixels = (bitmap.Width & 7); 127 | int pixelsToAdd = 8 - slackPixels; 128 | bitmap.EnlargeCanvas(0, 0, pixelsToAdd, 0, new RGBQUAD(Color.Black)); 129 | } 130 | 131 | if (vspHeader.is8Bit == 0) 132 | { 133 | vspHeader.height = bitmap.Height; 134 | vspHeader.width = bitmap.Width / 8; 135 | SaveHeader(vspHeader, stream); 136 | SavePalette(bitmap.Palette, stream); 137 | SaveImageData(stream, bitmap); 138 | } 139 | else 140 | { 141 | vspHeader.height = bitmap.Height; 142 | if (vspHeader.width != bitmap.Width - 1) 143 | { 144 | 145 | } 146 | vspHeader.width = bitmap.Width - 1; 147 | SaveHeader(vspHeader, stream); 148 | var pmsHeader = vspHeader.ToPmsHeader(); 149 | Pms.SavePalette(stream, bitmap.Palette); 150 | Pms.SaveImageData8Bit(stream, bitmap); 151 | } 152 | } 153 | 154 | public static void SaveHeader(VspHeader vspHeader, Stream stream) 155 | { 156 | var bw = new BinaryWriter(stream); 157 | bw.Write((short)vspHeader.xLocation); 158 | bw.Write((short)vspHeader.yLocation); 159 | bw.Write((short)(vspHeader.width + vspHeader.xLocation)); 160 | bw.Write((short)(vspHeader.height + vspHeader.yLocation)); 161 | bw.Write((byte)vspHeader.is8Bit); 162 | bw.Write((byte)vspHeader.paletteBank); 163 | if (vspHeader.is8Bit == 1) 164 | { 165 | bw.Write((ushort)vspHeader.unknownA); 166 | bw.Write(vspHeader.unknownC); 167 | 168 | bw.Write(vspHeader.unknown10); 169 | bw.Write(vspHeader.unknown14); 170 | bw.Write(vspHeader.unknown18); 171 | bw.Write(vspHeader.unknown1C); 172 | } 173 | } 174 | 175 | public static void SavePalette(Palette palette, Stream stream) 176 | { 177 | var bw = new BinaryWriter(stream); 178 | for (int i = 0; i < 16; i++) 179 | { 180 | var color = palette[i]; 181 | int r = (color.rgbRed + 7) / 17; 182 | int g = (color.rgbGreen + 7) / 17; 183 | int b = (color.rgbBlue + 7) / 17; 184 | if (r > 15) r = 15; 185 | if (g > 15) g = 15; 186 | if (b > 15) b = 15; 187 | if (r < 0) r = 0; 188 | if (g < 0) g = 0; 189 | if (b < 0) b = 0; 190 | bw.Write((byte)b); 191 | bw.Write((byte)r); 192 | bw.Write((byte)g); 193 | } 194 | } 195 | 196 | public static VspHeader GetHeader(byte[] bytes) 197 | { 198 | VspHeader vspHeader = new VspHeader(); 199 | 200 | vspHeader.xLocation = BitConverter.ToInt16(bytes, 0); 201 | vspHeader.yLocation = BitConverter.ToInt16(bytes, 2); 202 | vspHeader.width = BitConverter.ToInt16(bytes, 4) - vspHeader.xLocation; 203 | vspHeader.height = BitConverter.ToInt16(bytes, 6) - vspHeader.yLocation; 204 | vspHeader.is8Bit = bytes[8]; 205 | vspHeader.paletteBank = bytes[9]; 206 | 207 | if (vspHeader.is8Bit != 0) 208 | { 209 | //vspHeader.width += 7; 210 | //vspHeader.width /= 8; 211 | 212 | vspHeader.unknownA = BitConverter.ToUInt16(bytes, 0x0A); 213 | vspHeader.unknownC = BitConverter.ToInt32(bytes, 0x0c); 214 | vspHeader.unknown10 = BitConverter.ToInt32(bytes, 0x10); 215 | vspHeader.unknown14 = BitConverter.ToInt32(bytes, 0x14); 216 | vspHeader.unknown18 = BitConverter.ToInt32(bytes, 0x18); 217 | vspHeader.unknown1C = BitConverter.ToInt32(bytes, 0x1c); 218 | 219 | } 220 | 221 | return vspHeader; 222 | } 223 | 224 | /* 225 | * Get palette from raw data 226 | * pal: palette to be stored 227 | * b : raw data (pointer to palette) 228 | */ 229 | public static void GetPalette(Palette palette, byte[] bytes, VspHeader vspHeader) 230 | { 231 | int red, green, blue, i; 232 | 233 | int address = 0x0A; 234 | for (i = 0; i < 16; i++) 235 | { 236 | blue = bytes[i * 3 + 0 + address]; 237 | red = bytes[i * 3 + 1 + address]; 238 | green = bytes[i * 3 + 2 + address]; 239 | 240 | red = (red & 0x0F) * 17; 241 | green = (green & 0x0F) * 17; 242 | blue = (blue & 0x0F) * 17; 243 | palette[i] = Color.FromArgb(red, green, blue); 244 | } 245 | } 246 | 247 | /* 248 | * Do extract vsp image 249 | * vsp: vsp header information 250 | * pic: pixel to be stored 251 | * b : raw data (pointer to pixel) 252 | */ 253 | 254 | static byte[][] _bp = new byte[][] { new byte[480], new byte[480], new byte[480], new byte[480] }; 255 | static byte[][] _bc = new byte[][] { new byte[480], new byte[480], new byte[480], new byte[480] }; 256 | 257 | static byte[][] bp = new byte[][] { _bp[0], _bp[1], _bp[2], _bp[3] }; 258 | static byte[][] bc = new byte[][] { _bc[0], _bc[1], _bc[2], _bc[3] }; 259 | 260 | public static byte[] GetImage(VspHeader vspHeader, byte[] bytes) 261 | { 262 | int address = 0x3A; 263 | 264 | byte[] pic = new byte[vspHeader.height * vspHeader.width * 8]; 265 | int c0; 266 | byte b0, b1, b2, b3, mask = 0; 267 | byte[] bt; 268 | int i, l, x, y, pl, loc; 269 | 270 | bp[0] = _bp[0]; bc[0] = _bc[0]; 271 | bp[1] = _bp[1]; bc[1] = _bc[1]; 272 | bp[2] = _bp[2]; bc[2] = _bc[2]; 273 | bp[3] = _bp[3]; bc[3] = _bc[3]; 274 | for (x = 0; x < vspHeader.width; x++) 275 | { 276 | for (pl = 0; pl < 4; pl++) 277 | { 278 | y = 0; 279 | while (y < vspHeader.height) 280 | { 281 | c0 = bytes[address++]; 282 | if (c0 >= 0x08) 283 | { 284 | //literal 285 | bc[pl][y] = (byte)c0; y++; 286 | } 287 | else if (c0 == 0x00) 288 | { 289 | //take L+1 bytes from previous column (same plane) 290 | l = bytes[address] + 1; address++; 291 | memcpy(bc[pl], bp[pl], y, l); 292 | y += l; 293 | } 294 | else if (c0 == 0x01) 295 | { 296 | //RLE run: L+1 bytes long 297 | l = bytes[address] + 1; address++; 298 | b0 = bytes[address++]; 299 | memset(bc[pl], y, b0, l); 300 | y += l; 301 | } 302 | else if (c0 == 0x02) 303 | { 304 | //Alternating RLE run: (L+1)*2 bytes long 305 | l = bytes[address] + 1; address++; 306 | b0 = bytes[address++]; b1 = bytes[address++]; 307 | for (i = 0; i < l; i++) 308 | { 309 | bc[pl][y] = b0; y++; 310 | bc[pl][y] = b1; y++; 311 | } 312 | } 313 | else if (c0 == 0x03) 314 | { 315 | //Copy from plane 0 xor Mask, set mask to 0 316 | l = bytes[address] + 1; address++; 317 | for (i = 0; i < l; i++) 318 | { 319 | bc[pl][y] = (byte)(bc[0][y] ^ mask); y++; 320 | } 321 | mask = 0; 322 | } 323 | else if (c0 == 0x04) 324 | { 325 | //Copy from plane 1 xor Mask, set mask to 0 326 | l = bytes[address] + 1; address++; 327 | for (i = 0; i < l; i++) 328 | { 329 | bc[pl][y] = (byte)(bc[1][y] ^ mask); y++; 330 | } 331 | mask = 0; 332 | } 333 | else if (c0 == 0x05) 334 | { 335 | //Copy from plane 2 xor Mask, set mask to 0 336 | l = bytes[address] + 1; address++; 337 | for (i = 0; i < l; i++) 338 | { 339 | bc[pl][y] = (byte)(bc[2][y] ^ mask); y++; 340 | } 341 | mask = 0; 342 | } 343 | else if (c0 == 0x06) 344 | { 345 | //set mask to FF 346 | mask = 0xff; 347 | } 348 | else if (c0 == 0x07) 349 | { 350 | //Escaped literal 351 | bc[pl][y] = bytes[address++]; y++; 352 | } 353 | } 354 | } 355 | /* conversion from plane -> packed bytes */ 356 | for (y = 0; y < vspHeader.height; y++) 357 | { 358 | loc = (y * vspHeader.width + x) * 8; 359 | b0 = bc[0][y]; b1 = bc[1][y]; 360 | b2 = bc[2][y]; b3 = bc[3][y]; 361 | pic[loc + 0] = (byte)(((b0 >> 7) & 0x01) | ((b1 >> 6) & 0x02) | ((b2 >> 5) & 0x04) | ((b3 >> 4) & 0x08)); 362 | pic[loc + 1] = (byte)(((b0 >> 6) & 0x01) | ((b1 >> 5) & 0x02) | ((b2 >> 4) & 0x04) | ((b3 >> 3) & 0x08)); 363 | pic[loc + 2] = (byte)(((b0 >> 5) & 0x01) | ((b1 >> 4) & 0x02) | ((b2 >> 3) & 0x04) | ((b3 >> 2) & 0x08)); 364 | pic[loc + 3] = (byte)(((b0 >> 4) & 0x01) | ((b1 >> 3) & 0x02) | ((b2 >> 2) & 0x04) | ((b3 >> 1) & 0x08)); 365 | pic[loc + 4] = (byte)(((b0 >> 3) & 0x01) | ((b1 >> 2) & 0x02) | ((b2 >> 1) & 0x04) | ((b3) & 0x08)); 366 | pic[loc + 5] = (byte)(((b0 >> 2) & 0x01) | ((b1 >> 1) & 0x02) | ((b2) & 0x04) | ((b3 << 1) & 0x08)); 367 | pic[loc + 6] = (byte)(((b0 >> 1) & 0x01) | ((b1) & 0x02) | ((b2 << 1) & 0x04) | ((b3 << 2) & 0x08)); 368 | pic[loc + 7] = (byte)(((b0) & 0x01) | ((b1 << 1) & 0x02) | ((b2 << 2) & 0x04) | ((b3 << 3) & 0x08)); 369 | } 370 | /* bc -> bp swap */ 371 | bt = bp[0]; bp[0] = bc[0]; bc[0] = bt; 372 | bt = bp[1]; bp[1] = bc[1]; bc[1] = bt; 373 | bt = bp[2]; bp[2] = bc[2]; bc[2] = bt; 374 | bt = bp[3]; bp[3] = bc[3]; bc[3] = bt; 375 | } 376 | return pic; 377 | } 378 | 379 | private static void memset(byte[] bytes, int y, byte b0, int l) 380 | { 381 | for (int i = 0; i < l; i++) 382 | { 383 | bytes[y + i] = b0; 384 | } 385 | } 386 | 387 | private static void memcpy(byte[] dest, byte[] src, int index, int length) 388 | { 389 | Array.Copy(src, index, dest, index, length); 390 | } 391 | 392 | public static void SaveImageData(Stream stream, FreeImageBitmap image) 393 | { 394 | byte[] imageData = new byte[image.Width * image.Height / 2]; 395 | int o = 0; 396 | for (int y = 0; y < image.Height; y++) 397 | { 398 | if (image.ColorDepth == 4) 399 | { 400 | var scanline = image.GetScanlineFromTop4Bit(y); 401 | for (int x = 0; x < image.Width; x += 2) 402 | { 403 | int p1 = scanline[x]; 404 | int p2 = scanline[x + 1]; 405 | imageData[o++] = (byte)((p1 << 4) + p2); 406 | } 407 | } 408 | else if (image.ColorDepth == 8) 409 | { 410 | var scanline = image.GetScanlineFromTop8Bit(y); 411 | for (int x = 0; x < image.Width; x += 2) 412 | { 413 | int p1 = scanline[x] & 0x0F; 414 | int p2 = scanline[x + 1] & 0x0F; 415 | imageData[o++] = (byte)((p1 << 4) + p2); 416 | } 417 | } 418 | } 419 | byte[] bytes = new byte[imageData.Length]; 420 | 421 | int[] lengths = new int[9]; 422 | 423 | TransformImage(bytes, imageData, image.Width, image.Height); 424 | int height = image.Height; 425 | //encode the bytes 426 | for (int i = 0; i < bytes.Length; i++) 427 | { 428 | int x = i / image.Height; 429 | int p = x & 3; 430 | 431 | Array.Clear(lengths, 0, lengths.Length); 432 | lengths[0] = MeasureRleRun(bytes, i, height); 433 | lengths[1] = MeasureRleRun2(bytes, i, height); 434 | lengths[2] = MeasurePreviousColumnRun(bytes, i, height, height * 4, 0); 435 | if (p >= 1) 436 | { 437 | lengths[3] = MeasurePreviousColumnRun(bytes, i, height, height * p, 0); 438 | lengths[6] = MeasurePreviousColumnRun(bytes, i, height, height * p, 255); 439 | } 440 | if (p >= 2) 441 | { 442 | lengths[4] = MeasurePreviousColumnRun(bytes, i, height, height * (p - 1), 0); 443 | lengths[7] = MeasurePreviousColumnRun(bytes, i, height, height * (p - 1), 255); 444 | } 445 | if (p >= 3) 446 | { 447 | lengths[5] = MeasurePreviousColumnRun(bytes, i, height, height * (p - 2), 0); 448 | lengths[8] = MeasurePreviousColumnRun(bytes, i, height, height * (p - 2), 255); 449 | } 450 | int maxIndex; 451 | int max = lengths.Max(out maxIndex); 452 | 453 | byte b = bytes[i]; 454 | if (max < 2) 455 | { 456 | maxIndex = -1; 457 | } 458 | else if (max == 2) 459 | { 460 | if (b >= 0x08) 461 | { 462 | maxIndex = -1; 463 | } 464 | else 465 | { 466 | if (maxIndex < 2 || maxIndex >= 6) 467 | { 468 | maxIndex = -1; 469 | } 470 | } 471 | } 472 | else if (max == 3) 473 | { 474 | //don't compress 3 bytes to 3 bytes unless first byte would be a literal 475 | if (b >= 0x08 && maxIndex < 2 || maxIndex >= 6) 476 | { 477 | maxIndex = -1; 478 | } 479 | } 480 | 481 | switch (maxIndex) 482 | { 483 | default: 484 | { 485 | //No compression - raw byte or escaped literal 486 | if (b >= 0x08) 487 | { 488 | //raw byte 489 | stream.WriteByte((byte)b); 490 | } 491 | else 492 | { 493 | //encode a literal as 2 bytes 494 | stream.WriteByte((byte)0x07); 495 | stream.WriteByte((byte)b); 496 | } 497 | i++; 498 | } 499 | break; 500 | case 0: 501 | { 502 | //RLE - 01 503 | stream.WriteByte(0x01); 504 | stream.WriteByte((byte)(max - 1)); 505 | stream.WriteByte(b); 506 | i += max; 507 | } 508 | break; 509 | case 1: 510 | { 511 | //Alternating RLE - 02 512 | stream.WriteByte(0x02); 513 | stream.WriteByte((byte)(max / 2 - 1)); 514 | stream.WriteByte(b); 515 | stream.WriteByte(bytes[i + 1]); 516 | i += max; 517 | } 518 | break; 519 | case 2: 520 | { 521 | //from previous column (same plane) - 00 522 | stream.WriteByte(0x00); 523 | stream.WriteByte((byte)(max - 1)); 524 | i += max; 525 | } 526 | break; 527 | case 3: 528 | { 529 | //from plane 0 without mask - 03 530 | stream.WriteByte(0x03); 531 | stream.WriteByte((byte)(max - 1)); 532 | i += max; 533 | } 534 | break; 535 | case 6: 536 | { 537 | //from plane 0 with mask - 06 03 538 | stream.WriteByte(0x06); 539 | stream.WriteByte(0x03); 540 | stream.WriteByte((byte)(max - 1)); 541 | i += max; 542 | } 543 | break; 544 | case 4: 545 | { 546 | //from plane 1 without mask - 04 547 | stream.WriteByte(0x04); 548 | stream.WriteByte((byte)(max - 1)); 549 | i += max; 550 | } 551 | break; 552 | case 7: 553 | { 554 | //from plane 1 with mask - 06 04 555 | stream.WriteByte(0x06); 556 | stream.WriteByte(0x04); 557 | stream.WriteByte((byte)(max - 1)); 558 | i += max; 559 | } 560 | break; 561 | case 5: 562 | { 563 | //from plane 2 without mask - 05 564 | stream.WriteByte(0x05); 565 | stream.WriteByte((byte)(max - 1)); 566 | i += max; 567 | } 568 | break; 569 | case 8: 570 | { 571 | //from plane 2 with mask - 06 05 572 | stream.WriteByte(0x06); 573 | stream.WriteByte(0x05); 574 | stream.WriteByte((byte)(max - 1)); 575 | i += max; 576 | } 577 | break; 578 | } 579 | i--; 580 | } 581 | } 582 | 583 | static int MeasureRleRun(byte[] pic, int i, int height) 584 | { 585 | int maxLength = 256; 586 | int y = i % height; 587 | if (maxLength > height - y) 588 | { 589 | maxLength = height - y; 590 | } 591 | 592 | byte b = pic[i]; 593 | int i0 = i; 594 | i++; 595 | while (i < pic.Length && pic[i] == b) 596 | { 597 | if (i - i0 >= maxLength) break; 598 | i++; 599 | } 600 | return i - i0; 601 | } 602 | 603 | static int MeasureRleRun2(byte[] pic, int i, int height) 604 | { 605 | int maxLength = 512; 606 | int y = i % height; 607 | if (maxLength > height - y) 608 | { 609 | maxLength = height - y; 610 | } 611 | 612 | int i0 = i; 613 | if (i + 1 >= pic.Length) return 0; 614 | if (i + 2 == pic.Length) return 2; 615 | if (maxLength < 4) return 0; 616 | byte b1 = pic[i]; 617 | byte b2 = pic[i + 1]; 618 | i += 2; 619 | while (i + 1 < pic.Length && pic[i] == b1 && pic[i + 1] == b2) 620 | { 621 | if (i - i0 + 1 >= maxLength) break; 622 | i += 2; 623 | } 624 | return i - i0; 625 | } 626 | 627 | static int MeasurePreviousColumnRun(byte[] pic, int i, int height, int offset, byte mask) 628 | { 629 | int maxLength = 256; 630 | int y = i % height; 631 | if (maxLength > height - y) 632 | { 633 | maxLength = height - y; 634 | } 635 | 636 | int i0 = i; 637 | if (i - offset < 0) 638 | { 639 | return 0; 640 | } 641 | while (i < pic.Length && pic[i] == (pic[i - offset] ^ mask)) 642 | { 643 | if (i - i0 >= maxLength) break; 644 | i++; 645 | } 646 | return i - i0; 647 | } 648 | 649 | static uint GetBigEndian(byte[] bytes, int index) 650 | { 651 | unchecked 652 | { 653 | return (uint)((bytes[index + 0] << 24) + (bytes[index + 1] << 16) + (bytes[index + 2] << 8) + bytes[index + 3]); 654 | } 655 | } 656 | 657 | static void TransformImage(byte[] dest, byte[] src, int w, int h) 658 | { 659 | unchecked 660 | { 661 | //transforms an image from standard linear 4 bit to column-major bitplanes 662 | int x0, y; 663 | 664 | byte plane0, plane1, plane2, plane3; 665 | 666 | for (x0 = 0; x0 < w; x0 += 8) 667 | { 668 | int destIndex = (x0 / 8) * h * 4; 669 | for (y = 0; y < h; y++) 670 | { 671 | uint sourcePixels = GetBigEndian(src, w * y / 2 + x0 / 2); 672 | plane0 = 0; 673 | plane1 = 0; 674 | plane2 = 0; 675 | plane3 = 0; 676 | for (int x2 = 0; x2 < 8; x2++) 677 | { 678 | uint nibble = (sourcePixels >> 28); 679 | int bitValue = 1 << (7 - x2); 680 | if (0 != (nibble & 1)) 681 | { 682 | plane0 |= (byte)bitValue; 683 | } 684 | if (0 != (nibble & 2)) 685 | { 686 | plane1 |= (byte)bitValue; 687 | } 688 | if (0 != (nibble & 4)) 689 | { 690 | plane2 |= (byte)bitValue; 691 | } 692 | if (0 != (nibble & 8)) 693 | { 694 | plane3 |= (byte)bitValue; 695 | } 696 | sourcePixels <<= 4; 697 | } 698 | dest[destIndex] = plane0; 699 | dest[destIndex + h * 1] = plane1; 700 | dest[destIndex + h * 2] = plane2; 701 | dest[destIndex + h * 3] = plane3; 702 | destIndex++; 703 | } 704 | } 705 | } 706 | } 707 | } 708 | 709 | } -------------------------------------------------------------------------------- /ALDExplorer/XCFFormat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Collections.Generic; 4 | 5 | namespace ALDExplorer.Formats 6 | { 7 | /* 8 | internal class DcfMetaData 9 | { 10 | public string BaseName; 11 | public long DataOffset; 12 | } 13 | 14 | 15 | public class DcfFormat 16 | { 17 | public override string Tag { get { return "DCF"; } } 18 | public override string Description { get { return "AliceSoft System incremental image"; } } 19 | public override uint Signature { get { return 0x20666364; } } // 'dcf ' 20 | 21 | public override ImageMetaData ReadMetaData(IBinaryStream stream) 22 | { 23 | stream.Seek(4, SeekOrigin.Current); 24 | uint header_size = stream.ReadUInt32(); 25 | long data_pos = stream.Position + header_size; 26 | if (stream.ReadInt32() != 1) 27 | return null; 28 | uint width = stream.ReadUInt32(); 29 | uint height = stream.ReadUInt32(); 30 | int bpp = stream.ReadInt32(); 31 | int name_length = stream.ReadInt32(); 32 | if (name_length <= 0) 33 | return null; 34 | int shift = (name_length % 7) + 1; 35 | var name_bits = stream.ReadBytes(name_length); 36 | for (int i = 0; i < name_length; ++i) 37 | { 38 | name_bits[i] = Binary.RotByteL(name_bits[i], shift); 39 | } 40 | return new DcfMetaData 41 | { 42 | Width = width, 43 | Height = height, 44 | BPP = bpp, 45 | BaseName = Encodings.cp932.GetString(name_bits), 46 | DataOffset = data_pos, 47 | }; 48 | } 49 | 50 | public override ImageData Read(IBinaryStream stream, ImageMetaData info) 51 | { 52 | using (var reader = new DcfReader(stream, (DcfMetaData)info)) 53 | { 54 | reader.Unpack(); 55 | return ImageData.Create(info, reader.Format, null, reader.Data, reader.Stride); 56 | } 57 | } 58 | 59 | public override void Write(Stream file, ImageData image) 60 | { 61 | throw new System.NotImplementedException("DcfFormat.Write not implemented"); 62 | } 63 | } 64 | 65 | internal sealed class DcfReader : IDisposable 66 | { 67 | IBinaryStream m_input; 68 | DcfMetaData m_info; 69 | byte[] m_output; 70 | byte[] m_mask = null; 71 | byte[] m_base = null; 72 | int m_overlay_bpp; 73 | int m_base_bpp; 74 | 75 | static readonly Lazy s_QntFormat = new Lazy(() => ImageFormat.FindByTag("QNT")); 76 | 77 | internal ImageFormat Qnt { get { return s_QntFormat.Value; } } 78 | 79 | public byte[] Data { get { return m_output; } } 80 | public PixelFormat Format { get; private set; } 81 | public int Stride { get; private set; } 82 | 83 | public DcfReader(IBinaryStream input, DcfMetaData info) 84 | { 85 | m_input = input; 86 | m_info = info; 87 | } 88 | 89 | public void Unpack() 90 | { 91 | long next_pos = m_info.DataOffset; 92 | for (; ; ) 93 | { 94 | m_input.Position = next_pos; 95 | uint id = m_input.ReadUInt32(); 96 | next_pos += 8 + m_input.ReadUInt32(); 97 | if (0x6C646664 == id) // 'dfdl' 98 | { 99 | int unpacked_size = m_input.ReadInt32(); 100 | if (unpacked_size <= 0) 101 | continue; 102 | m_mask = new byte[unpacked_size]; 103 | using (var input = new ZLibStream(m_input.AsStream, CompressionMode.Decompress, true)) 104 | input.Read(m_mask, 0, unpacked_size); 105 | } 106 | else if (0x64676364 == id) // 'dcgd' 107 | break; 108 | } 109 | long qnt_pos = m_input.Position; 110 | if (m_input.ReadUInt32() != Qnt.Signature) 111 | throw new InvalidFormatException(); 112 | using (var reg = new StreamRegion(m_input.AsStream, qnt_pos, true)) 113 | using (var qnt = new BinaryStream(reg, m_input.Name)) 114 | { 115 | var qnt_info = Qnt.ReadMetaData(qnt) as QntMetaData; 116 | if (null == qnt_info) 117 | throw new InvalidFormatException(); 118 | 119 | var overlay = new QntFormat.Reader(reg, qnt_info); 120 | overlay.Unpack(); 121 | m_overlay_bpp = overlay.BPP; 122 | if (m_mask != null) 123 | ReadBaseImage(); 124 | 125 | if (m_base != null) 126 | { 127 | m_output = ApplyOverlay(overlay.Data); 128 | SetFormat((int)m_info.Width, m_overlay_bpp); 129 | } 130 | else 131 | { 132 | m_output = overlay.Data; 133 | SetFormat((int)qnt_info.Width, m_overlay_bpp); 134 | } 135 | } 136 | } 137 | 138 | void SetFormat(int width, int bpp) 139 | { 140 | Format = 24 == bpp ? PixelFormats.Bgr24 : PixelFormats.Bgra32; 141 | Stride = width * (bpp / 8); 142 | } 143 | 144 | byte[] ApplyOverlay(byte[] overlay) 145 | { 146 | int blocks_x = (int)m_info.Width / 0x10; 147 | int blocks_y = (int)m_info.Height / 0x10; 148 | int base_step = m_base_bpp / 8; 149 | int overlay_step = m_overlay_bpp / 8; 150 | int base_stride = (int)m_info.Width * base_step; 151 | int overlay_stride = (int)m_info.Width * overlay_step; 152 | int mask_pos = 4; 153 | for (int y = 0; y < blocks_y; ++y) 154 | { 155 | int base_pos = y * 0x10 * base_stride; 156 | int dst_pos = y * 0x10 * overlay_stride; 157 | for (int x = 0; x < blocks_x; ++x) 158 | { 159 | if (0 == m_mask[mask_pos++]) 160 | continue; 161 | for (int by = 0; by < 0x10; ++by) 162 | { 163 | int src = base_pos + by * base_stride + x * 0x10 * base_step; 164 | int dst = dst_pos + by * overlay_stride + x * 0x10 * overlay_step; 165 | for (int bx = 0; bx < 0x10; ++bx) 166 | { 167 | overlay[dst] = m_base[src]; 168 | overlay[dst + 1] = m_base[src + 1]; 169 | overlay[dst + 2] = m_base[src + 2]; 170 | if (4 == overlay_step) 171 | { 172 | overlay[dst + 3] = 4 == base_step ? m_base[src + 3] : (byte)0xFF; 173 | } 174 | src += base_step; 175 | dst += overlay_step; 176 | } 177 | } 178 | } 179 | } 180 | return overlay; 181 | } 182 | 183 | void ReadBaseImage() 184 | { 185 | try 186 | { 187 | string dir_name = VFS.GetDirectoryName(m_info.FileName); 188 | string base_name = Path.ChangeExtension(m_info.BaseName, "qnt"); 189 | base_name = VFS.CombinePath(dir_name, base_name); 190 | using (var base_file = VFS.OpenBinaryStream(base_name)) 191 | { 192 | var base_info = Qnt.ReadMetaData(base_file) as QntMetaData; 193 | if (null != base_info && m_info.Width == base_info.Width && m_info.Height == base_info.Height) 194 | { 195 | base_info.FileName = base_name; 196 | var reader = new QntFormat.Reader(base_file.AsStream, base_info); 197 | reader.Unpack(); 198 | m_base_bpp = reader.BPP; 199 | m_base = reader.Data; 200 | } 201 | } 202 | } 203 | catch (Exception X) 204 | { 205 | Trace.WriteLine(X.Message, "[DCF]"); 206 | } 207 | } 208 | 209 | #region IDisposable Members 210 | public void Dispose() 211 | { 212 | } 213 | #endregion 214 | } 215 | */ 216 | 217 | public class XcfHeader : ICloneable 218 | { 219 | public List tags; 220 | public byte[] extraData; 221 | 222 | public string GetComment() 223 | { 224 | return ImageConverter.GetComment(this); 225 | } 226 | 227 | public bool ParseComment(string comment) 228 | { 229 | return ImageConverter.ParseComment(this, comment); 230 | } 231 | 232 | public QntHeader Clone() 233 | { 234 | var clone = (QntHeader)this.MemberwiseClone(); 235 | if (clone.extraData != null) clone.extraData = (byte[])clone.extraData.Clone(); 236 | return clone; 237 | } 238 | 239 | public bool Validate() 240 | { 241 | var xcf = this; 242 | return true; 243 | } 244 | 245 | #region ICloneable Members 246 | 247 | object ICloneable.Clone() 248 | { 249 | return Clone(); 250 | } 251 | 252 | #endregion 253 | } 254 | } -------------------------------------------------------------------------------- /ALDExplorer/lib/zlib32.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/lib/zlib32.dll -------------------------------------------------------------------------------- /ALDExplorer/lib/zlibnet.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UserUnknownFactor/ALDExplorer/43444035b01c0b01edd8d73c4dc29e47d0b090bf/ALDExplorer/lib/zlibnet.dll -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 UserUnknownFactor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ALDExplorer --------------------------------------------------------------------------------