├── .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
--------------------------------------------------------------------------------