├── .gitattributes ├── .gitignore ├── ArchiveExplorer ├── App.config ├── ArchiveExplorer.Designer.cs ├── ArchiveExplorer.cs ├── ArchiveExplorer.csproj ├── ArchiveExplorer.licenseheader ├── ArchiveExplorer.resx ├── Images │ ├── File_large.png │ ├── File_small.png │ ├── Folder_large.png │ ├── Folder_small.png │ └── spinner.gif ├── KeyRequestForm.Designer.cs ├── KeyRequestForm.cs ├── KeyRequestForm.resx ├── Program.cs └── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── GameArchives.sln ├── GameArchivesTests ├── GameArchivesTests.csproj ├── Local │ └── LocalDirectoryTests.cs └── Properties │ └── AssemblyInfo.cs ├── LICENSE ├── LibArchiveExplorer ├── EditorWindow.Designer.cs ├── EditorWindow.cs ├── EditorWindow.resx ├── Extensions.cs ├── FolderSelectDialog.cs ├── LibArchiveExplorer.csproj ├── PackageManager.cs ├── PackageView.Designer.cs ├── PackageView.cs ├── PackageView.resx ├── Properties │ └── AssemblyInfo.cs └── Reflector.cs ├── Library ├── .editorconfig ├── ArchiveInterfaces.cs ├── Ark │ ├── ArkDirectory.cs │ ├── ArkPackage.cs │ ├── HdrCryptStream.cs │ └── ProtectedFileStream.cs ├── Common │ ├── DefaultDirectory.cs │ ├── MultiStream.cs │ ├── OffsetFile.cs │ └── OffsetStream.cs ├── FSAR │ ├── AesCryptStream.cs │ ├── FSARDirectory.cs │ ├── FSARFile.cs │ └── FSARPackage.cs ├── FSGIMG │ ├── FSGIMGDirectory.cs │ └── FSGIMGPackage.cs ├── GameArchives.csproj ├── GameArchives.licenseheader ├── Local │ ├── LocalDirectory.cs │ └── LocalFile.cs ├── PFS │ ├── PFSCDecompressStream.cs │ ├── PFSDirectory.cs │ ├── PFSFile.cs │ ├── PFSPackage.cs │ └── XtsCryptStream.cs ├── PKF │ ├── PKFDirectory.cs │ ├── PKFFile.cs │ └── PKFPackage.cs ├── PSARC │ ├── PSARCDirectory.cs │ ├── PSARCFile.cs │ └── PSARCPackage.cs ├── PackageReader.cs ├── PackageType.cs ├── Properties │ └── AssemblyInfo.cs ├── STFS │ ├── STFSDirectory.cs │ ├── STFSFile.cs │ └── STFSPackage.cs ├── Seven45 │ ├── PowerChordCryptStream.cs │ └── Seven45Package.cs ├── StreamExtensions.cs ├── U8 │ ├── U8Directory.cs │ └── U8Package.cs ├── Util.cs └── XISO │ ├── XISODirectory.cs │ ├── XISOFSNode.cs │ ├── XISOFile.cs │ └── XISOPackage.cs ├── README.md └── appveyor.yml /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | # DNX 42 | project.lock.json 43 | artifacts/ 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding add-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | ## TODO: Comment the next line if you want to checkin your 137 | ## web deploy settings but do note that will include unencrypted 138 | ## passwords 139 | #*.pubxml 140 | 141 | *.publishproj 142 | 143 | # NuGet Packages 144 | *.nupkg 145 | # The packages folder can be ignored because of Package Restore 146 | **/packages/* 147 | # except build/, which is used as an MSBuild target. 148 | !**/packages/build/ 149 | # Uncomment if necessary however generally it will be regenerated when needed 150 | #!**/packages/repositories.config 151 | 152 | # Windows Azure Build Output 153 | csx/ 154 | *.build.csdef 155 | 156 | # Windows Store app package directory 157 | AppPackages/ 158 | 159 | # Visual Studio cache files 160 | # files ending in .cache can be ignored 161 | *.[Cc]ache 162 | # but keep track of directories ending in .cache 163 | !*.[Cc]ache/ 164 | 165 | # Others 166 | ClientBin/ 167 | [Ss]tyle[Cc]op.* 168 | ~$* 169 | *~ 170 | *.dbmdl 171 | *.dbproj.schemaview 172 | *.pfx 173 | *.publishsettings 174 | node_modules/ 175 | orleans.codegen.cs 176 | 177 | # RIA/Silverlight projects 178 | Generated_Code/ 179 | 180 | # Backup & report files from converting an old project file 181 | # to a newer Visual Studio version. Backup files are not needed, 182 | # because we have git ;-) 183 | _UpgradeReport_Files/ 184 | Backup*/ 185 | UpgradeLog*.XML 186 | UpgradeLog*.htm 187 | 188 | # SQL Server files 189 | *.mdf 190 | *.ldf 191 | 192 | # Business Intelligence projects 193 | *.rdl.data 194 | *.bim.layout 195 | *.bim_*.settings 196 | 197 | # Microsoft Fakes 198 | FakesAssemblies/ 199 | 200 | # Node.js Tools for Visual Studio 201 | .ntvs_analysis.dat 202 | 203 | # Visual Studio 6 build log 204 | *.plg 205 | 206 | # Visual Studio 6 workspace options file 207 | *.opt 208 | 209 | # LightSwitch generated files 210 | GeneratedArtifacts/ 211 | _Pvt_Extensions/ 212 | ModelManifest.xml 213 | -------------------------------------------------------------------------------- /ArchiveExplorer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ArchiveExplorer/ArchiveExplorer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * ArchiveExplorer.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.ComponentModel; 23 | using System.Data; 24 | using System.Drawing; 25 | using System.Linq; 26 | using System.Text; 27 | using System.Threading.Tasks; 28 | using System.Windows.Forms; 29 | using GameArchives; 30 | using System.IO; 31 | using FolderSelect; 32 | using LibArchiveExplorer; 33 | 34 | namespace ArchiveExplorer 35 | { 36 | public partial class ArchiveExplorer : Form 37 | { 38 | private PackageManager pm; 39 | 40 | public ArchiveExplorer() 41 | { 42 | InitializeComponent(); 43 | pm = PackageManager.GetInstance(); 44 | pm.Spinner = this.spinnerLabel; 45 | pm.StatusLabel = this.toolStripStatusLabel1; 46 | pm.Loader = LoadFile; 47 | pm.SetReady(); 48 | string[] args = Environment.GetCommandLineArgs(); 49 | if (args.Length > 1) 50 | { 51 | if(File.Exists(args[1])) 52 | { 53 | LoadFile(Util.LocalFile(args[1])); 54 | } 55 | } 56 | } 57 | 58 | private string passcode_popup(string prompt) 59 | { 60 | var form = new KeyRequestForm(prompt); 61 | return form.ShowDialog() == DialogResult.OK ? form.Passcode : ""; 62 | } 63 | 64 | /// 65 | /// Deal with a new file. 66 | /// 67 | /// 68 | private async Task LoadFile(IFile file) 69 | { 70 | var newPage = new TabPage(); 71 | newPage.Text = "Loading..."; 72 | packagesTabControl.Controls.Add(newPage); 73 | PackageView packageView = null; 74 | try 75 | { 76 | var newPackage = await Task.Run(() => PackageReader.ReadPackageFromFile(file, passcode_popup)); 77 | packageView = new PackageView(newPackage, pm); 78 | packageView.OnRemoveTab += RemoveTab; 79 | newPage.Text = newPackage.FileName; 80 | newPage.Controls.Add(packageView); 81 | packageView.Tag = newPage; 82 | packageView.SetView(view); 83 | packageView.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; 84 | packageView.Dock = System.Windows.Forms.DockStyle.Fill; 85 | packageView.Location = new System.Drawing.Point(3, 3); 86 | packageView.Margin = new System.Windows.Forms.Padding(0); 87 | packageView.Name = "packageView"; 88 | packageView.TabIndex = 0; 89 | packagesTabControl.SelectedTab = newPage; 90 | } 91 | catch (Exception ex) 92 | { 93 | packagesTabControl.Controls.Remove(newPage); 94 | MessageBox.Show("Could not load archive!" + Environment.NewLine + ex.Message, "Error"); 95 | } 96 | return packageView; 97 | }//LoadFile 98 | 99 | public void RemoveTab(TabPage p) 100 | { 101 | packagesTabControl.Controls.Remove(p); 102 | } 103 | 104 | 105 | 106 | private void openToolStripMenuItem_Click(object sender, EventArgs e) 107 | { 108 | var of = new OpenFileDialog(); 109 | of.Title = "Select package to open."; 110 | of.Filter += "All Files (*.*)|*.*|" + PackageReader.SupportedFormats; 111 | if (of.ShowDialog() == DialogResult.OK) 112 | { 113 | LoadFile(Util.LocalFile(of.FileName)); 114 | } 115 | } 116 | 117 | private void detailsToolStripMenuItem_Click(object sender, EventArgs e) 118 | { 119 | if (!detailsToolStripMenuItem.Checked) 120 | { 121 | detailsToolStripMenuItem.Checked = true; 122 | iconsToolStripMenuItem.Checked = false; 123 | listToolStripMenuItem.Checked = false; 124 | SetView(View.Details); 125 | } 126 | } 127 | 128 | private void iconsToolStripMenuItem_Click(object sender, EventArgs e) 129 | { 130 | if (!iconsToolStripMenuItem.Checked) 131 | { 132 | detailsToolStripMenuItem.Checked = false; 133 | iconsToolStripMenuItem.Checked = true; 134 | listToolStripMenuItem.Checked = false; 135 | SetView(View.LargeIcon); 136 | } 137 | } 138 | 139 | private void listToolStripMenuItem_Click(object sender, EventArgs e) 140 | { 141 | if (!listToolStripMenuItem.Checked) 142 | { 143 | detailsToolStripMenuItem.Checked = false; 144 | iconsToolStripMenuItem.Checked = false; 145 | listToolStripMenuItem.Checked = true; 146 | SetView(View.List); 147 | } 148 | } 149 | 150 | private View view = View.Details; 151 | private void SetView(View v) 152 | { 153 | view = v; 154 | foreach(TabPage t in packagesTabControl.TabPages) 155 | { 156 | if(t.Controls.Count > 0) 157 | (t.Controls[0] as PackageView)?.SetView(v); 158 | } 159 | } 160 | 161 | private void exitToolStripMenuItem_Click(object sender, EventArgs e) 162 | { 163 | Application.Exit(); 164 | } 165 | 166 | private void closeToolStripMenuItem_Click(object sender, EventArgs e) 167 | { 168 | (packagesTabControl.SelectedTab?.Controls[0] as PackageView)?.Unload(); 169 | } 170 | 171 | private void tabControl1_Click(object sender, MouseEventArgs e) 172 | { 173 | var ctrl = sender as TabControl; 174 | if (e.Button == MouseButtons.Middle) 175 | { 176 | (ctrl.TabPages.Cast() 177 | .Where((t, i) => ctrl.GetTabRect(i).Contains(e.Location)) 178 | .First().Controls[0] as PackageView).Unload(); 179 | } 180 | } 181 | 182 | private void gitHubToolStripMenuItem_Click(object sender, EventArgs e) 183 | { 184 | System.Diagnostics.Process.Start("https://github.com/maxton/GameArchives"); 185 | } 186 | 187 | private void packagesTabControl_DragDrop(object sender, DragEventArgs e) 188 | { 189 | var files = e.Data.GetData(DataFormats.FileDrop) as string[]; 190 | foreach(string file in files) 191 | { 192 | LoadFile(Util.LocalFile(file)); 193 | } 194 | } 195 | 196 | private void packagesTabControl_DragEnter(object sender, DragEventArgs e) 197 | { 198 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) 199 | e.Effect = DragDropEffects.Copy; 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /ArchiveExplorer/ArchiveExplorer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {EAC2E703-6D41-4E69-B14D-D881A3C9909C} 8 | WinExe 9 | Properties 10 | ArchiveExplorer 11 | ArchiveExplorer 12 | v4.5 13 | 512 14 | true 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | false 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | false 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Form 54 | 55 | 56 | ArchiveExplorer.cs 57 | 58 | 59 | Form 60 | 61 | 62 | KeyRequestForm.cs 63 | 64 | 65 | 66 | 67 | ArchiveExplorer.cs 68 | 69 | 70 | KeyRequestForm.cs 71 | 72 | 73 | ResXFileCodeGenerator 74 | Resources.Designer.cs 75 | Designer 76 | 77 | 78 | True 79 | Resources.resx 80 | True 81 | 82 | 83 | 84 | SettingsSingleFileGenerator 85 | Settings.Designer.cs 86 | 87 | 88 | True 89 | Settings.settings 90 | True 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | {29722aa8-e9fc-4b5b-9c73-31a6293dba55} 99 | LibArchiveExplorer 100 | 101 | 102 | {906748f0-3a55-4b20-bccb-9dc7187f1d5e} 103 | GameArchives 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 121 | -------------------------------------------------------------------------------- /ArchiveExplorer/ArchiveExplorer.licenseheader: -------------------------------------------------------------------------------- 1 | extensions: designer.cs generated.cs 2 | extensions: .cs .cpp .h 3 | /* 4 | * %FileName% 5 | * 6 | * Copyright (c) 2015,%CurrentYear%, maxton. All rights reserved. 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 3.0 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; If not, see 20 | * . 21 | */ -------------------------------------------------------------------------------- /ArchiveExplorer/ArchiveExplorer.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 | 121 | 17, 17 122 | 123 | 124 | 132, 17 125 | 126 | -------------------------------------------------------------------------------- /ArchiveExplorer/Images/File_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxton/GameArchives/97875280485af067da6adebed5299beaac302fda/ArchiveExplorer/Images/File_large.png -------------------------------------------------------------------------------- /ArchiveExplorer/Images/File_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxton/GameArchives/97875280485af067da6adebed5299beaac302fda/ArchiveExplorer/Images/File_small.png -------------------------------------------------------------------------------- /ArchiveExplorer/Images/Folder_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxton/GameArchives/97875280485af067da6adebed5299beaac302fda/ArchiveExplorer/Images/Folder_large.png -------------------------------------------------------------------------------- /ArchiveExplorer/Images/Folder_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxton/GameArchives/97875280485af067da6adebed5299beaac302fda/ArchiveExplorer/Images/Folder_small.png -------------------------------------------------------------------------------- /ArchiveExplorer/Images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxton/GameArchives/97875280485af067da6adebed5299beaac302fda/ArchiveExplorer/Images/spinner.gif -------------------------------------------------------------------------------- /ArchiveExplorer/KeyRequestForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace ArchiveExplorer 2 | { 3 | partial class KeyRequestForm 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.button1 = new System.Windows.Forms.Button(); 32 | this.label1 = new System.Windows.Forms.Label(); 33 | this.passcodeTextBox = new System.Windows.Forms.TextBox(); 34 | this.checkBox1 = new System.Windows.Forms.CheckBox(); 35 | this.SuspendLayout(); 36 | // 37 | // button1 38 | // 39 | this.button1.DialogResult = System.Windows.Forms.DialogResult.OK; 40 | this.button1.Location = new System.Drawing.Point(148, 83); 41 | this.button1.Name = "button1"; 42 | this.button1.Size = new System.Drawing.Size(75, 23); 43 | this.button1.TabIndex = 0; 44 | this.button1.Text = "OK"; 45 | this.button1.UseVisualStyleBackColor = true; 46 | // 47 | // label1 48 | // 49 | this.label1.AutoSize = true; 50 | this.label1.Location = new System.Drawing.Point(12, 9); 51 | this.label1.Name = "label1"; 52 | this.label1.Size = new System.Drawing.Size(150, 13); 53 | this.label1.TabIndex = 1; 54 | this.label1.Text = "A passcode or key is needed: "; 55 | // 56 | // passcodeTextBox 57 | // 58 | this.passcodeTextBox.Location = new System.Drawing.Point(15, 26); 59 | this.passcodeTextBox.Name = "passcodeTextBox"; 60 | this.passcodeTextBox.Size = new System.Drawing.Size(344, 20); 61 | this.passcodeTextBox.TabIndex = 2; 62 | // 63 | // checkBox1 64 | // 65 | this.checkBox1.AutoSize = true; 66 | this.checkBox1.Location = new System.Drawing.Point(15, 52); 67 | this.checkBox1.Name = "checkBox1"; 68 | this.checkBox1.Size = new System.Drawing.Size(107, 17); 69 | this.checkBox1.TabIndex = 3; 70 | this.checkBox1.Text = "Decode from hex"; 71 | this.checkBox1.UseVisualStyleBackColor = true; 72 | // 73 | // KeyRequestForm 74 | // 75 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 76 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 77 | this.ClientSize = new System.Drawing.Size(371, 118); 78 | this.ControlBox = false; 79 | this.Controls.Add(this.checkBox1); 80 | this.Controls.Add(this.passcodeTextBox); 81 | this.Controls.Add(this.label1); 82 | this.Controls.Add(this.button1); 83 | this.Name = "KeyRequestForm"; 84 | this.ShowIcon = false; 85 | this.ShowInTaskbar = false; 86 | this.Text = "Decryption Key"; 87 | this.ResumeLayout(false); 88 | this.PerformLayout(); 89 | 90 | } 91 | 92 | #endregion 93 | 94 | private System.Windows.Forms.Button button1; 95 | private System.Windows.Forms.Label label1; 96 | private System.Windows.Forms.TextBox passcodeTextBox; 97 | private System.Windows.Forms.CheckBox checkBox1; 98 | } 99 | } -------------------------------------------------------------------------------- /ArchiveExplorer/KeyRequestForm.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.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace ArchiveExplorer 12 | { 13 | public partial class KeyRequestForm : Form 14 | { 15 | public string Passcode 16 | { 17 | get 18 | { 19 | if (checkBox1.Checked) 20 | { 21 | StringBuilder sb = new StringBuilder(); 22 | var key = passcodeTextBox.Text.Replace(" ", ""); 23 | for (var x = 0; x < key.Length; x += 2) 24 | { 25 | sb.Append((char)Convert.ToByte(key.Substring(x, 2), 16)); 26 | } 27 | return sb.ToString(); 28 | } 29 | return passcodeTextBox.Text; 30 | } 31 | } 32 | public KeyRequestForm(string request = "") 33 | { 34 | InitializeComponent(); 35 | label1.Text += request; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ArchiveExplorer/KeyRequestForm.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 | -------------------------------------------------------------------------------- /ArchiveExplorer/Program.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Program.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.Linq; 23 | using System.Threading.Tasks; 24 | using System.Windows.Forms; 25 | 26 | namespace ArchiveExplorer 27 | { 28 | static class Program 29 | { 30 | /// 31 | /// The main entry point for the application. 32 | /// 33 | [STAThread] 34 | static void Main() 35 | { 36 | Application.EnableVisualStyles(); 37 | Application.SetCompatibleTextRenderingDefault(false); 38 | Application.Run(new ArchiveExplorer()); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ArchiveExplorer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ArchiveExplorer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ArchiveExplorer")] 13 | [assembly: AssemblyCopyright("Copyright © maxton 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("eac2e703-6d41-4e69-b14d-d881a3c9909c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("0.12.0.*")] 36 | [assembly: AssemblyFileVersion("0.12.0.0")] 37 | -------------------------------------------------------------------------------- /ArchiveExplorer/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 ArchiveExplorer.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", "4.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("ArchiveExplorer.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 spinner { 67 | get { 68 | object obj = ResourceManager.GetObject("spinner", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ArchiveExplorer/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=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 | 121 | 122 | ..\Images\spinner.gif;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | -------------------------------------------------------------------------------- /ArchiveExplorer/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 ArchiveExplorer.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.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 | -------------------------------------------------------------------------------- /ArchiveExplorer/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GameArchives.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2000 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GameArchives", "Library\GameArchives.csproj", "{906748F0-3A55-4B20-BCCB-9DC7187F1D5E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ArchiveExplorer", "ArchiveExplorer\ArchiveExplorer.csproj", "{EAC2E703-6D41-4E69-B14D-D881A3C9909C}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibArchiveExplorer", "LibArchiveExplorer\LibArchiveExplorer.csproj", "{29722AA8-E9FC-4B5B-9C73-31A6293DBA55}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | Release-minimal|Any CPU = Release-minimal|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release-minimal|Any CPU.ActiveCfg = Release-minimal|Any CPU 24 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E}.Release-minimal|Any CPU.Build.0 = Release-minimal|Any CPU 25 | {EAC2E703-6D41-4E69-B14D-D881A3C9909C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {EAC2E703-6D41-4E69-B14D-D881A3C9909C}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {EAC2E703-6D41-4E69-B14D-D881A3C9909C}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {EAC2E703-6D41-4E69-B14D-D881A3C9909C}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {EAC2E703-6D41-4E69-B14D-D881A3C9909C}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 30 | {EAC2E703-6D41-4E69-B14D-D881A3C9909C}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 31 | {29722AA8-E9FC-4B5B-9C73-31A6293DBA55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {29722AA8-E9FC-4B5B-9C73-31A6293DBA55}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {29722AA8-E9FC-4B5B-9C73-31A6293DBA55}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {29722AA8-E9FC-4B5B-9C73-31A6293DBA55}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {29722AA8-E9FC-4B5B-9C73-31A6293DBA55}.Release-minimal|Any CPU.ActiveCfg = Release|Any CPU 36 | {29722AA8-E9FC-4B5B-9C73-31A6293DBA55}.Release-minimal|Any CPU.Build.0 = Release|Any CPU 37 | EndGlobalSection 38 | GlobalSection(SolutionProperties) = preSolution 39 | HideSolutionNode = FALSE 40 | EndGlobalSection 41 | GlobalSection(ExtensibilityGlobals) = postSolution 42 | SolutionGuid = {DEF13207-4D07-42E5-8F0E-69817D41B933} 43 | EndGlobalSection 44 | EndGlobal 45 | -------------------------------------------------------------------------------- /GameArchivesTests/GameArchivesTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | {4312AA1C-904E-4FCD-860A-AA888D5BE0CE} 7 | Library 8 | Properties 9 | GameArchivesTests 10 | GameArchivesTests 11 | v4.5 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E} 59 | GameArchives 60 | 61 | 62 | 63 | 64 | 65 | 66 | False 67 | 68 | 69 | False 70 | 71 | 72 | False 73 | 74 | 75 | False 76 | 77 | 78 | 79 | 80 | 81 | 82 | 89 | -------------------------------------------------------------------------------- /GameArchivesTests/Local/LocalDirectoryTests.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using GameArchives.Local; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace GameArchives.Local.Tests 10 | { 11 | [TestClass()] 12 | public class LocalDirectoryTests 13 | { 14 | [TestMethod()] 15 | public void GetDirectoryTest() 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /GameArchivesTests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GameArchivesTests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GameArchivesTests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("4312aa1c-904e-4fcd-860a-aa888d5be0ce")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LibArchiveExplorer/EditorWindow.cs: -------------------------------------------------------------------------------- 1 | using GameArchives; 2 | using GameArchives.Common; 3 | using GameArchives.XISO; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace LibArchiveExplorer 15 | { 16 | public partial class EditorWindow : Form 17 | { 18 | private AbstractPackage _pkg; 19 | private List _allFiles; 20 | 21 | public EditorWindow(AbstractPackage pkg) 22 | { 23 | InitializeComponent(); 24 | _pkg = pkg; 25 | Resize += (o,e) => discUsageGraphic.Invalidate(); 26 | LoadPackage(); 27 | } 28 | 29 | /// 30 | /// Draws a graphic that looks like the old Disk Defragmenter disk usage image. 31 | /// 32 | /// 33 | /// 34 | private void DrawGraphic(object sender, PaintEventArgs evt) 35 | { 36 | evt.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; 37 | foreach (var file in _allFiles ?? new List()) 38 | { 39 | var rect = new RectangleF 40 | { 41 | X = file.DataLocation * discUsageGraphic.Width / (float)_pkg.Size, 42 | Y = 0, 43 | Height = discUsageGraphic.Height, 44 | Width = file.Size * discUsageGraphic.Width / (float)_pkg.Size 45 | }; 46 | evt.Graphics.FillRectangle(Brushes.Gray, rect); 47 | } 48 | evt.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 49 | foreach (ListViewItem f in listView1.SelectedItems) 50 | { 51 | var file = f.Tag as OffsetFile; 52 | var rect = new RectangleF 53 | { 54 | X = file.DataLocation * discUsageGraphic.Width / (float)_pkg.Size, 55 | Y = -1, 56 | Height = discUsageGraphic.Height + 2, 57 | Width = Math.Max(file.Size * discUsageGraphic.Width / (float)_pkg.Size, 1) 58 | }; 59 | evt.Graphics.FillRectangle(Brushes.Black, rect); 60 | } 61 | } 62 | 63 | /// 64 | /// Load the package. 65 | /// 66 | private void LoadPackage() 67 | { 68 | if (_pkg.FileType.IsSubclassOrEqual(typeof(OffsetFile))) 69 | { 70 | discUsageGraphic.Invalidate(); 71 | _allFiles = _pkg.GetAllFiles(); 72 | var totalSize = _allFiles.Sum((f) => f.Size); 73 | discUsageBar.Value = ((int)(100 * totalSize / _pkg.Size)).Clamp(discUsageBar.Minimum, discUsageBar.Maximum); 74 | _allFiles.Sort((f, f2) => Math.Sign(f.DataLocation - f2.DataLocation)); 75 | _allFiles.ForEach(f => 76 | listView1.Items.Add(new ListViewItem(new string[] { f.Name }) 77 | { 78 | Tag = f 79 | })); 80 | 81 | lblPackageSize.Text = _pkg.Size.HumanReadableFileSize(); 82 | lblTotalFiles.Text = _allFiles.Count.ToString(); 83 | lblTotalFileSize.Text = totalSize.HumanReadableFileSize(); 84 | actionsGroupBox.Enabled = _pkg.Writeable; 85 | } 86 | else 87 | { 88 | throw new NotImplementedException("Editor support is not implemented for this package type"); 89 | } 90 | } 91 | 92 | private void SelectFileAtByte(long b) 93 | { 94 | foreach(ListViewItem item in listView1.SelectedItems) 95 | { 96 | item.Selected = false; 97 | item.Focused = false; 98 | } 99 | foreach(ListViewItem item in listView1.Items) 100 | { 101 | var file = item.Tag as OffsetFile; 102 | if(file.DataLocation <= b && file.DataLocation + file.Size >= b) 103 | { 104 | listView1.FocusedItem = item; 105 | item.Selected = true; 106 | item.Focused = true; 107 | item.EnsureVisible(); 108 | break; 109 | } 110 | } 111 | } 112 | 113 | /// 114 | /// Closes the currently opened package. 115 | /// 116 | private void ClosePackage() 117 | { 118 | _pkg = null; 119 | listView1.Items.Clear(); 120 | _allFiles?.Clear(); 121 | discUsageGraphic.Invalidate(); 122 | discUsageBar.Value = 0; 123 | } 124 | 125 | 126 | private void toolStripMenuItem1_Click(object sender, EventArgs e) 127 | { 128 | Close(); 129 | } 130 | 131 | private void listView1_SelectedIndexChanged(object sender, EventArgs e) 132 | { 133 | discUsageGraphic.Invalidate(); 134 | } 135 | 136 | private void discUsageGraphic_Click(object sender, MouseEventArgs e) 137 | { 138 | SelectFileAtByte((e as MouseEventArgs).X * _pkg.Size / discUsageGraphic.Width); 139 | } 140 | 141 | private void button1_Click(object sender, EventArgs e) 142 | { 143 | if(listView1.SelectedItems.Count != 1) 144 | { 145 | MessageBox.Show(this, "Please select only one item."); 146 | return; 147 | } 148 | var file = listView1.SelectedItems[0].Tag as OffsetFile; 149 | var x = new OpenFileDialog(); 150 | if(x.ShowDialog() == DialogResult.OK) 151 | { 152 | var newFile = Util.LocalFile(x.FileName); 153 | if(MessageBox.Show("This will PERMANENTLY modify the archive." 154 | +Environment.NewLine+"Are you sure you want to continue?","Confirm Action", MessageBoxButtons.YesNo) 155 | == DialogResult.Yes) 156 | { 157 | try 158 | { 159 | if((_pkg as MutablePackage)?.FileReplaceCheck(file, newFile) == true 160 | && (_pkg as MutablePackage)?.TryReplaceFile(file, newFile) == true) 161 | { 162 | MessageBox.Show("Successfully replaced."); 163 | } 164 | else 165 | { 166 | throw new Exception("Failed"); 167 | } 168 | } 169 | catch(Exception ex) 170 | { 171 | MessageBox.Show(ex.Message); 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /LibArchiveExplorer/EditorWindow.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 | -------------------------------------------------------------------------------- /LibArchiveExplorer/Extensions.cs: -------------------------------------------------------------------------------- 1 | using GameArchives; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace LibArchiveExplorer 6 | { 7 | static class Extensions 8 | { 9 | public static string HumanReadableFileSize(this long size) 10 | { 11 | if (size > (1024 * 1024 * 1024)) 12 | { 13 | return (size / (double)(1024 * 1024 * 1024)).ToString("F") + " GiB"; 14 | } 15 | else if (size > (1024 * 1024)) 16 | { 17 | return (size / (double)(1024 * 1024)).ToString("F") + " MiB"; 18 | } 19 | else if (size > 1024) 20 | { 21 | return (size / 1024.0).ToString("F") + " KiB"; 22 | } 23 | else 24 | { 25 | return size.ToString() + " B"; 26 | } 27 | } 28 | 29 | public static bool IsSubclassOrEqual(this Type type, Type other) 30 | { 31 | return type.IsSubclassOf(other) || type == other; 32 | } 33 | 34 | public static int Clamp(this int value, int min, int max) 35 | { 36 | if (value < min) return min; 37 | if (value > max) return max; 38 | return value; 39 | } 40 | 41 | public static Tuple GetLargestFreeBlock(this AbstractPackage pkg) 42 | { 43 | return new Tuple(0, 0); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LibArchiveExplorer/FolderSelectDialog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | // ------------------------------------------------------------------ 5 | // Wraps System.Windows.Forms.OpenFileDialog to make it present 6 | // a vista-style dialog. 7 | // ------------------------------------------------------------------ 8 | 9 | namespace FolderSelect 10 | { 11 | /// 12 | /// Wraps System.Windows.Forms.OpenFileDialog to make it present 13 | /// a vista-style dialog. 14 | /// 15 | public class FolderSelectDialog 16 | { 17 | // Wrapped dialog 18 | System.Windows.Forms.OpenFileDialog ofd = null; 19 | 20 | /// 21 | /// Default constructor 22 | /// 23 | public FolderSelectDialog() 24 | { 25 | ofd = new System.Windows.Forms.OpenFileDialog(); 26 | 27 | ofd.Filter = "Folders|\n"; 28 | ofd.AddExtension = false; 29 | ofd.CheckFileExists = false; 30 | ofd.DereferenceLinks = true; 31 | ofd.Multiselect = false; 32 | } 33 | 34 | #region Properties 35 | 36 | /// 37 | /// Gets/Sets the initial folder to be selected. A null value selects the current directory. 38 | /// 39 | public string InitialDirectory 40 | { 41 | get { return ofd.InitialDirectory; } 42 | set { ofd.InitialDirectory = value == null || value.Length == 0 ? Environment.CurrentDirectory : value; } 43 | } 44 | 45 | /// 46 | /// Gets/Sets the title to show in the dialog 47 | /// 48 | public string Title 49 | { 50 | get { return ofd.Title; } 51 | set { ofd.Title = value == null ? "Select a folder" : value; } 52 | } 53 | 54 | /// 55 | /// Gets the selected folder 56 | /// 57 | public string FileName 58 | { 59 | get { return ofd.FileName; } 60 | } 61 | 62 | #endregion 63 | 64 | #region Methods 65 | 66 | /// 67 | /// Shows the dialog 68 | /// 69 | /// True if the user presses OK else false 70 | public bool ShowDialog() 71 | { 72 | return ShowDialog(IntPtr.Zero); 73 | } 74 | 75 | /// 76 | /// Shows the dialog 77 | /// 78 | /// Handle of the control to be parent 79 | /// True if the user presses OK else false 80 | public bool ShowDialog(IntPtr hWndOwner) 81 | { 82 | bool flag = false; 83 | 84 | if (Environment.OSVersion.Version.Major >= 6) 85 | { 86 | var r = new Reflector("System.Windows.Forms"); 87 | 88 | uint num = 0; 89 | Type typeIFileDialog = r.GetType("FileDialogNative.IFileDialog"); 90 | object dialog = r.Call(ofd, "CreateVistaDialog"); 91 | r.Call(ofd, "OnBeforeVistaDialog", dialog); 92 | 93 | uint options = (uint)r.CallAs(typeof(System.Windows.Forms.FileDialog), ofd, "GetOptions"); 94 | options |= (uint)r.GetEnum("FileDialogNative.FOS", "FOS_PICKFOLDERS"); 95 | r.CallAs(typeIFileDialog, dialog, "SetOptions", options); 96 | 97 | object pfde = r.New("FileDialog.VistaDialogEvents", ofd); 98 | object[] parameters = new object[] { pfde, num }; 99 | r.CallAs2(typeIFileDialog, dialog, "Advise", parameters); 100 | num = (uint)parameters[1]; 101 | try 102 | { 103 | int num2 = (int)r.CallAs(typeIFileDialog, dialog, "Show", hWndOwner); 104 | flag = 0 == num2; 105 | } 106 | finally 107 | { 108 | r.CallAs(typeIFileDialog, dialog, "Unadvise", num); 109 | GC.KeepAlive(pfde); 110 | } 111 | } 112 | else 113 | { 114 | var fbd = new FolderBrowserDialog(); 115 | fbd.Description = this.Title; 116 | fbd.SelectedPath = this.InitialDirectory; 117 | fbd.ShowNewFolderButton = false; 118 | if (fbd.ShowDialog(new WindowWrapper(hWndOwner)) != DialogResult.OK) return false; 119 | ofd.FileName = fbd.SelectedPath; 120 | flag = true; 121 | } 122 | 123 | return flag; 124 | } 125 | 126 | #endregion 127 | } 128 | 129 | /// 130 | /// Creates IWin32Window around an IntPtr 131 | /// 132 | public class WindowWrapper : System.Windows.Forms.IWin32Window 133 | { 134 | /// 135 | /// Constructor 136 | /// 137 | /// Handle to wrap 138 | public WindowWrapper(IntPtr handle) 139 | { 140 | _hwnd = handle; 141 | } 142 | 143 | /// 144 | /// Original ptr 145 | /// 146 | public IntPtr Handle 147 | { 148 | get { return _hwnd; } 149 | } 150 | 151 | private IntPtr _hwnd; 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /LibArchiveExplorer/LibArchiveExplorer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {29722AA8-E9FC-4B5B-9C73-31A6293DBA55} 8 | Library 9 | Properties 10 | LibArchiveExplorer 11 | LibArchiveExplorer 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Form 49 | 50 | 51 | EditorWindow.cs 52 | 53 | 54 | 55 | 56 | 57 | UserControl 58 | 59 | 60 | PackageView.cs 61 | 62 | 63 | 64 | 65 | 66 | 67 | EditorWindow.cs 68 | 69 | 70 | PackageView.cs 71 | 72 | 73 | 74 | 75 | {906748f0-3a55-4b20-bccb-9dc7187f1d5e} 76 | GameArchives 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /LibArchiveExplorer/PackageManager.cs: -------------------------------------------------------------------------------- 1 | using GameArchives; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace LibArchiveExplorer 9 | { 10 | public class PackageManager 11 | { 12 | private static PackageManager _pm; 13 | public static PackageManager GetInstance() 14 | { 15 | if(_pm == null) 16 | { 17 | return (_pm = new PackageManager()); 18 | } 19 | return _pm; 20 | } 21 | 22 | public bool Ready { get; private set; } 23 | 24 | public Func> Loader { get; set; } 25 | 26 | private System.Windows.Forms.ToolStripStatusLabel statusLabel; 27 | private System.Windows.Forms.ToolStripStatusLabel spinner; 28 | private PackageManager() 29 | { 30 | Ready = true; 31 | } 32 | 33 | public System.Windows.Forms.ToolStripStatusLabel Spinner 34 | { 35 | set { spinner = value; } 36 | } 37 | public System.Windows.Forms.ToolStripStatusLabel StatusLabel 38 | { 39 | set { statusLabel = value; } 40 | } 41 | 42 | public void SetReady() 43 | { 44 | Ready = true; 45 | if(spinner != null) 46 | spinner.Visible = false; 47 | if (statusLabel != null) 48 | statusLabel.Text = "Ready."; 49 | } 50 | 51 | public void SetBusyState(string busyState) 52 | { 53 | Ready = false; 54 | if (spinner != null) 55 | spinner.Visible = true; 56 | if (statusLabel != null) 57 | statusLabel.Text = busyState; 58 | } 59 | 60 | public async void LoadFile(IFile f, PackageView owner = null) 61 | { 62 | if(Loader == null) return; 63 | var pkgView = await Loader(f); 64 | if(owner != null && pkgView != null) 65 | { 66 | owner.AddChildPackage(pkgView); 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /LibArchiveExplorer/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("LibArchiveExplorer")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("LibArchiveExplorer")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("29722aa8-e9fc-4b5b-9c73-31a6293dba55")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /LibArchiveExplorer/Reflector.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace FolderSelect 5 | { 6 | /// 7 | /// This class is from the Front-End for Dosbox and is used to present a 'vista' dialog box to select folders. 8 | /// Being able to use a vista style dialog box to select folders is much better then using the shell folder browser. 9 | /// http://code.google.com/p/fed/ 10 | /// 11 | /// Example: 12 | /// var r = new Reflector("System.Windows.Forms"); 13 | /// 14 | public class Reflector 15 | { 16 | #region variables 17 | 18 | string m_ns; 19 | Assembly m_asmb; 20 | 21 | #endregion 22 | 23 | #region Constructors 24 | 25 | /// 26 | /// Constructor 27 | /// 28 | /// The namespace containing types to be used 29 | public Reflector(string ns) 30 | : this(ns, ns) 31 | { } 32 | 33 | /// 34 | /// Constructor 35 | /// 36 | /// A specific assembly name (used if the assembly name does not tie exactly with the namespace) 37 | /// The namespace containing types to be used 38 | public Reflector(string an, string ns) 39 | { 40 | m_ns = ns; 41 | m_asmb = null; 42 | foreach (AssemblyName aN in Assembly.GetExecutingAssembly().GetReferencedAssemblies()) 43 | { 44 | if (aN.FullName.StartsWith(an)) 45 | { 46 | m_asmb = Assembly.Load(aN); 47 | break; 48 | } 49 | } 50 | } 51 | 52 | #endregion 53 | 54 | #region Methods 55 | 56 | /// 57 | /// Return a Type instance for a type 'typeName' 58 | /// 59 | /// The name of the type 60 | /// A type instance 61 | public Type GetType(string typeName) 62 | { 63 | Type type = null; 64 | string[] names = typeName.Split('.'); 65 | 66 | if (names.Length > 0) 67 | type = m_asmb.GetType(m_ns + "." + names[0]); 68 | 69 | for (int i = 1; i < names.Length; ++i) { 70 | type = type.GetNestedType(names[i], BindingFlags.NonPublic); 71 | } 72 | return type; 73 | } 74 | 75 | /// 76 | /// Create a new object of a named type passing along any params 77 | /// 78 | /// The name of the type to create 79 | /// 80 | /// An instantiated type 81 | public object New(string name, params object[] parameters) 82 | { 83 | Type type = GetType(name); 84 | 85 | ConstructorInfo[] ctorInfos = type.GetConstructors(); 86 | foreach (ConstructorInfo ci in ctorInfos) { 87 | try { 88 | return ci.Invoke(parameters); 89 | } catch { } 90 | } 91 | 92 | return null; 93 | } 94 | 95 | /// 96 | /// Calls method 'func' on object 'obj' passing parameters 'parameters' 97 | /// 98 | /// The object on which to excute function 'func' 99 | /// The function to execute 100 | /// The parameters to pass to function 'func' 101 | /// The result of the function invocation 102 | public object Call(object obj, string func, params object[] parameters) 103 | { 104 | return Call2(obj, func, parameters); 105 | } 106 | 107 | /// 108 | /// Calls method 'func' on object 'obj' passing parameters 'parameters' 109 | /// 110 | /// The object on which to excute function 'func' 111 | /// The function to execute 112 | /// The parameters to pass to function 'func' 113 | /// The result of the function invocation 114 | public object Call2(object obj, string func, object[] parameters) 115 | { 116 | return CallAs2(obj.GetType(), obj, func, parameters); 117 | } 118 | 119 | /// 120 | /// Calls method 'func' on object 'obj' which is of type 'type' passing parameters 'parameters' 121 | /// 122 | /// The type of 'obj' 123 | /// The object on which to excute function 'func' 124 | /// The function to execute 125 | /// The parameters to pass to function 'func' 126 | /// The result of the function invocation 127 | public object CallAs(Type type, object obj, string func, params object[] parameters) 128 | { 129 | return CallAs2(type, obj, func, parameters); 130 | } 131 | 132 | /// 133 | /// Calls method 'func' on object 'obj' which is of type 'type' passing parameters 'parameters' 134 | /// 135 | /// The type of 'obj' 136 | /// The object on which to excute function 'func' 137 | /// The function to execute 138 | /// The parameters to pass to function 'func' 139 | /// The result of the function invocation 140 | public object CallAs2(Type type, object obj, string func, object[] parameters) { 141 | MethodInfo methInfo = type.GetMethod(func, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 142 | return methInfo.Invoke(obj, parameters); 143 | } 144 | 145 | /// 146 | /// Returns the value of property 'prop' of object 'obj' 147 | /// 148 | /// The object containing 'prop' 149 | /// The property name 150 | /// The property value 151 | public object Get(object obj, string prop) 152 | { 153 | return GetAs(obj.GetType(), obj, prop); 154 | } 155 | 156 | /// 157 | /// Returns the value of property 'prop' of object 'obj' which has type 'type' 158 | /// 159 | /// The type of 'obj' 160 | /// The object containing 'prop' 161 | /// The property name 162 | /// The property value 163 | public object GetAs(Type type, object obj, string prop) { 164 | PropertyInfo propInfo = type.GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 165 | return propInfo.GetValue(obj, null); 166 | } 167 | 168 | /// 169 | /// Returns an enum value 170 | /// 171 | /// The name of enum type 172 | /// The name of the value 173 | /// The enum value 174 | public object GetEnum(string typeName, string name) { 175 | Type type = GetType(typeName); 176 | FieldInfo fieldInfo = type.GetField(name); 177 | return fieldInfo.GetValue(null); 178 | } 179 | 180 | #endregion 181 | 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Library/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # 2 space indentation 12 | [*.cs] 13 | indent_style = space 14 | indent_size = 2 -------------------------------------------------------------------------------- /Library/Ark/ArkDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * ArkDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | 22 | namespace GameArchives.Ark 23 | { 24 | class ArkDirectory : DefaultDirectory 25 | { 26 | public ArkDirectory(IDirectory parent, string name) : base(parent, name) 27 | { 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Library/Ark/HdrCryptStream.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * HdrCryptStream.cs 3 | * 4 | * Copyright (c) 2015,2016,2017 maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.IO; 22 | 23 | namespace GameArchives.Ark 24 | { 25 | class HdrCryptStream : Stream 26 | { 27 | private long position; 28 | private int key; 29 | private int curKey; 30 | private long keypos; 31 | private Stream file; 32 | public byte xor; 33 | 34 | internal HdrCryptStream(Stream file, byte xor = 0) 35 | { 36 | file.Position = 0; 37 | // The initial key is found in the first 4 bytes. 38 | this.key = cryptRound(file.ReadInt32LE()); 39 | this.curKey = this.key; 40 | this.file = file; 41 | this.Length = file.Length - 4; 42 | this.xor = xor; 43 | } 44 | 45 | public override bool CanRead => position < Length && position >= 0; 46 | public override bool CanSeek => true; 47 | public override bool CanWrite => false; 48 | public override long Length { get; } 49 | 50 | public override long Position 51 | { 52 | get 53 | { 54 | return position; 55 | } 56 | 57 | set 58 | { 59 | Seek(value, SeekOrigin.Begin); 60 | } 61 | } 62 | 63 | private void updateKey() 64 | { 65 | if (keypos == position) 66 | return; 67 | if (keypos > position) // reset key 68 | { 69 | keypos = 0; 70 | curKey = key; 71 | } 72 | while (keypos < position) // don't think there's a faster way to do this 73 | { 74 | curKey = cryptRound(curKey); 75 | keypos++; 76 | } 77 | } 78 | 79 | private int cryptRound(int key) 80 | { 81 | int ret = (key - ((key / 0x1F31D) * 0x1F31D)) * 0x41A7 - (key / 0x1F31D) * 0xB14; 82 | if (ret <= 0) 83 | ret += 0x7FFFFFFF; 84 | return ret; 85 | } 86 | 87 | public override int Read(byte[] buffer, int offset, int count) 88 | { 89 | // ensure file is at correct offset 90 | file.Seek(this.position + 4, SeekOrigin.Begin); 91 | if (offset + count > buffer.Length) 92 | { 93 | throw new IndexOutOfRangeException("Attempt to fill buffer past its end"); 94 | } 95 | if (this.Position == this.Length || this.Position + count > this.Length) 96 | { 97 | count = (int)(this.Length - this.Position); 98 | //throw new System.IO.EndOfStreamException("Cannot read past end of file."); 99 | } 100 | 101 | int bytesRead = file.Read(buffer, offset, count); 102 | 103 | for (uint i = 0; i < bytesRead; i++) 104 | { 105 | buffer[offset + i] ^= (byte)(this.curKey ^ xor); 106 | this.position++; 107 | updateKey(); 108 | } 109 | return bytesRead; 110 | } 111 | 112 | public override long Seek(long offset, SeekOrigin origin) 113 | { 114 | int adjust = origin == SeekOrigin.Current ? 0 : 4; 115 | this.position = file.Seek(offset + adjust, origin) - 4; 116 | updateKey(); 117 | return position; 118 | } 119 | 120 | #region Not Used 121 | 122 | public override void Flush() 123 | { 124 | throw new NotSupportedException(); 125 | } 126 | 127 | public override void SetLength(long value) 128 | { 129 | throw new NotSupportedException(); 130 | } 131 | 132 | public override void Write(byte[] buffer, int offset, int count) 133 | { 134 | throw new NotSupportedException(); 135 | } 136 | 137 | #endregion 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Library/Ark/ProtectedFileStream.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * ProtectedFileStream.cs 3 | * 4 | * Copyright (c) 2017 maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.IO; 22 | 23 | namespace GameArchives.Ark 24 | { 25 | /// 26 | /// A "protected file" wrapper. 27 | /// 28 | public class ProtectedFileStream : Stream 29 | { 30 | public override bool CanRead => true; 31 | 32 | public override bool CanSeek => true; 33 | 34 | public override bool CanWrite => false; 35 | 36 | public override long Length { get; } 37 | 38 | private Stream pkg; 39 | private long data_offset; 40 | private byte initialKey; 41 | private byte key; 42 | 43 | private long _position; 44 | public override long Position 45 | { 46 | get 47 | { 48 | return _position; 49 | } 50 | 51 | set 52 | { 53 | Seek(value, SeekOrigin.Begin); 54 | } 55 | } 56 | 57 | /// 58 | /// Constructs a new protected file stream from the given base stream. 59 | /// 60 | /// The base stream 61 | public ProtectedFileStream(Stream package) 62 | { 63 | this.pkg = package; 64 | package.Seek(-36, SeekOrigin.End); 65 | var size = package.ReadInt32LE(); 66 | package.Seek(-size, SeekOrigin.End); 67 | var metadata = new byte[size]; 68 | package.Read(metadata, 0, size); 69 | initialKey = CalculateKeyByte(metadata); 70 | 71 | data_offset = 0; 72 | Length = package.Length - size; 73 | } 74 | 75 | 76 | 77 | public override int Read(byte[] buffer, int offset, int count) 78 | { 79 | pkg.Seek(data_offset + Position, SeekOrigin.Begin); 80 | if (count + Position > Length) 81 | { 82 | count = (int)(Length - Position); 83 | } 84 | int bytes_read = pkg.Read(buffer, offset, count); 85 | 86 | for(int i = 0; i < bytes_read; i++) 87 | { 88 | byte tmp = buffer[offset+i]; 89 | buffer[offset+i] ^= key; 90 | key = (byte)((initialKey ^ tmp) - _position); 91 | _position++; 92 | } 93 | return bytes_read; 94 | } 95 | 96 | public override long Seek(long offset, SeekOrigin origin) 97 | { 98 | switch (origin) 99 | { 100 | case SeekOrigin.Begin: 101 | break; 102 | case SeekOrigin.Current: 103 | offset += _position; 104 | break; 105 | case SeekOrigin.End: 106 | offset += Length; 107 | break; 108 | } 109 | if (offset > Length) 110 | { 111 | offset = Length; 112 | } 113 | else if (offset < 0) 114 | { 115 | offset = 0; 116 | } 117 | _position = offset; 118 | 119 | if(_position > 0) 120 | { 121 | pkg.Position = data_offset + _position - 1; 122 | key = (byte)((initialKey ^ pkg.ReadByte()) - _position + 1); 123 | } 124 | else if(_position == 0) 125 | { 126 | key = initialKey; 127 | } 128 | 129 | return _position; 130 | } 131 | 132 | private static uint RotL(uint value, int count) 133 | { 134 | const int bits = 32; 135 | count %= bits; 136 | 137 | uint high = value >> (bits - count); 138 | value <<= count; 139 | value |= high; 140 | return value; 141 | } 142 | 143 | private static byte BYTE(int num, uint value) 144 | { 145 | return (byte)(value >> (num * 8)); 146 | } 147 | 148 | private static uint Mangle(byte[] bytes, int offset, int count) 149 | { 150 | var mangled = 0U; 151 | for(var i = 0; i < count; i++) 152 | { 153 | mangled = bytes[offset + i] ^ 2 * (bytes[offset + i] + mangled); 154 | } 155 | return mangled; 156 | } 157 | 158 | private static uint Fold(uint value) 159 | { 160 | return (uint)(BYTE(0, value) + BYTE(1, value) + BYTE(2, value) + BYTE(3, value)); 161 | } 162 | 163 | private static uint Hash(byte[] key, int offset, long count) 164 | { 165 | uint tmp; 166 | 167 | byte counter = 0; 168 | var seed = 0xE3AFEC21; 169 | for (var i = 0L; i < count; i++) 170 | { 171 | tmp = (key[offset + i] ^ Fold(seed)); 172 | key[offset + i] = (byte)tmp; 173 | seed = RotL((tmp | ((tmp | ((tmp | (tmp << 8)) << 8)) << 8)) + RotL(seed, (int)(tmp & 0x1F)), 1); 174 | if (counter > 16) 175 | { 176 | seed = (2 * seed); 177 | counter = 0; 178 | } 179 | counter++; 180 | } 181 | return seed; 182 | } 183 | 184 | private static byte CalculateKeyByte(byte[] metadata) 185 | { 186 | var word_0xE = BitConverter.ToUInt16(metadata, 0xE); 187 | 188 | byte mangled = (byte)Fold( 189 | Mangle(metadata, 4, 9) + 190 | Mangle(metadata, 0, 4) + 191 | Mangle(metadata, 13, 1) + 192 | Mangle(metadata, 16, 4) + 193 | Mangle(metadata, 24, word_0xE)); 194 | 195 | Hash(metadata, 24, word_0xE); 196 | Hash(metadata, 13, 1); 197 | Hash(metadata, 16, 4); 198 | Hash(metadata, 0, 4); 199 | Hash(metadata, 4, 9); 200 | 201 | return (byte)(metadata[5] ^ mangled); 202 | } 203 | 204 | #region Not Supported 205 | public override void Flush() 206 | { 207 | throw new NotSupportedException(); 208 | } 209 | public override void SetLength(long value) 210 | { 211 | throw new NotSupportedException(); 212 | } 213 | 214 | public override void Write(byte[] buffer, int offset, int count) 215 | { 216 | throw new NotSupportedException(); 217 | } 218 | #endregion 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /Library/Common/DefaultDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * DefaultDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System.Collections.Generic; 21 | 22 | namespace GameArchives.Common 23 | { 24 | /// 25 | /// A default implementation of a directory. 26 | /// Useful for archives where directories are implicit. 27 | /// Important: File and directory names are case-insensitive. 28 | /// 29 | public class DefaultDirectory : IDirectory 30 | { 31 | protected SortedDictionary files; 32 | protected SortedDictionary dirs; 33 | 34 | public string Name { get; } 35 | 36 | public IDirectory Parent { get; } 37 | 38 | public virtual ICollection Dirs => dirs.Values; 39 | public virtual ICollection Files => files.Values; 40 | 41 | public virtual bool TryGetDirectory(string name, out IDirectory dir) 42 | { 43 | return dirs.TryGetValue(name.ToLower(), out dir); 44 | } 45 | 46 | public IDirectory GetDirectory(string name) 47 | { 48 | IDirectory ret; 49 | if (TryGetDirectory(name, out ret)) 50 | return ret; 51 | throw new System.IO.DirectoryNotFoundException("Unable to find the directory " + name); 52 | } 53 | 54 | public virtual bool TryGetFile(string name, out IFile file) 55 | { 56 | return files.TryGetValue(name.ToLower(), out file); 57 | } 58 | 59 | public IFile GetFile(string name) 60 | { 61 | IFile ret; 62 | if (TryGetFile(name, out ret)) 63 | return ret; 64 | throw new System.IO.FileNotFoundException("Unable to find the file " + name); 65 | } 66 | 67 | public IFile GetFileAtPath(string path) 68 | { 69 | if (path[0] == AbstractPackage.PATH_SEPARATOR) 70 | path = path.Substring(1); 71 | string[] breadcrumbs = path.Split('/'); 72 | if(breadcrumbs.Length == 1) 73 | { 74 | return GetFile(breadcrumbs[0]); 75 | } 76 | string newPath = string.Join(AbstractPackage.PATH_SEPARATOR.ToString(), breadcrumbs, 1, breadcrumbs.Length - 1); 77 | return GetDirectory(breadcrumbs[0]).GetFileAtPath(newPath); 78 | } 79 | 80 | internal void AddFile(IFile f) 81 | { 82 | if (!files.ContainsKey(f.Name.ToLower())) 83 | { 84 | files.Add(f.Name.ToLower(), f); 85 | } 86 | } 87 | 88 | internal void AddDir(IDirectory d) 89 | { 90 | if (!dirs.ContainsKey(d.Name.ToLower())) 91 | { 92 | dirs.Add(d.Name.ToLower(), d); 93 | } 94 | } 95 | 96 | internal void GetAllNodes() 97 | { 98 | 99 | } 100 | 101 | internal DefaultDirectory(IDirectory parent, string name) 102 | { 103 | Parent = parent; 104 | Name = name; 105 | files = new SortedDictionary(); 106 | dirs = new SortedDictionary(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Library/Common/MultiStream.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * MultiStream.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.Linq; 24 | using System.Text; 25 | 26 | namespace GameArchives.Common 27 | { 28 | /// 29 | /// A single stream which is actually made up of a number of streams in sequence. 30 | /// 31 | class MultiStream : Stream 32 | { 33 | private IList parts; 34 | private IList partSizes; 35 | private long position; 36 | 37 | internal MultiStream(IList streams) 38 | { 39 | var length = 0L; 40 | partSizes = new List(streams.Count); 41 | foreach(var stream in streams) 42 | { 43 | if(!stream.CanSeek) 44 | { 45 | throw new Exception("All component streams must be seekable."); 46 | } 47 | if(!stream.CanRead) 48 | { 49 | throw new Exception("All component streams must be readable."); 50 | } 51 | partSizes.Add(stream.Length); 52 | length += stream.Length; 53 | } 54 | Length = length; 55 | parts = streams; 56 | } 57 | 58 | /// 59 | /// Denotes whether the stream can be read from. 60 | /// 61 | public override bool CanRead => true; 62 | 63 | /// 64 | /// Denotes whether the user can seek this stream. 65 | /// 66 | public override bool CanSeek => true; 67 | 68 | /// 69 | /// Denotes whether the user can write to this stream. 70 | /// 71 | public override bool CanWrite => false; 72 | 73 | /// 74 | /// The total length of this file. 75 | /// 76 | public override long Length { get; } 77 | 78 | /// 79 | /// The current position the stream points to within the file. 80 | /// 81 | public override long Position 82 | { 83 | get { return position; } 84 | set 85 | { 86 | if (value < 0) 87 | { 88 | throw new ArgumentOutOfRangeException("Attempted to seek to before the beginning of the file."); 89 | } 90 | if (value > Length) 91 | { 92 | throw new System.IO.EndOfStreamException("Attempted to seek past the end of the file."); 93 | } 94 | position = value; 95 | } 96 | } 97 | 98 | /// 99 | /// Not implemented; read-only stream. 100 | /// 101 | public override void Flush() 102 | { 103 | throw new NotSupportedException(); 104 | } 105 | 106 | /// 107 | /// Reads `count` bytes into `buffer` at offset `offset`. 108 | /// 109 | /// 110 | /// 111 | /// 112 | /// 113 | public override int Read(byte[] buffer, int offset, int count) 114 | { 115 | if (offset + count > buffer.Length) 116 | { 117 | throw new IndexOutOfRangeException("Attempt to fill buffer past its end"); 118 | } 119 | if (this.Position == this.Length || this.Position + count > this.Length) 120 | { 121 | count = (int)(this.Length - this.Position); 122 | //throw new System.IO.EndOfStreamException("Cannot read past end of file."); 123 | } 124 | 125 | int totalBytesRead = 0; 126 | while(count > 0) 127 | { 128 | Stream current; 129 | long current_position = offsetToStream(position, out current); 130 | current.Position = current_position; 131 | int bytesRead = current.Read(buffer, offset, count); 132 | offset += bytesRead; 133 | count -= bytesRead; 134 | position += bytesRead; 135 | totalBytesRead += bytesRead; 136 | } 137 | return totalBytesRead; 138 | } 139 | 140 | /// 141 | /// Get the correct stream and offset. 142 | /// Returns the offset into that stream. 143 | /// 144 | /// 145 | /// 146 | /// 147 | private long offsetToStream(long offset, out Stream s) 148 | { 149 | for (var i = 0; i < parts.Count; i++) 150 | { 151 | if (partSizes[i] > offset) 152 | { 153 | s = parts[i]; 154 | return offset; 155 | } 156 | offset -= partSizes[i]; 157 | } 158 | throw new ArgumentOutOfRangeException("Desired offset extends past final part file."); 159 | } 160 | 161 | /// 162 | /// Seek the stream to given position within the file relative to given origin. 163 | /// 164 | /// 165 | /// 166 | /// 167 | public override long Seek(long offset, System.IO.SeekOrigin origin) 168 | { 169 | switch (origin) 170 | { 171 | case System.IO.SeekOrigin.Begin: 172 | Position = offset; 173 | break; 174 | case System.IO.SeekOrigin.Current: 175 | Position = Position + offset; 176 | break; 177 | case System.IO.SeekOrigin.End: 178 | Position = Length + offset; 179 | break; 180 | } 181 | return Position; 182 | } 183 | 184 | public override void Close() 185 | { 186 | base.Close(); 187 | foreach (var s in parts) 188 | { 189 | s.Close(); 190 | } 191 | } 192 | 193 | /// 194 | /// Not implemented; read-only stream. 195 | /// 196 | /// 197 | public override void SetLength(long value) 198 | { 199 | throw new NotSupportedException(); 200 | } 201 | 202 | /// 203 | /// Not implemented; read-only stream. 204 | /// 205 | /// 206 | /// 207 | /// 208 | public override void Write(byte[] buffer, int offset, int count) 209 | { 210 | throw new NotSupportedException(); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /Library/Common/OffsetFile.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * OffsetFile.cs 3 | * 4 | * Copyright (c) 2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.Linq; 24 | using System.Text; 25 | 26 | namespace GameArchives.Common 27 | { 28 | /// 29 | /// An uncompressed file which is simply a number of bytes at a certain offset in a stream. 30 | /// 31 | public class OffsetFile : IFile 32 | { 33 | public bool Compressed => false; 34 | public long CompressedSize => Size; 35 | public IDictionary ExtendedInfo { get; } 36 | public string Name { get; } 37 | public IDirectory Parent { get; } 38 | public long Size { get; protected set; } 39 | public Stream Stream => GetStream(); 40 | 41 | public long DataLocation => data_offset; 42 | 43 | private Stream img_file; 44 | private long data_offset; 45 | private const int BUFFER_SIZE = 8192; 46 | private Func wrapStream = null; 47 | 48 | /// 49 | /// Constructs a new OffsetFile 50 | /// 51 | /// The name of the file, including extension 52 | /// The directory in which this file resides 53 | /// Stream which contains this file 54 | /// Offset into the stream at which the file starts 55 | /// Length in bytes of the file 56 | public OffsetFile(string name, IDirectory parent, Stream img, long offset, long size, Func wrapStream = null) 57 | { 58 | Name = name; 59 | Parent = parent; 60 | Size = size; 61 | img_file = img; 62 | data_offset = offset; 63 | ExtendedInfo = new Dictionary(); 64 | this.wrapStream = wrapStream; 65 | } 66 | 67 | public byte[] GetBytes() 68 | { 69 | byte[] bytes; 70 | using (var stream = this.GetStream()) 71 | { 72 | bytes = stream.ReadBytes((int)Size); 73 | } 74 | return bytes; 75 | } 76 | 77 | public Stream GetStream() 78 | { 79 | var s = new BufferedStream(new OffsetStream(img_file, data_offset, Size), BUFFER_SIZE); 80 | if (wrapStream != null) 81 | { 82 | return wrapStream(s); 83 | } 84 | return s; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Library/Common/OffsetStream.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * OffsetStream.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.IO; 22 | 23 | namespace GameArchives.Common 24 | { 25 | /// 26 | /// A stream based on another stream, useful for representing 27 | /// file streams within simple archive packages. 28 | /// 29 | public class OffsetStream : Stream 30 | { 31 | public override bool CanRead => true; 32 | 33 | public override bool CanSeek => true; 34 | 35 | public override bool CanWrite => false; 36 | 37 | public override long Length { get; } 38 | 39 | private Stream pkg; 40 | private long data_offset; 41 | 42 | private long _position; 43 | public override long Position 44 | { 45 | get 46 | { 47 | return _position; 48 | } 49 | 50 | set 51 | { 52 | Seek(value, SeekOrigin.Begin); 53 | } 54 | } 55 | 56 | /// 57 | /// Constructs a new offset stream on the given base stream with the given offset and length. 58 | /// 59 | /// The base stream 60 | /// Offset into the base stream where this stream starts 61 | /// Number of bytes in this stream 62 | public OffsetStream(Stream package, long offset, long length) 63 | { 64 | this.pkg = package; 65 | this.data_offset = offset; 66 | Length = length; 67 | } 68 | 69 | public override int Read(byte[] buffer, int offset, int count) 70 | { 71 | pkg.Seek(data_offset + Position, SeekOrigin.Begin); 72 | if (count + Position > Length) 73 | { 74 | count = (int)(Length - Position); 75 | } 76 | int bytes_read = pkg.Read(buffer, offset, count); 77 | _position += bytes_read; 78 | return bytes_read; 79 | } 80 | 81 | public override long Seek(long offset, SeekOrigin origin) 82 | { 83 | switch (origin) 84 | { 85 | case SeekOrigin.Begin: 86 | break; 87 | case SeekOrigin.Current: 88 | offset += _position; 89 | break; 90 | case SeekOrigin.End: 91 | offset += Length; 92 | break; 93 | } 94 | if (offset > Length) 95 | { 96 | offset = Length; 97 | } 98 | else if (offset < 0) 99 | { 100 | offset = 0; 101 | } 102 | _position = offset; 103 | return _position; 104 | } 105 | 106 | #region Not Supported 107 | public override void Flush() 108 | { 109 | throw new NotSupportedException(); 110 | } 111 | public override void SetLength(long value) 112 | { 113 | throw new NotSupportedException(); 114 | } 115 | 116 | public override void Write(byte[] buffer, int offset, int count) 117 | { 118 | throw new NotSupportedException(); 119 | } 120 | #endregion 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /Library/FSAR/AesCryptStream.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * AesCryptStream.cs 3 | * 4 | * Copyright (c) 2019 maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.IO; 22 | using System.Security.Cryptography; 23 | 24 | namespace GameArchives.FSAR 25 | { 26 | public class AesCtrStream : Stream 27 | { 28 | Stream s; 29 | long offset; 30 | long position; 31 | AesManaged aes; 32 | byte[] initialIv; 33 | byte[] counter; 34 | byte[] cryptedCounter = new byte[16]; 35 | bool closeStream; 36 | public AesCtrStream(Stream input, byte[] key, byte[] iv, long offset = 0, bool shouldClose = false) 37 | { 38 | s = input; 39 | initialIv = (byte[])iv.Clone(); 40 | counter = (byte[])iv.Clone(); 41 | this.offset = offset; 42 | closeStream = shouldClose; 43 | aes = new AesManaged() 44 | { 45 | Mode = CipherMode.ECB, 46 | BlockSize = 128, 47 | KeySize = 128, 48 | Padding = PaddingMode.None, 49 | Key = key, 50 | }; 51 | } 52 | 53 | private void resetCounter() 54 | { 55 | // TODO: optimize this 56 | Buffer.BlockCopy(initialIv, 0, counter, 0, 16); 57 | var block = position / 16; 58 | counterBlock = 0; 59 | for (long i = 0; i < block; i++) 60 | { 61 | IncrementCounter(); 62 | } 63 | } 64 | 65 | private long counterBlock = 0; 66 | private void IncrementCounter() 67 | { 68 | counterBlock++; 69 | for (int j = 0; j < 16; j++) 70 | { 71 | counter[j]++; 72 | if (counter[j] != 0) 73 | break; 74 | } 75 | } 76 | public override void Close() 77 | { 78 | if (closeStream) 79 | s.Close(); 80 | base.Close(); 81 | } 82 | public override bool CanRead => position < Length; 83 | 84 | public override bool CanSeek => true; 85 | 86 | public override bool CanWrite => false; 87 | 88 | public override long Length => s.Length - offset; 89 | 90 | public override long Position { get => position; set { position = value; resetCounter(); } } 91 | 92 | public override void Flush() 93 | { 94 | throw new NotImplementedException(); 95 | } 96 | 97 | public override int Read(byte[] buffer, int bufOffset, int count) 98 | { 99 | if (position + count > Length) 100 | { 101 | count = (int)(Length - position); 102 | } 103 | 104 | s.Position = position + offset; 105 | int bytesRead = s.Read(buffer, bufOffset, count); 106 | 107 | // Create a decrytor to perform the stream transform. 108 | ICryptoTransform encryptor = aes.CreateEncryptor(); 109 | int counterLoc = (int)(position % 16); 110 | encryptor.TransformBlock(counter, 0, counter.Length, cryptedCounter, 0); 111 | for (int i = 0; i < bytesRead; i++) 112 | { 113 | if (position / 16 != counterBlock) 114 | { 115 | IncrementCounter(); 116 | counterLoc = 0; 117 | encryptor.TransformBlock(counter, 0, counter.Length, cryptedCounter, 0); 118 | } 119 | buffer[bufOffset++] ^= cryptedCounter[counterLoc++]; //decrypt one byte 120 | position++; 121 | } 122 | 123 | return bytesRead; 124 | } 125 | 126 | public override long Seek(long offset, SeekOrigin origin) 127 | { 128 | switch (origin) 129 | { 130 | case SeekOrigin.Begin: 131 | Position = offset; 132 | break; 133 | case SeekOrigin.Current: 134 | Position += offset; 135 | break; 136 | case SeekOrigin.End: 137 | Position = Length + offset; 138 | break; 139 | default: 140 | break; 141 | } 142 | return position; 143 | } 144 | 145 | public override void SetLength(long value) 146 | { 147 | throw new NotImplementedException(); 148 | } 149 | 150 | public override void Write(byte[] buffer, int offset, int count) 151 | { 152 | throw new NotImplementedException(); 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /Library/FSAR/FSARDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * FSARDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | 22 | namespace GameArchives.FSAR 23 | { 24 | class FSARDirectory : DefaultDirectory 25 | { 26 | public FSARDirectory(IDirectory parent, string name) : base(parent, name) 27 | { 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Library/FSAR/FSARFile.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * FSARFile.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.Linq; 24 | using System.Text; 25 | using System.IO.Compression; 26 | using GameArchives.Common; 27 | 28 | namespace GameArchives.FSAR 29 | { 30 | class FSARFile : IFile 31 | { 32 | public string Name { get; } 33 | public IDirectory Parent { get; } 34 | public long Size { get; } 35 | public bool Compressed { get; } 36 | public long CompressedSize { get; } 37 | public IDictionary ExtendedInfo { get; } 38 | public Stream Stream => GetStream(); 39 | 40 | private long offset; 41 | private Stream archive; 42 | 43 | public FSARFile(string n, IDirectory p, long size, bool compressed, 44 | long zsize, long offset, Stream archive) 45 | { 46 | Name = n; 47 | Parent = p; 48 | Size = size; 49 | Compressed = compressed; 50 | CompressedSize = zsize; 51 | this.offset = offset; 52 | this.archive = archive; 53 | ExtendedInfo = new Dictionary(); 54 | } 55 | 56 | public byte[] GetBytes() 57 | { 58 | byte[] bytes = new byte[Size]; 59 | if(Size > Int32.MaxValue) 60 | { 61 | throw new NotSupportedException("Can't read bytes for file larger than int32 max, yet."); 62 | } 63 | using (var stream = this.GetStream()) 64 | { 65 | stream.Read(bytes, 0, (int)Size); 66 | } 67 | return bytes; 68 | } 69 | 70 | public Stream GetStream() 71 | { 72 | if (!Compressed) 73 | return new OffsetStream(archive, offset, Size); 74 | else 75 | return new DeflateStream(new OffsetStream(archive, offset + 2, CompressedSize - 2), 76 | CompressionMode.Decompress); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Library/FSAR/FSARPackage.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * FSARPackage.cs 3 | * 4 | * Copyright (c) 2015,2016,2019 maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.Linq; 24 | using System.Text; 25 | 26 | namespace GameArchives.FSAR 27 | { 28 | public class FSARPackage : AbstractPackage 29 | { 30 | const uint kFSAR = 0x46534152; 31 | const uint kFSGC = 0x46534743; 32 | public static PackageTestResult IsFSAR(IFile f) 33 | { 34 | using (Stream s = f.GetStream()) 35 | { 36 | s.Position = 0; 37 | switch(s.ReadUInt32BE()) 38 | { 39 | case kFSAR: 40 | return PackageTestResult.YES; 41 | case kFSGC: 42 | return PackageTestResult.MAYBE; 43 | default: 44 | return PackageTestResult.NO; 45 | } 46 | } 47 | } 48 | 49 | public static FSARPackage FromFile(IFile f) 50 | { 51 | return new FSARPackage(f); 52 | } 53 | 54 | public override string FileName { get; } 55 | 56 | public override IDirectory RootDirectory => root; 57 | 58 | public override long Size => filestream.Length; 59 | 60 | public override bool Writeable => false; 61 | 62 | public override Type FileType => typeof(FSARFile); 63 | 64 | private Stream filestream; 65 | private FSARDirectory root; 66 | private static byte[] fsgcKey = new byte[] 67 | { 68 | 0x47, 0x3F, 0x2A, 0xD8, 0xCA, 0x3B, 0xBC, 0xF7, 69 | 0xAD, 0x71, 0x5D, 0xE7, 0x90, 0x96, 0x2E, 0xFE 70 | }; 71 | private static byte[] fsgcCtr = new byte[] 72 | { 73 | 0xE0, 0xAC, 0x52, 0x9C, 0x1B, 0x97, 0x3B, 0x27, 74 | 0x65, 0x89, 0x78, 0x46, 0x30, 0x82, 0x58, 0x3E, 75 | }; 76 | 77 | /// 78 | /// Open the .far archive which is the given file. 79 | /// 80 | /// 81 | public FSARPackage(IFile f) 82 | { 83 | FileName = f.Name; 84 | root = new FSARDirectory(null, ROOT_DIR); 85 | filestream = f.GetStream(); 86 | uint magic = filestream.ReadUInt32BE(); 87 | if(magic == kFSGC) // "FSGC" 88 | { 89 | filestream = new AesCtrStream(filestream, fsgcKey, fsgcCtr, 8, true); 90 | magic = filestream.ReadUInt32BE(); 91 | } 92 | if (magic != kFSAR) // "FSAR" 93 | { 94 | throw new InvalidDataException("File does not have a valid FSAR header."); 95 | } 96 | filestream.Position = 8; 97 | uint file_base = filestream.ReadUInt32BE(); 98 | uint num_files = filestream.ReadUInt32BE(); 99 | filestream.Position = 0x20; 100 | for(int i = 0; i < num_files; i++) 101 | { 102 | filestream.Position = 0x20 + 0x120 * i; 103 | string fpath = filestream.ReadASCIINullTerminated(); 104 | filestream.Position = 0x20 + 0x120 * i + 0x100; 105 | long size = filestream.ReadInt64BE(); 106 | long zsize = filestream.ReadInt64BE(); 107 | long offset = filestream.ReadInt64BE(); 108 | uint zipped = filestream.ReadUInt32BE(); 109 | FSARDirectory dir = makeOrGetDir(fpath); 110 | string filename = fpath.Split('\\').Last(); 111 | dir.AddFile(new FSARFile(filename, dir, size, zipped != 1, zsize, offset + file_base, filestream)); 112 | } 113 | } 114 | 115 | /// 116 | /// Get the directory at the end of this path, or make it (and all 117 | /// intermediate dirs) if it doesn't exist. 118 | /// 119 | /// 120 | /// 121 | private FSARDirectory makeOrGetDir(string path) 122 | { 123 | string[] breadcrumbs = path.Split('\\'); 124 | IDirectory last = root; 125 | IDirectory current; 126 | if (breadcrumbs.Length == 1) 127 | { 128 | return root; 129 | } 130 | 131 | for (var idx = 0; idx < breadcrumbs.Length - 1; idx++) 132 | { 133 | if (!last.TryGetDirectory(breadcrumbs[idx], out current)) 134 | { 135 | current = new FSARDirectory(last, breadcrumbs[idx]); 136 | (last as FSARDirectory).AddDir(current as FSARDirectory); 137 | } 138 | last = current; 139 | } 140 | return last as FSARDirectory; 141 | } 142 | 143 | public override void Dispose() 144 | { 145 | filestream.Close(); 146 | filestream.Dispose(); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Library/FSGIMG/FSGIMGDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * FSGIMGDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using GameArchives.Common; 22 | 23 | namespace GameArchives.FSGIMG 24 | { 25 | class FSGIMGDirectory : DefaultDirectory 26 | { 27 | public long Offset { get; } 28 | public FSGIMGDirectory(IDirectory parent, string filename, long offset) : base(parent, filename) 29 | { 30 | Offset = offset; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Library/FSGIMG/FSGIMGPackage.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * FSGIMGPackage.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | using System; 22 | using System.Collections.Generic; 23 | using System.IO; 24 | 25 | namespace GameArchives.FSGIMG 26 | { 27 | public class FSGIMGPackage : AbstractPackage 28 | { 29 | public static PackageTestResult IsFSGIMG(IFile f) 30 | { 31 | using (Stream fs = f.GetStream()) 32 | return fs.ReadASCIINullTerminated(16) == "FSG-FILE-SYSTEM" ? PackageTestResult.YES : PackageTestResult.NO; 33 | } 34 | public static FSGIMGPackage OpenFile(IFile f) 35 | { 36 | return new FSGIMGPackage(f); 37 | } 38 | 39 | public override string FileName { get; } 40 | 41 | public override IDirectory RootDirectory => root; 42 | public override long Size => filestream.Length; 43 | public override bool Writeable => false; 44 | public override Type FileType => typeof(OffsetFile); 45 | 46 | private Stream filestream; 47 | private FSGIMGDirectory root; 48 | 49 | class file_descriptor 50 | { 51 | public uint filename_hash; 52 | public byte type; 53 | public uint offset; 54 | public long data_offset; 55 | public long size; 56 | } 57 | 58 | public FSGIMGPackage(IFile f) 59 | { 60 | FileName = f.Name; 61 | var parts = new List(1); 62 | parts.Add(f.GetStream()); 63 | if (f.Name.Split('.').Length > 2) // this is a .part* file 64 | { 65 | int partNum = 1; 66 | IFile tmp; 67 | while (f.Parent.TryGetFile($"{f.Name.Substring(0, f.Name.Length - 3)}{partNum:d3}", out tmp)) 68 | { 69 | var fs = tmp.GetStream(); 70 | parts.Add(fs); 71 | partNum++; 72 | } 73 | } 74 | filestream = new MultiStream(parts); 75 | 76 | if (filestream.ReadASCIINullTerminated(16) != "FSG-FILE-SYSTEM") 77 | { 78 | throw new InvalidDataException("FSG-FILE-SYSTEM header not found."); 79 | } 80 | 81 | filestream.ReadUInt32BE(); // unknown, == 2 82 | uint header_length = filestream.ReadUInt32BE(); 83 | uint num_sectors = filestream.ReadUInt32BE(); 84 | 85 | //points to a list of all (used) sectors? 86 | //starting at 0x180 and increasing by 0x80 up to (num_sectors + 3) << 17 87 | uint sectormap_offset = filestream.ReadUInt32BE(); 88 | uint base_offset = filestream.ReadUInt32BE(); 89 | filestream.ReadUInt32BE(); // unknown, read buffer size? 90 | filestream.ReadUInt32BE(); // unknown, == 8 91 | uint num_files = filestream.ReadUInt32BE(); 92 | uint zero = filestream.ReadUInt32BE(); 93 | uint checksum = filestream.ReadUInt32BE(); 94 | var nodes = new Dictionary((int)num_files); 95 | byte[] sector_types = new byte[num_sectors + (base_offset >> 17)]; 96 | 97 | for (var i = 0; i < num_files; i++) 98 | { 99 | var node = new file_descriptor(); 100 | node.filename_hash = filestream.ReadUInt32BE(); 101 | node.type = (byte)filestream.ReadByte(); 102 | node.offset = filestream.ReadUInt24BE(); 103 | nodes.Add(node.filename_hash, node); 104 | } 105 | foreach(file_descriptor node in nodes.Values) 106 | { 107 | filestream.Position = node.offset; 108 | long offset = filestream.ReadUInt32BE(); 109 | node.data_offset = (offset << 10) + base_offset; 110 | node.size = filestream.ReadUInt32BE(); 111 | } 112 | root = RecursivelyGetFiles(null, ROOT_DIR, base_offset, "", nodes); 113 | } 114 | 115 | /// 116 | /// Parse a directory for its contents. 117 | /// 118 | /// The name of this directory. 119 | /// Location of its filename infos. 120 | /// File descriptor dictionary 121 | /// 122 | private FSGIMGDirectory RecursivelyGetFiles(FSGIMGDirectory parent, string name, long base_offset, string path_acc, Dictionary nodes) 123 | { 124 | filestream.Position = base_offset; 125 | string filename; 126 | FSGIMGDirectory ret = new FSGIMGDirectory(parent, name, base_offset); 127 | while ((filename = filestream.ReadASCIINullTerminated()) != "") 128 | { 129 | long pos = filestream.Position; 130 | string real_name = filename.Substring(1); 131 | file_descriptor desc; 132 | string nextPath = path_acc == "" ? real_name : $"{path_acc}/{real_name}"; 133 | nodes.TryGetValue(Hash(nextPath), out desc); 134 | if (filename[0] == 'D') 135 | { 136 | ret.AddDir(RecursivelyGetFiles(ret, real_name, desc.data_offset, nextPath, nodes)); 137 | filestream.Position = pos; 138 | } 139 | else if (filename[0] == 'F') 140 | { 141 | ret.AddFile(new OffsetFile(real_name, ret, filestream, desc.data_offset, desc.size)); 142 | } 143 | else 144 | { 145 | throw new InvalidDataException($"Got invalid filename prefix: {filename[0]}."); 146 | } 147 | } 148 | return ret; 149 | } 150 | 151 | /// 152 | /// Hashes a path with a broken fnv132 hashing algorithm 153 | /// 154 | /// 155 | /// 156 | private uint Hash(string str) 157 | { 158 | if (str[0] == '/') 159 | str = str.Substring(1); 160 | str = str.ToUpper(); 161 | uint hash = 2166136261U; 162 | for (var i = 0; i < str.Length; i++) 163 | { 164 | hash = (1677619U * hash) ^ (byte)str[i]; 165 | } 166 | return hash; 167 | } 168 | 169 | public override void Dispose() 170 | { 171 | filestream.Close(); 172 | filestream.Dispose(); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /Library/GameArchives.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {906748F0-3A55-4B20-BCCB-9DC7187F1D5E} 8 | Library 9 | Properties 10 | GameArchives 11 | GameArchives 12 | v3.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | bin\Release\GameArchives.XML 35 | 36 | 37 | bin\Release-minimal\ 38 | TRACE;MINIMAL 39 | true 40 | pdbonly 41 | AnyCPU 42 | prompt 43 | MinimumRecommendedRules.ruleset 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 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 | Always 103 | 104 | 105 | Always 106 | 107 | 108 | 109 | 110 | 117 | -------------------------------------------------------------------------------- /Library/GameArchives.licenseheader: -------------------------------------------------------------------------------- 1 | extensions: designer.cs generated.cs 2 | extensions: .cs .cpp .h 3 | /* 4 | * %FileName% 5 | * 6 | * Copyright (c) 2015-%CurrentYear%, maxton. All rights reserved. 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 3.0 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; If not, see 20 | * . 21 | */ -------------------------------------------------------------------------------- /Library/Local/LocalDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * LocalDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | using System; 22 | using System.Collections.Generic; 23 | using System.Linq; 24 | using System.Text; 25 | using System.IO; 26 | 27 | namespace GameArchives.Local 28 | { 29 | /// 30 | /// Represents a directory in the local file system. 31 | /// All files in the directory are loaded by default, while subdirectories 32 | /// are loaded on-demand (although this may change in the future). 33 | /// 34 | public class LocalDirectory : DefaultDirectory 35 | { 36 | private readonly string path; 37 | private bool dirsFilled = false; 38 | private bool filesFilled = false; 39 | 40 | /// 41 | /// Make a shallow instance of the given local directory. 42 | /// 43 | /// Location of the directory. 44 | internal LocalDirectory(string path) : base(null, new DirectoryInfo(path).Name) 45 | { 46 | this.path = path; 47 | if (File.Exists(path)) 48 | { 49 | throw new ArgumentException("Given path must point to directory, not file."); 50 | } 51 | } 52 | 53 | public override ICollection Dirs { 54 | get 55 | { 56 | if (dirsFilled) { return dirs.Values; } 57 | foreach(string d in Directory.GetDirectories(path)) 58 | { 59 | IDirectory tmp = new LocalDirectory(d); 60 | AddDir(tmp); 61 | } 62 | dirsFilled = true; 63 | return dirs.Values; 64 | } 65 | } 66 | 67 | public override ICollection Files 68 | { 69 | get 70 | { 71 | if (filesFilled) 72 | { 73 | return files.Values; 74 | } 75 | foreach (string f in Directory.GetFiles(path)) 76 | { 77 | IFile tmp = new LocalFile(this, f); 78 | AddFile(tmp); 79 | } 80 | filesFilled = true; 81 | return files.Values; 82 | } 83 | } 84 | 85 | public override bool TryGetDirectory(string name, out IDirectory dir) 86 | { 87 | if(dirs.TryGetValue(name, out dir)) 88 | { 89 | return true; 90 | } 91 | else if(Directory.Exists(Path.Combine(path, name))) 92 | { 93 | dir = new LocalDirectory(Path.Combine(path, name)); 94 | AddDir(dir); 95 | return true; 96 | } 97 | return false; 98 | } 99 | 100 | public override bool TryGetFile(string name, out IFile file) 101 | { 102 | if (files.TryGetValue(name, out file)) 103 | { 104 | return true; 105 | } 106 | else if (File.Exists(Path.Combine(path, name))) 107 | { 108 | file = new LocalFile(this, Path.Combine(path, name)); 109 | AddFile(file); 110 | return true; 111 | } 112 | return false; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Library/Local/LocalFile.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * LocalFile.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.Linq; 24 | 25 | namespace GameArchives.Local 26 | { 27 | /// 28 | /// Represents a file on the local filesystem. 29 | /// 30 | class LocalFile : IFile 31 | { 32 | public bool Compressed => false; 33 | public long CompressedSize => Size; 34 | public long Size { get; } 35 | public string Name { get; } 36 | public IDirectory Parent { get; } 37 | public Stream Stream => GetStream(); 38 | public IDictionary ExtendedInfo { get; } 39 | 40 | private string path; 41 | 42 | internal LocalFile(IDirectory parent, string path) 43 | { 44 | Parent = parent; 45 | this.path = path; 46 | Size = new FileInfo(path).Length; 47 | this.Name = Path.GetFileName(path); 48 | ExtendedInfo = new Dictionary(); 49 | } 50 | 51 | public byte[] GetBytes() 52 | { 53 | return File.ReadAllBytes(path); 54 | } 55 | public Stream GetStream() 56 | { 57 | try 58 | { 59 | return File.Open(path, FileMode.Open, FileAccess.ReadWrite); 60 | } 61 | catch(Exception ex) 62 | { 63 | return File.OpenRead(path); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Library/PFS/PFSCDecompressStream.cs: -------------------------------------------------------------------------------- 1 | using GameArchives.Common; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.IO.Compression; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace GameArchives.PFS 10 | { 11 | public class PFSCDecompressStream : Stream 12 | { 13 | /// 14 | /// Wraps a PFSC file to create a decompressed stream. 15 | /// 16 | /// 17 | /// 18 | /// 19 | public PFSCDecompressStream(Stream pfsc) 20 | { 21 | s = pfsc; 22 | s.Position = 0x10; 23 | sectorSize = s.ReadInt64LE(); 24 | var offsetsStart = s.ReadInt64LE(); 25 | dataStart = s.ReadInt64LE(); 26 | length = s.ReadInt64LE(); 27 | sectorBuffer = new byte[sectorSize]; 28 | var num_blocks = length / sectorSize; 29 | sectorMap = new long[num_blocks + 1]; 30 | s.Position = offsetsStart; 31 | for(var i = 0; i < sectorMap.Length; i++) 32 | { 33 | sectorMap[i] = s.ReadInt64LE(); 34 | } 35 | currentSector = 0; 36 | ReadSectorBuffer(); 37 | } 38 | 39 | private Stream s; 40 | private long length; 41 | private long sectorSize; 42 | private long dataStart; 43 | private byte[] sectorBuffer; 44 | private long currentSector; 45 | private long[] sectorMap; 46 | /// 47 | /// Offset within the sector for Read()s 48 | /// Should always be == position % sectorSize 49 | /// 50 | private int offsetIntoSector; 51 | /// 52 | /// Position within logical stream. 53 | /// 54 | private long position; 55 | 56 | public override bool CanRead => true; 57 | 58 | public override bool CanSeek => true; 59 | 60 | public override bool CanWrite => false; 61 | 62 | public override long Length => length; 63 | 64 | public override long Position 65 | { 66 | get => position; 67 | set 68 | { 69 | var newSector = (value / sectorSize); 70 | if (newSector != currentSector) 71 | { 72 | currentSector = newSector; 73 | ReadSectorBuffer(); 74 | offsetIntoSector = (int)(value - position); 75 | position = value; 76 | } 77 | else 78 | { 79 | offsetIntoSector = (int)(value - (sectorSize * currentSector)); 80 | position = value; 81 | } 82 | } 83 | } 84 | 85 | private void ReadSectorBuffer() 86 | { 87 | var start = sectorMap[currentSector]; 88 | var compressedLength = (int)(sectorMap[currentSector + 1] - start); 89 | s.Position = start; 90 | if (compressedLength == sectorSize) 91 | { 92 | s.Read(sectorBuffer, 0, (int)sectorSize); 93 | } 94 | else 95 | { 96 | using (var os = new OffsetStream(s, start + 2, compressedLength - 2)) 97 | using (var ds = new DeflateStream(os, CompressionMode.Decompress, true)) 98 | { 99 | ds.Read(sectorBuffer, 0, (int)sectorSize); 100 | } 101 | } 102 | position = sectorSize * currentSector; 103 | offsetIntoSector = 0; 104 | } 105 | 106 | public override int Read(byte[] buffer, int offset, int count) 107 | { 108 | int totalRead = 0; 109 | while (count > 0 && position < length) 110 | { 111 | if (offsetIntoSector >= sectorSize) 112 | { 113 | currentSector++; 114 | ReadSectorBuffer(); 115 | } 116 | int bufferedRead = Math.Min((int)sectorSize - offsetIntoSector, count); 117 | Buffer.BlockCopy(sectorBuffer, offsetIntoSector, buffer, offset, bufferedRead); 118 | count -= bufferedRead; 119 | offset += bufferedRead; 120 | totalRead += bufferedRead; 121 | offsetIntoSector += bufferedRead; 122 | position += bufferedRead; 123 | } 124 | return totalRead; 125 | } 126 | 127 | public override long Seek(long offset, SeekOrigin origin) 128 | { 129 | switch (origin) 130 | { 131 | case SeekOrigin.Begin: 132 | Position = offset; 133 | break; 134 | case SeekOrigin.Current: 135 | Position += offset; 136 | break; 137 | case SeekOrigin.End: 138 | Position = Length + offset; 139 | break; 140 | } 141 | return position; 142 | } 143 | 144 | public override void Flush() 145 | { 146 | throw new NotImplementedException(); 147 | } 148 | 149 | public override void SetLength(long value) 150 | { 151 | throw new NotImplementedException(); 152 | } 153 | 154 | public override void Write(byte[] buffer, int offset, int count) 155 | { 156 | throw new NotImplementedException(); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /Library/PFS/PFSDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PFSDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | 22 | namespace GameArchives.PFS 23 | { 24 | /// 25 | /// Playstation File System Directory 26 | /// 27 | class PFSDirectory : DefaultDirectory 28 | { 29 | public PFSDirectory(IDirectory parent, string name) : base(parent, name) 30 | { } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Library/PFS/PFSFile.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PFSFile.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System.IO; 21 | using GameArchives.Common; 22 | 23 | namespace GameArchives.PFS 24 | { 25 | public class PFSFile : OffsetFile 26 | { 27 | public long InodeIdx => (long) ExtendedInfo["InodeIdx"]; 28 | public PFSFile(string name, IDirectory parent, Stream img, long offset, long size, long inodeIdx) 29 | : base(name, parent, img, offset, size) 30 | { 31 | ExtendedInfo.Add("InodeIdx", inodeIdx); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Library/PFS/XtsCryptStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | namespace GameArchives.PFS 9 | { 10 | public class XtsCryptStream : Stream 11 | { 12 | // Used on the plaintext XORed with the encrypted sector number 13 | private SymmetricAlgorithm cipher; 14 | // Used to encrypt the tweak 15 | private SymmetricAlgorithm tweakCipher; 16 | 17 | private byte[] tweak = new byte[16]; 18 | private byte[] xor = new byte[16]; 19 | private byte[] xor2 = new byte[16]; 20 | private byte[] encryptedTweak = new byte[16]; 21 | /// 22 | /// Size of each XEX sector 23 | /// 24 | private uint sectorSize; 25 | /// 26 | /// Offset within the sector for Read()s 27 | /// Should always be == position % sectorSize 28 | /// 29 | private int offsetIntoSector; 30 | /// 31 | /// Active sector number 32 | /// 33 | private ulong activeSector; 34 | /// 35 | /// Sector at and after which the encryption is active 36 | /// 37 | private uint cryptStartSector; 38 | /// 39 | /// Position within logical stream. 40 | /// 41 | private long position; 42 | /// 43 | /// Temporary location for the decrypted sector 44 | /// 45 | private byte[] sectorBuf; 46 | private Stream stream; 47 | 48 | /// 49 | /// Creates an AES-XTS-128 stream. 50 | /// Reads from the stream will decrypt data. Writes to the stream will encrypt data. 51 | /// 52 | public XtsCryptStream(Stream s, byte[] dataKey, byte[] tweakKey, uint startSector = 16, uint sectorSize = 0x1000) 53 | { 54 | cipher = new AesManaged 55 | { 56 | Mode = CipherMode.ECB, 57 | KeySize = 128, 58 | Key = dataKey, 59 | Padding = PaddingMode.None, 60 | BlockSize = 128, 61 | }; 62 | tweakCipher = new AesManaged 63 | { 64 | Mode = CipherMode.ECB, 65 | KeySize = 128, 66 | Key = tweakKey, 67 | Padding = PaddingMode.None, 68 | BlockSize = 128, 69 | }; 70 | cryptStartSector = startSector; 71 | this.sectorSize = sectorSize; 72 | sectorBuf = new byte[sectorSize]; 73 | stream = s; 74 | stream.Position = 0; 75 | position = 0; 76 | offsetIntoSector = 0; 77 | activeSector = 0; 78 | ReadSectorBuffer(); 79 | } 80 | 81 | public override bool CanRead => stream.CanRead; 82 | 83 | public override bool CanSeek => stream.CanSeek; 84 | 85 | public override bool CanWrite => stream.CanWrite; 86 | 87 | public override long Length => stream.Length; 88 | 89 | public override long Position 90 | { 91 | get => position; 92 | set 93 | { 94 | activeSector = (ulong)(value / sectorSize); 95 | ReadSectorBuffer(); 96 | offsetIntoSector = (int)(value - position); 97 | position = value; 98 | } 99 | } 100 | 101 | public void DecryptSector(byte[] sector, ulong sectorNum) 102 | { 103 | // Reset tweak to sector number 104 | Buffer.BlockCopy(BitConverter.GetBytes(sectorNum), 0, tweak, 0, 8); 105 | for (int x = 8; x < 16; x++) 106 | tweak[x] = 0; 107 | using (var tweakEncryptor = tweakCipher.CreateEncryptor()) 108 | using (var decryptor = cipher.CreateDecryptor()) 109 | { 110 | tweakEncryptor.TransformBlock(tweak, 0, 16, encryptedTweak, 0); 111 | for (int plaintextOffset = 0; plaintextOffset < sector.Length; plaintextOffset += 16) 112 | { 113 | for (var x = 0; x < 16; x++) 114 | { 115 | xor[x] = (byte)(sector[x + plaintextOffset] ^ encryptedTweak[x]); 116 | } 117 | decryptor.TransformBlock(xor, 0, 16, xor, 0); 118 | for (var x = 0; x < 16; x++) 119 | { 120 | sector[x + plaintextOffset] = (byte)(xor[x] ^ encryptedTweak[x]); 121 | } 122 | // GF-Multiply Tweak 123 | int feedback = 0; 124 | for (int k = 0; k < 16; k ++) 125 | { 126 | byte tmp = encryptedTweak[k]; 127 | encryptedTweak[k] = (byte)(2 * encryptedTweak[k] | feedback); 128 | feedback = (tmp & 0x80) >> 7; 129 | } 130 | if (feedback != 0) 131 | encryptedTweak[0] ^= 0x87; 132 | } 133 | } 134 | } 135 | 136 | public override void Flush() 137 | { 138 | stream.Flush(); 139 | } 140 | 141 | /// 142 | /// Precondition: activeSector is set 143 | /// Postconditions: 144 | /// - sectorOffset is reset to 0 145 | /// - sectorBuf[] is filled with decrypted sector 146 | /// - position is updated 147 | /// 148 | private void ReadSectorBuffer() 149 | { 150 | position = sectorSize * (long)activeSector; 151 | stream.Position = position; 152 | stream.Read(sectorBuf, 0, (int)sectorSize); 153 | if (activeSector >= cryptStartSector) 154 | DecryptSector(sectorBuf, activeSector); 155 | offsetIntoSector = 0; 156 | } 157 | 158 | public override int Read(byte[] buffer, int offset, int count) 159 | { 160 | int totalRead = 0; 161 | while (count > 0 && position < stream.Length) 162 | { 163 | if (offsetIntoSector >= sectorSize) 164 | { 165 | activeSector++; 166 | ReadSectorBuffer(); 167 | } 168 | int bufferedRead = Math.Min((int)sectorSize - offsetIntoSector, count); 169 | Buffer.BlockCopy(sectorBuf, offsetIntoSector, buffer, offset, bufferedRead); 170 | count -= bufferedRead; 171 | offset += bufferedRead; 172 | totalRead += bufferedRead; 173 | offsetIntoSector += bufferedRead; 174 | position += bufferedRead; 175 | } 176 | return totalRead; 177 | } 178 | 179 | public override long Seek(long offset, SeekOrigin origin) 180 | { 181 | switch (origin) 182 | { 183 | case SeekOrigin.Begin: 184 | Position = offset; 185 | break; 186 | case SeekOrigin.Current: 187 | Position += offset; 188 | break; 189 | case SeekOrigin.End: 190 | Position = Length + offset; 191 | break; 192 | } 193 | return position; 194 | } 195 | 196 | public override void SetLength(long value) 197 | { 198 | throw new NotImplementedException(); 199 | } 200 | 201 | public override void Write(byte[] buffer, int offset, int count) 202 | { 203 | throw new NotImplementedException(); 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /Library/PKF/PKFDirectory.cs: -------------------------------------------------------------------------------- 1 | using GameArchives.Common; 2 | 3 | namespace GameArchives.PKF 4 | { 5 | public class PKFDirectory : DefaultDirectory 6 | { 7 | public PKFDirectory(IDirectory parent, string name) : base (parent, name) 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Library/PKF/PKFFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.IO.Compression; 7 | using GameArchives.Common; 8 | 9 | namespace GameArchives.PKF 10 | { 11 | public class PKFFile : IFile 12 | { 13 | public long Size { get; } 14 | public long CompressedSize { get; } 15 | public bool Compressed { get; } 16 | 17 | public IDictionary ExtendedInfo { get; } 18 | public Stream Stream => GetStream(); 19 | public string Name { get; } 20 | public IDirectory Parent { get; } 21 | 22 | private long offset; 23 | private Stream archive; 24 | 25 | public PKFFile(string name, IDirectory parent, long size, bool compressed, long compressedSize, long offset, Stream archive) 26 | { 27 | Name = name; 28 | Parent = parent; 29 | Size = size; 30 | Compressed = compressed; 31 | CompressedSize = compressedSize; 32 | 33 | this.offset = offset; 34 | this.archive = archive; 35 | 36 | ExtendedInfo = new Dictionary(); 37 | } 38 | 39 | public byte[] GetBytes() 40 | { 41 | byte[] bytes = new byte[CompressedSize]; 42 | if (CompressedSize > Int32.MaxValue) 43 | throw new NotSupportedException("Can't read bytes for file larger than int32 max, yet."); 44 | 45 | using (Stream stream = this.GetStream()) 46 | { 47 | stream.Read(bytes, 0, (int)CompressedSize); 48 | } 49 | 50 | return bytes; 51 | } 52 | 53 | public Stream GetStream() 54 | { 55 | if (Compressed) 56 | return new DeflateStream(new OffsetStream(archive, offset + 2, CompressedSize - 2), CompressionMode.Decompress); 57 | else 58 | return new OffsetStream(archive, offset, CompressedSize); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Library/PKF/PKFPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace GameArchives.PKF 8 | { 9 | public class PKFPackage : AbstractPackage 10 | { 11 | public static PackageTestResult IsPKF(IFile file) 12 | { 13 | switch(Path.GetExtension(file.Name).ToLower()) 14 | { 15 | case ".pkf": 16 | case ".themes": 17 | break; 18 | default: 19 | return PackageTestResult.NO; 20 | } 21 | 22 | using (Stream stream = file.GetStream()) 23 | { 24 | stream.Position = 0; 25 | string magic = stream.ReadASCIINullTerminated(8); 26 | return magic == "PACKAGE " ? PackageTestResult.YES : PackageTestResult.NO; 27 | } 28 | } 29 | 30 | public static PKFPackage OpenFile(IFile file) 31 | { 32 | return new PKFPackage(file); 33 | } 34 | 35 | public override string FileName { get; } 36 | public override IDirectory RootDirectory => root; 37 | public override long Size => stream.Length; 38 | public override bool Writeable => false; 39 | public override Type FileType => typeof(PKFFile); 40 | 41 | private Stream stream; 42 | private PKFDirectory root; 43 | 44 | public PKFPackage(IFile file) 45 | { 46 | FileName = file.Name; 47 | root = new PKFDirectory(null, ROOT_DIR); 48 | stream = file.GetStream(); 49 | 50 | if (stream.ReadASCIINullTerminated(8) != "PACKAGE ") 51 | throw new InvalidDataException("File does not have a valid PACKAGE header."); 52 | 53 | stream.Position += 6; // Unknown int32 + int16 54 | long fileStart = stream.ReadUInt32BE() + stream.Position; 55 | 56 | while (stream.Position < fileStart) 57 | { 58 | string pathName, fileName; 59 | uint offset, size, compressedSize; 60 | bool compressed = false; 61 | 62 | // Reads file entry 63 | stream.Position += 4; // Hash? 64 | pathName = stream.ReadASCIINullTerminated(); 65 | offset = stream.ReadUInt32BE(); 66 | size = compressedSize = stream.ReadUInt32BE(); 67 | 68 | // ZLIB (12 bytes) 69 | // "ZLIB" 70 | // INT32 - Always 1 71 | // INT32 - Max block size? (0x8080) 72 | 73 | // Checks if file is compressed 74 | if (size > 12) 75 | { 76 | long nextEntry = stream.Position; 77 | stream.Position = offset; 78 | bool zlib = stream.ReadASCIINullTerminated(4) == "ZLIB"; 79 | 80 | if (zlib) 81 | { 82 | compressed = Convert.ToBoolean(stream.ReadUInt32BE()); 83 | size = stream.ReadUInt32BE(); // Uncompressed size 84 | 85 | offset += 12; 86 | compressedSize -= 12; 87 | } 88 | 89 | stream.Position = nextEntry; 90 | } 91 | 92 | // Adds file 93 | PKFDirectory dir = MakeOrGetDirectory(pathName); 94 | fileName = pathName.Split('\\').Last(); 95 | dir.AddFile(new PKFFile(fileName, dir, size, compressed, compressedSize, offset, stream)); 96 | } 97 | } 98 | 99 | private PKFDirectory MakeOrGetDirectory(string path) 100 | { 101 | string[] breadcrumbs = path.Split('\\'); 102 | IDirectory last = root; 103 | IDirectory current; 104 | 105 | if (breadcrumbs.Length == 1) 106 | return root; 107 | 108 | for (var idx = 0; idx < breadcrumbs.Length - 1; idx++) 109 | { 110 | if (!last.TryGetDirectory(breadcrumbs[idx], out current)) 111 | { 112 | current = new PKFDirectory(last, breadcrumbs[idx]); 113 | (last as PKFDirectory).AddDir(current as PKFDirectory); 114 | } 115 | last = current; 116 | } 117 | 118 | return last as PKFDirectory; 119 | } 120 | 121 | public override void Dispose() 122 | { 123 | stream.Close(); 124 | stream.Dispose(); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Library/PSARC/PSARCDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PSARCDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | 22 | namespace GameArchives.PSARC 23 | { 24 | class PSARCDirectory : DefaultDirectory 25 | { 26 | public PSARCDirectory(IDirectory parent, string name) : base(parent, name) 27 | { 28 | 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Library/PSARC/PSARCFile.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PSARCFile.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.IO.Compression; 24 | using GameArchives.Common; 25 | 26 | namespace GameArchives.PSARC 27 | { 28 | class PSARCFile : IFile 29 | { 30 | public string Name { get; } 31 | public IDirectory Parent { get; } 32 | public long Size { get; } 33 | public bool Compressed { get; } 34 | public long CompressedSize { get; } 35 | public IDictionary ExtendedInfo { get; } 36 | public Stream Stream => GetStream(); 37 | 38 | private long offset; 39 | private Stream archive; 40 | 41 | public PSARCFile(string n, IDirectory p, PSARCPackage.TocEntry entry, Stream archive) 42 | { 43 | Name = n; 44 | Parent = p; 45 | Size = entry.uncompressedSize; 46 | Compressed = true; 47 | CompressedSize = entry.uncompressedSize; 48 | offset = entry.offset; 49 | this.archive = archive; 50 | ExtendedInfo = new Dictionary(); 51 | } 52 | 53 | public byte[] GetBytes() 54 | { 55 | byte[] bytes = new byte[Size]; 56 | if(Size > Int32.MaxValue) 57 | { 58 | throw new NotSupportedException("Can't read bytes for file larger than int32 max, yet."); 59 | } 60 | using (var stream = this.GetStream()) 61 | { 62 | stream.Read(bytes, 0, (int)Size); 63 | } 64 | return bytes; 65 | } 66 | 67 | public Stream GetStream() 68 | { 69 | if (!Compressed) 70 | return new OffsetStream(archive, offset, Size); 71 | else 72 | return new DeflateStream(new OffsetStream(archive, offset + 2, CompressedSize - 2), 73 | CompressionMode.Decompress); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Library/PackageReader.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PackageReader.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | using System.IO; 23 | using System.Text; 24 | 25 | namespace GameArchives 26 | { 27 | /// 28 | /// The result of a package test. 29 | /// 30 | public enum PackageTestResult 31 | { 32 | /// 33 | /// Definitely not an instance of the package type. 34 | /// 35 | NO, 36 | /// 37 | /// Possibly an instance of the package type, but a more in-depth analysis would be needed. 38 | /// 39 | MAYBE, 40 | /// 41 | /// Definitely an instance of the package type. 42 | /// 43 | YES 44 | }; 45 | 46 | /// 47 | /// Collection of methods for reading packages. 48 | /// 49 | public static class PackageReader 50 | { 51 | /// 52 | /// Attempts to read the file as a supported archive package. 53 | /// If the file is not of a supported format, throws an exception. 54 | /// 55 | /// 56 | /// 57 | /// Thrown when an unsupported file type is given. 58 | public static AbstractPackage ReadPackageFromFile(string file) 59 | => ReadPackageFromFile(Util.LocalFile(file)); 60 | 61 | /// 62 | /// Attempts to read the file as a supported archive package. 63 | /// If the file is not of a supported format, throws an exception. 64 | /// 65 | /// An IFile referring to the archive package. 66 | /// 67 | /// This callback will be called when a package is a likely match but needs a password/decryption 68 | /// key. It will be called with a request string, and should return the key. 69 | /// 70 | /// The package, if it could be opened. 71 | /// Thrown when an unsupported file type is given. 72 | public static AbstractPackage ReadPackageFromFile(IFile file, Func passcode_cb) 73 | { 74 | var possible = new List(); 75 | foreach (PackageType t in PackageType.Types) 76 | { 77 | var result = t.CheckFile(file); 78 | if (result == PackageTestResult.YES) 79 | { 80 | return t.Load(file, passcode_cb); 81 | } 82 | else if(result == PackageTestResult.MAYBE) 83 | { 84 | possible.Add(t); 85 | } 86 | } 87 | foreach(PackageType t in possible) 88 | { 89 | try 90 | { 91 | return t.Load(file, passcode_cb); 92 | } 93 | catch(InvalidDataException) 94 | { 95 | continue; 96 | } 97 | } 98 | throw new NotSupportedException("Given file was not a supported archive format."); 99 | } 100 | /// 101 | /// Reads a package using the given decryption key if necessary. 102 | /// 103 | public static AbstractPackage ReadPackageFromFile(IFile file, string key) 104 | { 105 | return ReadPackageFromFile(file, s => key); 106 | } 107 | public static AbstractPackage ReadPackageFromFile(IFile file) 108 | { 109 | return ReadPackageFromFile(file, s => throw new Exception("A passcode is needed to open this file")); 110 | } 111 | 112 | /// 113 | /// Tries to read a package given only a stream. This makes a dummy file which works with the package reader. 114 | /// 115 | /// Stream to read from. This must support the Length property. 116 | /// (Optional) the filename to give the dummy file. 117 | /// 118 | public static AbstractPackage ReadPackageFromStream(Stream stream, string filename = "unknown") 119 | => ReadPackageFromFile(new Common.OffsetFile(filename, new Common.DefaultDirectory(null, ""), stream, 0, stream.Length)); 120 | 121 | /// 122 | /// A list of supported file formats and their extensions, presented 123 | /// in a format that an OpenFileDialog supports. 124 | /// 125 | public static string SupportedFormats 126 | { 127 | get 128 | { 129 | StringBuilder sb = new StringBuilder(); 130 | bool first = true; 131 | foreach (PackageType t in PackageType.Types) 132 | { 133 | if (!first) sb.Append("|"); 134 | sb.AppendFormat("{0} ({1})|{1}", t.Name, string.Join(";",t.Extensions)); 135 | if (first) first = false; 136 | } 137 | return sb.ToString(); 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Library/PackageType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * PackageTypes.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.Collections.Generic; 22 | 23 | namespace GameArchives 24 | { 25 | class PackageType 26 | { 27 | /// 28 | /// The common name of this package type. 29 | /// 30 | public string Name { get; } 31 | 32 | /// 33 | /// The common file extensions of this package type. 34 | /// 35 | /// 36 | /// These should be of the format: 37 | /// *.ext 38 | /// As expected by an OpenFileDialog filter list. 39 | /// 40 | public string[] Extensions { get; } 41 | 42 | /// 43 | /// Given a file, determines whether the file is 44 | /// of this package type. 45 | /// 46 | public Func CheckFile { get; } 47 | 48 | /// 49 | /// Given a file which is a valid package, opens it as this 50 | /// package type, returning the package instance. 51 | /// 52 | public Func, AbstractPackage> Load { get; } 53 | 54 | PackageType(string name, string[] extensions, 55 | Func file, Func, AbstractPackage> load) 56 | { 57 | Name = name; 58 | Extensions = extensions; 59 | CheckFile = file; 60 | Load = load; 61 | } 62 | 63 | PackageType(string name, string[] extensions, 64 | Func file, Func load) 65 | { 66 | Name = name; 67 | Extensions = extensions; 68 | CheckFile = file; 69 | Load = (f, req) => load(f); 70 | } 71 | 72 | public static readonly ICollection Types; 73 | 74 | /// 75 | /// Add an archive package type to the supported types. 76 | /// 77 | /// Friendly name for the package type 78 | /// String-array of typical file extensions, formatted 79 | /// as *.ext 80 | /// Function which, given a file, returns a PackageTestResult 81 | /// which tells if the file is of that package type. 82 | /// Function which loads the package. 83 | public static void AddType(string name, string[] extensions, 84 | Func file, Func load) 85 | { 86 | Types.Add(new PackageType(name, extensions, file, load)); 87 | } 88 | 89 | static PackageType() 90 | { 91 | Types = new List { 92 | new PackageType("Ark/Hdr Package", 93 | new string[] { "*.ark","*.hdr" }, 94 | Ark.ArkPackage.IsArk, 95 | Ark.ArkPackage.OpenFile), 96 | new PackageType("STFS Package", 97 | new string[] { "*.*" }, 98 | STFS.STFSPackage.IsSTFS, 99 | STFS.STFSPackage.OpenFile), 100 | new PackageType("FSAR Archive", 101 | new string[] { "*.far" }, 102 | FSAR.FSARPackage.IsFSAR, 103 | FSAR.FSARPackage.FromFile), 104 | new PackageType("FSG-FILE-SYSTEM", 105 | new string[] { "*.img", "*.img.part000", "*.img.part0" }, 106 | FSGIMG.FSGIMGPackage.IsFSGIMG, 107 | FSGIMG.FSGIMGPackage.OpenFile), 108 | new PackageType("XBOX/Xbox360 ISO", 109 | new string[] { "*.iso" }, 110 | XISO.XISOPackage.IsXISO, 111 | XISO.XISOPackage.OpenFile), 112 | new PackageType("Playstation 4 PFS", 113 | new string[] { "*.dat", "*.*" }, 114 | PFS.PFSPackage.IsPFS, 115 | PFS.PFSPackage.OpenFile), 116 | new PackageType("Playstation PSARC", 117 | new string[] {"*.psarc","*.pak" }, 118 | PSARC.PSARCPackage.IsPSARC, 119 | PSARC.PSARCPackage.FromFile), 120 | new PackageType("Nintendo U8", 121 | new string[] { "*.app", "*.arc" }, 122 | U8.U8Package.IsU8, 123 | U8.U8Package.FromFile), 124 | new PackageType("PS3 SingStar Package", 125 | new string[] { "*.pkf", "*.themes" }, 126 | PKF.PKFPackage.IsPKF, 127 | PKF.PKFPackage.OpenFile 128 | ), 129 | new PackageType("Seven45 hdr/pk", 130 | new string[] { "*.hdr.e.2" }, 131 | Seven45.Seven45Package.IsSeven45, 132 | Seven45.Seven45Package.OpenFile 133 | ), 134 | //new PackageType("", new string[] { }, null, null, null) 135 | }; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Library/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * AssemblyInfo.cs 3 | * 4 | * Copyright (c) 2015-2017, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System.Reflection; 21 | using System.Runtime.CompilerServices; 22 | using System.Runtime.InteropServices; 23 | 24 | // General Information about an assembly is controlled through the following 25 | // set of attributes. Change these attribute values to modify the information 26 | // associated with an assembly. 27 | [assembly: AssemblyTitle("GameArchives")] 28 | [assembly: AssemblyDescription("A library for reading several video game archive formats.")] 29 | [assembly: AssemblyConfiguration("")] 30 | [assembly: AssemblyCompany("")] 31 | [assembly: AssemblyProduct("GameArchives")] 32 | [assembly: AssemblyCopyright("Copyright © 2017")] 33 | [assembly: AssemblyTrademark("")] 34 | [assembly: AssemblyCulture("")] 35 | 36 | // Setting ComVisible to false makes the types in this assembly not visible 37 | // to COM components. If you need to access a type in this assembly from 38 | // COM, set the ComVisible attribute to true on that type. 39 | [assembly: ComVisible(false)] 40 | 41 | // The following GUID is for the ID of the typelib if this project is exposed to COM 42 | [assembly: Guid("906748f0-3a55-4b20-bccb-9dc7187f1d5e")] 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("0.12.0.*")] 55 | [assembly: AssemblyFileVersion("0.12.0.0")] 56 | -------------------------------------------------------------------------------- /Library/STFS/STFSDirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * STFSDirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | using System; 22 | using System.Collections.Generic; 23 | 24 | namespace GameArchives.STFS 25 | { 26 | /// 27 | /// Represents a Directory within an STFS package. 28 | /// 29 | class STFSDirectory : DefaultDirectory 30 | { 31 | public STFSDirectory(IDirectory parent, string name) : base(parent, name) 32 | { } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Library/Seven45/PowerChordCryptStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Security.Cryptography; 7 | 8 | namespace GameArchives.Seven45 9 | { 10 | public class PowerChordCryptStream : Stream 11 | { 12 | const string header_password = "The corresponding .model file does not exist for '%s'"; 13 | 14 | const int BLOCK_SIZE = 512; 15 | const long BLOCK_MASK = ~511L; 16 | private Stream base_; 17 | private static readonly long data_offset = 512; 18 | private long position; 19 | private Aes aes; 20 | private byte[] headerBlock = new byte[BLOCK_SIZE]; 21 | private byte[] chunkBuffer = new byte[BLOCK_SIZE + 16]; 22 | private byte[] key; 23 | private byte[] iv; 24 | 25 | public override bool CanRead => true; 26 | 27 | public override bool CanSeek => true; 28 | 29 | public override bool CanWrite => false; 30 | 31 | public override long Length { get; } 32 | 33 | public override long Position { get => position; set => Seek(value, SeekOrigin.Begin); } 34 | 35 | public PowerChordCryptStream(Stream file) 36 | { 37 | if(file.Length < BLOCK_SIZE) 38 | { 39 | throw new Exception("File is not large enough to be .e.2 encrypted"); 40 | } 41 | 42 | this.base_ = file; 43 | position = 0; 44 | aes = Aes.Create(); 45 | aes.BlockSize = 128; 46 | aes.KeySize = 256; 47 | aes.Mode = CipherMode.CBC; 48 | using (SHA256 sha256 = SHA256.Create()) 49 | { 50 | aes.Key = sha256.ComputeHash(Encoding.ASCII.GetBytes(header_password)); 51 | } 52 | base_.Seek(0, SeekOrigin.Begin); 53 | base_.Read(chunkBuffer, 0, BLOCK_SIZE); 54 | var header_iv = new byte[16]; 55 | Buffer.BlockCopy(chunkBuffer, BLOCK_SIZE - 16, header_iv, 0, 16); 56 | aes.IV = header_iv; 57 | using (var d = aes.CreateDecryptor(aes.Key, aes.IV)) 58 | { 59 | d.TransformBlock(chunkBuffer, 0, BLOCK_SIZE + 16, chunkBuffer, 0); 60 | } 61 | Buffer.BlockCopy(chunkBuffer, 0, headerBlock, 0, BLOCK_SIZE); 62 | iv = new byte[16]; 63 | Buffer.BlockCopy(chunkBuffer, 0, iv, 0, 16); 64 | key = new byte[32]; 65 | Buffer.BlockCopy(chunkBuffer, 16, key, 0, 32); 66 | 67 | Length = BitConverter.ToUInt32(chunkBuffer, 48); 68 | if(Length > file.Length) 69 | { 70 | throw new InvalidDataException("Decryption failed: length was invalid"); 71 | } 72 | 73 | UpdateBuffer(); 74 | } 75 | 76 | public byte[] GetHeader() 77 | { 78 | return (byte[])headerBlock.Clone(); 79 | } 80 | 81 | private void UpdateBuffer() 82 | { 83 | base_.Seek(data_offset + (position & BLOCK_MASK), SeekOrigin.Begin); 84 | base_.Read(chunkBuffer, 0, BLOCK_SIZE); 85 | using (var decryptor = aes.CreateDecryptor(key, iv)) 86 | { 87 | // Apparently we have to overshoot by one block because the last block is never decrypted? 88 | decryptor.TransformBlock(chunkBuffer, 0, BLOCK_SIZE + 16, chunkBuffer, 0); 89 | } 90 | } 91 | 92 | public override int Read(byte[] buffer, int offset, int count) 93 | { 94 | if (count < 0 || offset < 0) return 0; 95 | if (count + position > Length) 96 | { 97 | count = (int)(Length - position); 98 | } 99 | var totalRead = 0; 100 | while (count > 0) 101 | { 102 | var slack = BLOCK_SIZE - (int)(position % BLOCK_SIZE); 103 | var read = slack > count ? count : slack; 104 | if (read > 0) 105 | { 106 | Buffer.BlockCopy(chunkBuffer, (int)(position % BLOCK_SIZE), buffer, offset, read); 107 | } 108 | count -= read; 109 | offset += read; 110 | position += read; 111 | totalRead += read; 112 | UpdateBuffer(); 113 | } 114 | 115 | return totalRead; 116 | } 117 | 118 | public override long Seek(long offset, SeekOrigin origin) 119 | { 120 | switch (origin) 121 | { 122 | case SeekOrigin.Begin: 123 | break; 124 | case SeekOrigin.Current: 125 | offset += position; 126 | break; 127 | case SeekOrigin.End: 128 | offset += Length; 129 | break; 130 | } 131 | position = offset > Length ? Length : offset < 0 ? 0 : offset; 132 | UpdateBuffer(); 133 | return position; 134 | } 135 | 136 | #region Not Supported 137 | public override void Flush() 138 | { 139 | throw new NotImplementedException(); 140 | } 141 | 142 | public override void SetLength(long value) 143 | { 144 | throw new NotImplementedException(); 145 | } 146 | 147 | public override void Write(byte[] buffer, int offset, int count) 148 | { 149 | throw new NotImplementedException(); 150 | } 151 | #endregion 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Library/Seven45/Seven45Package.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace GameArchives.Seven45 8 | { 9 | public class Seven45Package : AbstractPackage 10 | { 11 | private Stream headerStream; 12 | private Stream[] contentFiles; 13 | private Common.DefaultDirectory root; 14 | 15 | public static PackageTestResult IsSeven45(IFile fn) 16 | { 17 | if (fn.Name.ToLower().EndsWith(".hdr.e.2")) 18 | { 19 | using (var stream = fn.GetStream()) 20 | { 21 | using (var s = new PowerChordCryptStream(stream)) 22 | if (s.ReadUInt32LE() == 0x745) 23 | return PackageTestResult.YES; 24 | } 25 | } 26 | return PackageTestResult.NO; 27 | } 28 | 29 | public static Seven45Package OpenFile(IFile f) 30 | { 31 | return new Seven45Package(f); 32 | } 33 | 34 | private Seven45Package(IFile f) 35 | { 36 | FileName = f.Name; 37 | var stream = f.GetStream(); 38 | headerStream = new PowerChordCryptStream(stream); 39 | root = new Common.DefaultDirectory(null, "/"); 40 | ParseHeader(f); 41 | } 42 | 43 | private class Header 44 | { 45 | public uint magic_seven45; 46 | public uint version; 47 | public uint block_size; 48 | public uint num_files; 49 | public uint num_unk; 50 | public uint num_dirs; 51 | public byte[] unk; 52 | public uint string_table_offset; 53 | public uint string_table_size; 54 | public uint num_offsets; 55 | 56 | public static Header Read(Stream s) => new Header 57 | { 58 | magic_seven45 = s.ReadUInt32LE(), 59 | version = s.ReadUInt32LE(), 60 | block_size = s.ReadUInt32LE(), 61 | num_files = s.ReadUInt32LE(), 62 | num_unk = s.ReadUInt32LE(), 63 | num_dirs = s.ReadUInt32LE(), 64 | unk = s.ReadBytes(8), 65 | string_table_offset = s.ReadUInt32LE(), 66 | string_table_size = s.ReadUInt32LE(), 67 | num_offsets = s.ReadUInt32LE() 68 | }; 69 | } 70 | 71 | private struct FileEntry 72 | { 73 | public byte[] unk; 74 | public ushort dir_num; 75 | public uint string_num; 76 | public uint offset_num; 77 | public long size; 78 | public byte[] unk_2; 79 | public long time; 80 | 81 | public static FileEntry Read(Stream s) => new FileEntry 82 | { 83 | unk = s.ReadBytes(6), 84 | dir_num = s.ReadUInt16LE(), 85 | string_num = s.ReadUInt32LE(), 86 | offset_num = s.ReadUInt32LE(), 87 | size = s.ReadInt64LE(), 88 | unk_2 = s.ReadBytes(16), 89 | time = s.ReadInt64LE() 90 | }; 91 | } 92 | 93 | private class DirEntry 94 | { 95 | public uint path_hash; 96 | public int parent; 97 | public uint string_num; 98 | 99 | public static DirEntry Read(Stream s) => new DirEntry 100 | { 101 | path_hash = s.ReadUInt32LE(), 102 | parent = s.ReadInt32LE(), 103 | string_num = s.ReadUInt32LE() 104 | }; 105 | } 106 | 107 | private class OffsetEntry 108 | { 109 | public uint pk_offset; 110 | public ushort pk_num; 111 | public ushort unk; 112 | 113 | public static OffsetEntry Read(Stream s) => new OffsetEntry 114 | { 115 | pk_offset = s.ReadUInt32LE(), 116 | pk_num = s.ReadUInt16LE(), 117 | unk = s.ReadUInt16LE() 118 | }; 119 | } 120 | 121 | private void ParseHeader(IFile f) 122 | { 123 | headerStream.Position = 0; 124 | var numDataFiles = 0; 125 | var header = Header.Read(headerStream); 126 | var fileEntries = new FileEntry[header.num_files]; 127 | for(var i = 0; i < fileEntries.Length; i++) 128 | { 129 | fileEntries[i] = FileEntry.Read(headerStream); 130 | } 131 | 132 | var dirEntries = new DirEntry[header.num_dirs]; 133 | for(var i = 0; i < dirEntries.Length; i++) 134 | { 135 | dirEntries[i] = DirEntry.Read(headerStream); 136 | } 137 | 138 | var stringTable = new List(); // # of strings doesn't always match file count 139 | var stringTableEnd = header.string_table_offset + header.string_table_size; 140 | while(headerStream.Position < stringTableEnd) 141 | { 142 | stringTable.Add(headerStream.ReadASCIINullTerminated()); 143 | } 144 | 145 | var fileOffsets = new OffsetEntry[header.num_offsets]; 146 | for(var i = 0; i < fileOffsets.Length; i++) 147 | { 148 | fileOffsets[i] = OffsetEntry.Read(headerStream); 149 | if (fileOffsets[i].pk_num > numDataFiles) numDataFiles = fileOffsets[i].pk_num; 150 | } 151 | 152 | var dirs_flat = new Common.DefaultDirectory[header.num_dirs]; 153 | dirs_flat[0] = root; 154 | for(var i = 1; i < dirs_flat.Length; i++) 155 | { 156 | var parent = dirs_flat[dirEntries[i].parent]; 157 | parent.AddDir(dirs_flat[i] = new Common.DefaultDirectory(parent, stringTable[(int)dirEntries[i].string_num])); 158 | } 159 | 160 | contentFiles = new Stream[numDataFiles + 1]; 161 | var baseName = f.Name.Replace(".hdr.e.2", ""); 162 | for (var i = 0; i <= numDataFiles; i++) 163 | { 164 | contentFiles[i] = f.Parent.GetFile($"{baseName}.pk{i}").GetStream(); 165 | } 166 | 167 | for (var i = 0; i < fileEntries.Length; i++) 168 | { 169 | var entry = fileEntries[i]; 170 | var name = entry.string_num < stringTable.Count ? stringTable[(int)entry.string_num] : "ERROR_FILENAME"; 171 | bool encrypted = false; 172 | if (name.EndsWith(".e.2")) 173 | { 174 | encrypted = true; 175 | name = name.Remove(name.Length - 4); 176 | } 177 | dirs_flat[entry.dir_num].AddFile(new Common.OffsetFile( 178 | name, 179 | dirs_flat[entry.dir_num], 180 | contentFiles[fileOffsets[entry.offset_num].pk_num], 181 | fileOffsets[entry.offset_num].pk_offset, 182 | entry.size, 183 | wrapStream: encrypted ? (s => new PowerChordCryptStream(s)) : (Func)null)); 184 | } 185 | } 186 | 187 | public override string FileName { get; } 188 | 189 | public override IDirectory RootDirectory => root; 190 | 191 | public override long Size => headerStream.Length + contentFiles.Sum(f => f.Length); 192 | 193 | public override bool Writeable => false; 194 | 195 | public override void Dispose() 196 | { 197 | headerStream.Dispose(); 198 | foreach(var x in contentFiles) 199 | { 200 | x.Dispose(); 201 | } 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /Library/U8/U8Directory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * U8Directory.cs 3 | * 4 | * Copyright (c) 2015-2017, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using GameArchives.Common; 22 | 23 | namespace GameArchives.U8 24 | { 25 | class U8Directory : DefaultDirectory 26 | { 27 | public U8Directory(U8Directory parent, string name) : base(parent, name) 28 | { } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Library/U8/U8Package.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * U8Package.cs 3 | * 4 | * Copyright (c) 2015-2017, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | using System; 22 | using System.Collections.Generic; 23 | using System.IO; 24 | using System.Linq; 25 | using System.Text; 26 | 27 | namespace GameArchives.U8 28 | { 29 | class U8Package : AbstractPackage 30 | { 31 | const uint MAGIC = 0x55AA382D; 32 | const byte DIR = 1; 33 | const byte FILE = 0; 34 | 35 | public static PackageTestResult IsU8(IFile f) 36 | { 37 | using (Stream s = f.GetStream()) 38 | { 39 | s.Position = 0; 40 | return s.ReadUInt32BE() == MAGIC ? PackageTestResult.YES : PackageTestResult.NO; 41 | } 42 | } 43 | 44 | public static U8Package FromFile(IFile f) 45 | { 46 | return new U8Package(f); 47 | } 48 | 49 | public override string FileName { get; } 50 | 51 | public override IDirectory RootDirectory => root; 52 | 53 | public override long Size => filestream.Length; 54 | 55 | public override bool Writeable => false; 56 | 57 | public override Type FileType => typeof(OffsetFile); 58 | 59 | private Stream filestream; 60 | private U8Directory root; 61 | 62 | /// 63 | /// Open the .far archive which is the given file. 64 | /// 65 | /// 66 | private U8Package(IFile f) 67 | { 68 | FileName = f.Name; 69 | filestream = f.GetStream(); 70 | if (filestream.ReadUInt32BE() != MAGIC) 71 | throw new InvalidDataException("Package is not a U8 package."); 72 | uint rootNode = filestream.ReadUInt32BE(); 73 | uint dataOffset = filestream.ReadUInt32BE(); 74 | root = ReadFileTable(rootNode); 75 | } 76 | 77 | private U8Directory ReadFileTable(uint nodeOffset) 78 | { 79 | U8Directory root = new U8Directory(null, ROOT_DIR); 80 | filestream.Position = nodeOffset; 81 | U8Node rootNode = new U8Node() 82 | { 83 | type = filestream.ReadUInt8(), 84 | nameOffset = filestream.ReadUInt24BE(), 85 | dataOffset = filestream.ReadUInt32BE(), 86 | size = filestream.ReadUInt32BE() 87 | }; 88 | if (rootNode.type != DIR) 89 | throw new InvalidDataException("Root node of U8 archive was not a directory"); 90 | 91 | var stringTableOffset = nodeOffset + 12 * rootNode.size; 92 | var lastNodes = new Stack(); 93 | lastNodes.Push(rootNode.size); 94 | U8Directory currentDir = root; 95 | for(var i = 1; i < rootNode.size; i++) 96 | { 97 | if(i == lastNodes.Peek()) 98 | { 99 | lastNodes.Pop(); 100 | currentDir = currentDir.Parent as U8Directory; 101 | } 102 | 103 | var node = new U8Node() 104 | { 105 | type = filestream.ReadUInt8(), 106 | nameOffset = filestream.ReadUInt24BE(), 107 | dataOffset = filestream.ReadUInt32BE(), 108 | size = filestream.ReadUInt32BE() 109 | }; 110 | var pos = filestream.Position; 111 | filestream.Position = stringTableOffset + node.nameOffset; 112 | var name = filestream.ReadASCIINullTerminated(); 113 | filestream.Position = pos; 114 | if(node.type == DIR) 115 | { 116 | var newDir = new U8Directory(currentDir, name); 117 | currentDir.AddDir(newDir); 118 | currentDir = newDir; 119 | lastNodes.Push(node.size); 120 | } 121 | else 122 | { 123 | currentDir.AddFile(new OffsetFile(name, currentDir, filestream, node.dataOffset, node.size)); 124 | } 125 | } 126 | return root; 127 | } 128 | 129 | private struct U8Node 130 | { 131 | public byte type; 132 | public uint nameOffset; 133 | public uint dataOffset; 134 | public uint size; 135 | } 136 | 137 | public override void Dispose() 138 | { 139 | filestream.Close(); 140 | filestream.Dispose(); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /Library/Util.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * Util.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using System; 21 | using System.IO; 22 | 23 | namespace GameArchives 24 | { 25 | public static class Util 26 | { 27 | /// 28 | /// Opens a directory from the local filesystem as an IDirectory 29 | /// 30 | /// Path to the directory. 31 | /// An IDirectory representing the local directory. 32 | public static IDirectory LocalDir(string dir) 33 | { 34 | IDirectory d = new Local.LocalDirectory(dir); 35 | return d; 36 | } 37 | 38 | /// 39 | /// Create an instance of an IFile from the given local path. 40 | /// Note that this creates a new LocalDirectory object each time it is 41 | /// called. If you are opening a lot of files from one directory, it's more 42 | /// efficient to grab the directory with Util.LocalDir(), then get each 43 | /// file from there. 44 | /// 45 | /// 46 | /// 47 | public static IFile LocalFile(string file) 48 | { 49 | IDirectory d = new Local.LocalDirectory(Path.GetDirectoryName(file)); 50 | IFile f = d.GetFile(Path.GetFileName(file)); 51 | return f; 52 | } 53 | 54 | public static IFile NullFile() 55 | { 56 | return new Common.OffsetFile("", null, new MemoryStream(0), 0, 0); 57 | } 58 | 59 | /// 60 | /// Returns the last element of this array. 61 | /// 62 | /// The type of the array. 63 | /// 64 | /// The last element of the array. 65 | public static T Last(this T[] arr) 66 | { 67 | return arr[arr.Length - 1]; 68 | } 69 | 70 | /// 71 | /// Saves this file to the given path. Overwrites existing files. 72 | /// 73 | /// 74 | /// 75 | public static void ExtractTo(this IFile file, string path) 76 | { 77 | using (FileStream fs = new FileStream(path, FileMode.Create)) 78 | using (Stream s = file.GetStream()) 79 | { 80 | s.CopyTo(fs); 81 | } 82 | } 83 | 84 | // Only useful before .NET 4 85 | // by Jon Skeet (http://stackoverflow.com/questions/5730863/how-to-use-stream-copyto-on-net-framework-3-5) 86 | /// 87 | /// Copies one stream to the other. 88 | /// 89 | /// The source stream. 90 | /// The destination stream. 91 | public static void CopyTo(this Stream input, Stream output) 92 | { 93 | byte[] buffer = new byte[16 * 1024]; // Fairly arbitrary size 94 | int bytesRead; 95 | 96 | while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0) 97 | { 98 | output.Write(buffer, 0, bytesRead); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Library/XISO/XISODirectory.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * XISODirectory.cs 3 | * 4 | * Copyright (c) 2015,2016, maxton. All rights reserved. 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 3.0 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; If not, see 18 | * . 19 | */ 20 | using GameArchives.Common; 21 | using System; 22 | using System.Collections.Generic; 23 | using System.Linq; 24 | using System.Text; 25 | 26 | namespace GameArchives.XISO 27 | { 28 | /// 29 | /// Xbox (360) ISO Directory 30 | /// 31 | public class XISODirectory : DefaultDirectory, XISOFSNode 32 | { 33 | public long EntryLocation { get; } 34 | 35 | public XISODirectory(IDirectory parent, string name, long loc) : base(parent, name) 36 | { 37 | EntryLocation = loc; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Library/XISO/XISOFSNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace GameArchives.XISO 7 | { 8 | /// 9 | /// Represents an element of an XISO file system. 10 | /// 11 | public interface XISOFSNode : IFSNode 12 | { 13 | /// 14 | /// The location of the filesystem entry node in the ISO. 15 | /// 16 | long EntryLocation { get; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Library/XISO/XISOFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using GameArchives.Common; 4 | 5 | namespace GameArchives.XISO 6 | { 7 | public class XISOFile : OffsetFile, XISOFSNode 8 | { 9 | public long EntryLocation => (long) ExtendedInfo["EntryLocation"]; 10 | public XISOFile(string name, IDirectory parent, Stream img, long offset, long size, long entryLocation) 11 | : base(name, parent, img, offset, size) 12 | { 13 | ExtendedInfo.Add("EntryLocation", entryLocation); 14 | } 15 | 16 | internal void UpdateSize(long newSize) 17 | { 18 | Size = newSize; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GameArchives 2 | C# Library for reading video game archives, and example file browser ("ArchiveExplorer"). 3 | 4 | ## Downloading 5 | You can get the most up-to-date release from [Appveyor](https://ci.appveyor.com/project/maxton/gamearchives/build/artifacts) 6 | (download the Release-x.x.x.x.zip file). 7 | 8 | ## Supported Archive Formats 9 | ### Ark (*.hdr, *.ark) 10 | This format is used in many Harmonix games, including but not limited to: 11 | * Frequency 12 | * Amplitude (PS2/PS3 versions) 13 | * EyeToy: AntiGrav 14 | * Guitar Hero 1 - 2, Encore: Rocks the 80s 15 | * Rock Band 1 - 4, Lego, Green Day, Beatles, VR 16 | * Karaoke Revolution (untested) 17 | * MAGMA (RBN Authoring Tool, PC) 18 | * Disney Fantasia: Music Evolved 19 | 20 | Versions 1 through 7, 9, and 10 are supported at this time. 21 | 22 | ### FSG-FILE-SYSTEM (DISC0.img) 23 | This format is used in some FreeStyleGames games, including: 24 | * DJ Hero 2 25 | * Guitar Hero Live 26 | 27 | #### Notes 28 | Usually, these are on disc as DISC0.img.part0, DISC0.img.part1. 29 | The library will handle these files in addition to the combined DISC0.img. 30 | 31 | ### FSAR (*.far) 32 | This format is used in some FreeStyleGames games, including: 33 | * DJ Hero 1,2 34 | * Guitar Hero Live 35 | * Sing Party 36 | 37 | This format may use compression. 38 | 39 | ### PFS (pfs_image.dat, etc) 40 | This format is used for downloadable content and games on the PS4. It is structured much like the Unix File System. 41 | PKG files from game discs and downloads contain encrypted (and compressed?) PFS images within them. 42 | 43 | ### PSARC (*.psarc, *.pak) 44 | This format is used commonly in Playstation 3 and 4 games, and usually has the extension .psarc. 45 | The files within can be compressed with zlib or lzma. Currently, only zlib-compressed archives are supported. 46 | 47 | ### STFS (*) 48 | This includes the CON and LIVE formats, used for game saves and downloadable 49 | content (among other things) on the Xbox 360. Since documentation on the format 50 | is somewhat limited, you may come across errors when trying to read these 51 | files. Please report any errors you encounter. 52 | 53 | ### XDVDFS / GDFS (*.iso) 54 | This is the file system used on Xbox and Xbox 360 game discs. 55 | 56 | ### U8 (*.arc, *.app, etc) 57 | This is a simple archive format commonly used in Wii games and system software, 58 | which gets its name from the printable ASCII characters in its magic number. 59 | 60 | ### PACKAGE (*.pkf, *.themes) 61 | Unencrypted archive format used in SingStar games on PS3. 62 | 63 | ### Seven45 PK (*.hdr.e.2) 64 | Archive format with an encrypted header used in Power Gig: Rise of the SixString by Seven45 Studios. -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.12.0.{build} 2 | 3 | image: Visual Studio 2017 4 | 5 | configuration: Release 6 | platform: Any CPU 7 | 8 | build: 9 | project: GameArchives.sln 10 | 11 | after_build: 12 | - > 13 | 7z a Release-%APPVEYOR_BUILD_VERSION%.zip 14 | %APPVEYOR_BUILD_FOLDER%\ArchiveExplorer\bin\Release\ArchiveExplorer.exe 15 | %APPVEYOR_BUILD_FOLDER%\ArchiveExplorer\bin\Release\GameArchives.dll 16 | %APPVEYOR_BUILD_FOLDER%\ArchiveExplorer\bin\Release\GameArchives.xml 17 | %APPVEYOR_BUILD_FOLDER%\ArchiveExplorer\bin\Release\LibArchiveExplorer.dll 18 | %APPVEYOR_BUILD_FOLDER%\ArchiveExplorer\bin\Release\LICENSE 19 | %APPVEYOR_BUILD_FOLDER%\README.md 20 | 21 | artifacts: 22 | - path: Release-%APPVEYOR_BUILD_VERSION%.zip --------------------------------------------------------------------------------