├── .gitignore
├── MapPacker
├── .editorconfig
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── AssetPacker.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── MapPacker.csproj
├── MapPacker.csproj.user
├── MapPacker.sln
├── favicon.ico
├── icons
│ ├── icon_eagleone.png
│ ├── icon_file.png
│ ├── icon_folder.png
│ ├── mappacker.png
│ └── resizeIcon.png
├── sounds
│ └── steam-message.wav
└── style.txt
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | MapPacker/bin/
2 | MapPacker/obj/
3 | MapPacker/.vs/
4 | .vs/
5 |
--------------------------------------------------------------------------------
/MapPacker/.editorconfig:
--------------------------------------------------------------------------------
1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories
2 | root = true
3 |
4 | # C# files
5 | [*.cs]
6 | indent_style = tab
7 | indent_size = tab
8 | tab_size = 4
9 |
10 | # New line preferences
11 | end_of_line = crlf
12 | insert_final_newline = true
13 |
14 |
15 | #### C# Coding Conventions ####
16 |
17 | # Expression-bodied members
18 | csharp_style_expression_bodied_accessors = true:silent
19 | csharp_style_expression_bodied_constructors = false:silent
20 | csharp_style_expression_bodied_indexers = true:silent
21 | csharp_style_expression_bodied_lambdas = true:silent
22 | csharp_style_expression_bodied_local_functions = false:silent
23 | csharp_style_expression_bodied_methods = false:silent
24 | csharp_style_expression_bodied_operators = false:silent
25 | csharp_style_expression_bodied_properties = true:silent
26 |
27 | # Pattern matching preferences
28 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
29 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
30 | csharp_style_prefer_not_pattern = true:suggestion
31 | csharp_style_prefer_pattern_matching = true:silent
32 | csharp_style_prefer_switch_expression = true:suggestion
33 |
34 | # Null-checking preferences
35 | csharp_style_conditional_delegate_call = true:suggestion
36 |
37 | # Code-block preferences
38 | csharp_prefer_braces = true:silent
39 |
40 | # Expression-level preferences
41 | csharp_prefer_simple_default_expression = true:suggestion
42 | csharp_style_deconstructed_variable_declaration = true:suggestion
43 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
44 | csharp_style_inlined_variable_declaration = true:suggestion
45 | csharp_style_pattern_local_over_anonymous_function = true:suggestion
46 | csharp_style_prefer_index_operator = true:suggestion
47 | csharp_style_prefer_range_operator = true:suggestion
48 | csharp_style_throw_expression = true:suggestion
49 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion
50 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent
51 |
52 | # 'using' directive preferences
53 | csharp_using_directive_placement = outside_namespace:silent
54 |
55 | #### C# Formatting Rules ####
56 |
57 | # New line preferences
58 | csharp_new_line_before_catch = false
59 | csharp_new_line_before_else = false
60 | csharp_new_line_before_finally = false
61 | csharp_new_line_before_members_in_anonymous_types = false
62 | csharp_new_line_before_members_in_object_initializers = false
63 | csharp_new_line_before_open_brace = false
64 | csharp_new_line_between_query_expression_clauses = false
65 |
66 | # Indentation preferences
67 | csharp_indent_block_contents = true
68 | csharp_indent_braces = false
69 | csharp_indent_case_contents = true
70 | csharp_indent_case_contents_when_block = true
71 | csharp_indent_labels = no_change
72 | csharp_indent_switch_labels = true
73 |
74 | # Space preferences
75 | csharp_space_after_cast = false
76 | csharp_space_after_colon_in_inheritance_clause = true
77 | csharp_space_after_comma = true
78 | csharp_space_after_dot = false
79 | csharp_space_after_keywords_in_control_flow_statements = false
80 | csharp_space_after_semicolon_in_for_statement = true
81 | csharp_space_around_binary_operators = before_and_after
82 | csharp_space_around_declaration_statements = false
83 | csharp_space_before_colon_in_inheritance_clause = true
84 | csharp_space_before_comma = false
85 | csharp_space_before_dot = false
86 | csharp_space_before_open_square_brackets = false
87 | csharp_space_before_semicolon_in_for_statement = false
88 | csharp_space_between_empty_square_brackets = false
89 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
90 | csharp_space_between_method_call_name_and_opening_parenthesis = false
91 | csharp_space_between_method_call_parameter_list_parentheses = false
92 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
93 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
94 | csharp_space_between_method_declaration_parameter_list_parentheses = false
95 | csharp_space_between_parentheses = false
96 | csharp_space_between_square_brackets = false
97 |
98 | # Wrapping preferences
99 | csharp_preserve_single_line_blocks = true
100 | csharp_preserve_single_line_statements = true
--------------------------------------------------------------------------------
/MapPacker/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/MapPacker/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace MapPacker
10 | {
11 | ///
12 | /// Interaction logic for App.xaml
13 | ///
14 | public partial class App : Application
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MapPacker/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/MapPacker/AssetPacker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.IO;
5 | using System.Threading;
6 | using System.Media;
7 | using System.Windows;
8 |
9 | namespace MapPacker {
10 | class AssetPacker{
11 |
12 | public static HashSet assets = new HashSet();
13 | private string assetPath;
14 | public string outputDirectory;
15 | private string vpkDirectory;
16 | private string vmapFile;
17 |
18 | public MainWindow parentForm;
19 |
20 | public AssetPacker(string assetDir, string sboxDir, string vmapFile) {
21 | this.assetPath = assetDir;
22 | this.vpkDirectory = sboxDir + "\\bin\\win64\\vpk.exe";
23 | this.vmapFile = vmapFile;
24 | outputDirectory = Path.GetDirectoryName(vmapFile) + "\\" + Path.GetFileNameWithoutExtension(vmapFile);
25 | }
26 |
27 | private bool noNotf = false;
28 | private bool vpkFailed = false;
29 |
30 | public void GetAssets() {
31 | GetAssets(false);
32 | }
33 |
34 | public void GetAssets(bool noNotf = false) {
35 |
36 | if(noNotf)
37 | this.noNotf = noNotf;
38 |
39 | if(parentForm.Pack) {
40 | Directory.CreateDirectory(outputDirectory);
41 | }
42 |
43 | parentForm.SetProgress(10);
44 |
45 | // path where the map is
46 | string pathToMap = vmapFile;
47 |
48 | parentForm.PrintToConsole($"reading map file: {pathToMap}. This might take some time for big maps!");
49 | GetAssetsFromMap(pathToMap, true);
50 |
51 | parentForm.SetProgress(30);
52 |
53 | if(assets.Count > 0) {
54 | parentForm.PrintToConsole("Found assets:");
55 | } else {
56 | parentForm.PrintToConsole("No assets found in provided asset directory!");
57 | parentForm.SetProgress(0);
58 | if(!this.noNotf)
59 | MessageBox.Show("No assets could be found!", "Alert", MessageBoxButton.OK, MessageBoxImage.Warning);
60 | return;
61 | }
62 | foreach(string asset in assets) {
63 | parentForm.PrintToConsole($"\t{asset}", "steam2004ControlText");
64 | }
65 |
66 | parentForm.SetProgress(40);
67 | CopyFiles();
68 | }
69 |
70 | public void ExecuteCommandAsync(string command) {
71 | try {
72 | //Asynchronously start the Thread to process the Execute command request.
73 | Thread objThread = new Thread(new ParameterizedThreadStart(ExecuteCommandSync));
74 | //Make the thread as background thread.
75 | objThread.IsBackground = true;
76 | //Set the Priority of the thread.
77 | objThread.Priority = ThreadPriority.AboveNormal;
78 | //Start the thread.
79 | objThread.Start(command);
80 | } catch {
81 | }
82 | }
83 |
84 | public void ExecuteCommandSync(object command) {
85 | try {
86 | System.Diagnostics.ProcessStartInfo procStartInfo =
87 | _ = new System.Diagnostics.ProcessStartInfo(vpkDirectory, "/c");
88 |
89 | procStartInfo.Arguments = $"{command}";
90 | procStartInfo.RedirectStandardOutput = true;
91 | procStartInfo.UseShellExecute = false;
92 | procStartInfo.CreateNoWindow = true;
93 | System.Diagnostics.Process proc = new System.Diagnostics.Process();
94 | proc.StartInfo = procStartInfo;
95 | proc.Start();
96 | // Get the output into a string
97 | string result = proc.StandardOutput.ReadToEnd();
98 | //parentForm.PrintToConsole($"{result}", "steam2004ControlText");
99 |
100 | //vpk.exe does not output any important information and normal packing/unpacking does not allow for the verbose option.....
101 |
102 | } catch {
103 | }
104 | }
105 |
106 | public void CopyFiles() {
107 |
108 | if(!parentForm.Pack) {
109 | outputDirectory += "_content";
110 | parentForm.PrintToConsole("\nAssets Copied: ");
111 | } else {
112 | ExtractVPK(); // extract first, pack after copying
113 | if(vpkFailed) { // treat as nopack operation of vpk.exe didn't run properly (FOR SOME BLOODY REASON)
114 | parentForm.PrintToConsole("\nAssets Copied: ");
115 | } else {
116 | parentForm.PrintToConsole("\nAssets Packed: ");
117 | }
118 | }
119 |
120 | int index = 0;
121 | foreach(string asset in assets) {
122 | index++;
123 |
124 | parentForm.SetProgress(40 + 30 * (int)Math.Round(index / (float)assets.Count));
125 |
126 | string fileName = asset;
127 | try {
128 | string source = Path.Combine(assetPath, fileName);
129 | string destination = Path.Combine(outputDirectory, fileName);
130 | string directory = destination.Substring(0, destination.LastIndexOf("\\"));
131 |
132 | if(File.Exists(source)) {
133 | Directory.CreateDirectory(directory);
134 | File.Copy(source, destination, true);
135 | parentForm.PrintToConsole($"\t{asset}", "steam2004ControlText");
136 | } else {
137 | // asset is in core files or another addon, ignore
138 | }
139 | } catch {
140 | // asset not found, broken or otherwise defunct, ignore
141 | }
142 | }
143 | if(parentForm.Pack && !vpkFailed) {
144 | PackVPK();
145 | } else {
146 | parentForm.SetProgress(0);
147 |
148 | parentForm.PrintToConsole("\nAsset copy completed.");
149 | if(!this.noNotf)
150 | MessageBox.Show($"Content successfully copied! {(vpkFailed ? "\nvpk.exe failed! Content is located separate from the vpk." : "")}", "Complete", MessageBoxButton.OK, MessageBoxImage.Information);
151 | parentForm.SetCheckBoxEnabled(true);
152 | }
153 | }
154 |
155 | public void ExtractVPK() {
156 | parentForm.PrintToConsole("\nUnpacking vpk...\n");
157 | //execute vpk file.vpk to extract
158 | string command = $"{vmapFile.Replace(".vmap", ".vpk")}";
159 | ExecuteCommandSync(command);
160 | if(!Directory.Exists(outputDirectory) || vpkFailed) { //hacky check to see if vpk.exe ran properly... extra check is for debug
161 | vpkFailed = true;
162 | outputDirectory += "_content";
163 | parentForm.PrintToConsole("\tvpk.exe failed to run! Treating this as a nopack operation instead... ");
164 | }
165 | if(parentForm.Pack && !vpkFailed) { // only "make" a backup if the original was extracted successfully, meaning it will be packed with content
166 | File.Move($"{vmapFile.Replace(".vmap", ".vpk")}", $"{vmapFile.Replace(".vmap", ".vpk.backup")}", true);
167 | parentForm.PrintToConsole("\nvpk unpacked\n");
168 | }
169 | parentForm.SetProgress(95);
170 | }
171 |
172 | public void PackVPK() {
173 | // execute vpk outputDirectory to repack
174 | string command = $"{outputDirectory}";
175 | ExecuteCommandSync(command);
176 | //parentForm.PrintToConsole("\nPacked vpk\n");
177 | parentForm.SetProgress(0);
178 |
179 | // delete temp directory
180 | Directory.Delete(outputDirectory, true);
181 |
182 | //SoundPlayer player = new SoundPlayer(Properties.Resources.steam_message);
183 | //player.Play();
184 | parentForm.PrintToConsole("\nAsset pack completed.\n");
185 | if(!this.noNotf)
186 | MessageBox.Show("Map Successfully packed!", "Complete", MessageBoxButton.OK, MessageBoxImage.Information);
187 | parentForm.SetCheckBoxEnabled(true);
188 | }
189 |
190 | public void GetAssetsFromMap(string map, bool rootMap = false) { // this uses a full path, since it's kinda for the original map
191 | try {
192 | var mapData = File.ReadAllBytes($"{map}");
193 | if(rootMap)
194 | parentForm.PrintToConsole($"Read Vmap file, parsing...");
195 | AssetFile mapFile = AssetFile.From(mapData);
196 | mapFile.TrimAssetList(); // trim it to the space where assets are actually referenced, using "map_assets_references" markers
197 |
198 | foreach(var item in mapFile.SplitNull()) {
199 | if(item.EndsWith("vmat")) {
200 | GetAssetsFromMaterial(item);
201 | } else if(item.EndsWith("vmdl")) {
202 | GetAssetsFromModel(item);
203 | } else if(item.EndsWith("vpcf")) {
204 | GetAssetsFromParticle(item);
205 | } else if(item.EndsWith("vmap")) {
206 | var mapItem = CleanAssetPath(item);
207 | var path = TrimAddonPath(map);
208 | if(!($"{path}\\{item}" == map)) { // this will also deal with 3d skybox content
209 | GetAssetsFromMap($"{path}\\{item}"); // we can assume that prefabs will also be wherever the original map is
210 | GetAssetsFromMap($"{assetPath}\\{item}"); // prefabs *could* also be in the asset directory however
211 | }
212 | } else if (item.EndsWith("vpost")) {
213 | AddAsset(item); // post processing file
214 | }
215 | }
216 | } catch {
217 | // asset not in asset directory or not found
218 | }
219 | }
220 |
221 | // this is for all Model related asset types, including legacy support; vmdl, vmesh, vphys, vseq, vagrp and vanim
222 | public void GetAssetsFromModel(string item) {
223 | try {
224 | var data = File.ReadAllBytes($"{assetPath}\\{item}_c");
225 | AddAsset(item); // add vmdl_c (or legacy reference)
226 | var material = AssetFile.From(data);
227 | foreach(var matItem in material.SplitNull()) {
228 | if(matItem.EndsWith("vmat")) {
229 | // add vmat_c referenced by the vmdl_c
230 | GetAssetsFromMaterial(matItem); // add vtex_c referenced by the vmat_c
231 |
232 | // LEGACY IMPORT SUPPORT
233 | } else if(matItem.EndsWith("vmesh")) {
234 | // add vmesh_c referenced by the vmdl_c
235 | GetAssetsFromModel(matItem); // add vmat_c referenced by the vmesh_c
236 | } else if(matItem.EndsWith("vphys")) {
237 | AddAsset(matItem); // add vphys_c referenced by a vmesh_c
238 | } else if(matItem.EndsWith("vseq")) {
239 | AddAsset(matItem); // add vseq_c referenced by a vmdl_c
240 | } else if(matItem.EndsWith("vagrp")) {
241 | // add vagrp_c referenced by a vmdl_c
242 | GetAssetsFromModel(matItem); // add vanim_c referenced by a vagrp
243 | } else if(matItem.EndsWith("vanim")) {
244 | AddAsset(matItem); // add vanim_c referenced by a vagrp_c
245 | }
246 | }
247 | } catch {
248 | // asset not in asset directory or not found
249 | }
250 | }
251 |
252 | public void GetAssetsFromMaterial(string item) {
253 | try {
254 | var materialData = File.ReadAllBytes($"{assetPath}\\{item}_c");
255 | AddAsset(item); // add vmat_c
256 | var material = AssetFile.From(materialData);
257 | foreach(var texItem in material.SplitNull()) {
258 | if(texItem.EndsWith("vtex")) {
259 | AddAsset(texItem); // add vtex_c referenced by the vmat_c
260 | }
261 | }
262 | } catch {
263 | // asset not in asset directory or not found
264 | }
265 | }
266 |
267 | public void GetAssetsFromParticle(string item) {
268 | try {
269 | var materialData = File.ReadAllBytes($"{assetPath}\\{item}_c");
270 | AddAsset(item); // add vpcf_c
271 | var material = AssetFile.From(materialData);
272 | foreach(var assetItem in material.SplitNull()) {
273 | if(assetItem.EndsWith("vtex")) {
274 | AddAsset(assetItem); // add vtex_c referenced by the vpcf_c
275 | } else if(assetItem.EndsWith("vmat")) {
276 | GetAssetsFromMaterial(assetItem);
277 | } else if(assetItem.EndsWith("vmdl")) {
278 | GetAssetsFromModel(assetItem);
279 | } else if(assetItem.EndsWith("vpcf")) {
280 | if(!(item == assetItem)) // if there's a self reference in the particle. this is often the case for some reason
281 | GetAssetsFromParticle(assetItem);
282 | }
283 | }
284 | } catch {
285 | // asset not in asset directory or not found
286 | }
287 | }
288 |
289 | public static void AddAsset(string item) {
290 | // basic clean
291 | var asset = CleanAssetPath(item);
292 |
293 | if(!asset.EndsWith("_c")) {
294 | // make sure the file is an asset file as we'd want it
295 | return;
296 | }
297 | assets.Add(asset);
298 | }
299 |
300 | public static string CleanAssetPath(string item) {
301 | var asset = item.Replace("\r\n", "").Replace("\r", "").Replace("\n", "").Replace("/", "\\");
302 |
303 | // this is dumb
304 | if(asset.EndsWith("vmat")) {
305 | asset = asset.Replace("vmat", "vmat_c");
306 | } else if(asset.EndsWith("vmdl")) {
307 | asset = asset.Replace("vmdl", "vmdl_c");
308 | } else if(asset.EndsWith("vtex")) {
309 | asset = asset.Replace("vtex", "vtex_c");
310 | } else if(asset.EndsWith("vpcf")) {
311 | asset = asset.Replace("vpcf", "vpcf_c");
312 | } else if(asset.EndsWith("vsnd")) {
313 | asset = asset.Replace("vsnd", "vsnd_c");
314 | } else if(asset.EndsWith("vphys")) {
315 | asset = asset.Replace("vphys", "vphys_c");
316 | } else if(asset.EndsWith("vmesh")) {
317 | asset = asset.Replace("vmesh", "vmesh_c");
318 | } else if(asset.EndsWith("vanim")) {
319 | asset = asset.Replace("vanim", "vanim_c");
320 | } else if(asset.EndsWith("vpost")) {
321 | asset = asset.Replace("vpost", "vpost_c");
322 | } else if(asset.EndsWith("vseq")) {
323 | asset = asset.Replace("vseq", "vseq_c");
324 | } else if(asset.EndsWith("vagrp")) {
325 | asset = asset.Replace("vagrp", "vagrp_c");
326 | }
327 | return asset;
328 | }
329 |
330 | public static string TrimAddonPath(string path) {
331 | // trim full path to .../addons/addonName/
332 | var addonsIndex = path.LastIndexOf("addons");
333 | var trim1 = path.Substring(0, addonsIndex + 7);
334 | var trim2 = path.Substring(addonsIndex + 7, path.Length - addonsIndex - 7);
335 | var addonName = trim2.Substring(0, trim2.IndexOf("\\"));
336 | return trim1 + addonName;
337 | }
338 | }
339 |
340 | // this should probably get reworked with asset type enums
341 | public class AssetFile {
342 | private string assetReference;
343 | public static AssetFile From(byte[] bytes) {
344 | AssetFile asset = new AssetFile();
345 | asset.assetReference = Encoding.Default.GetString(bytes);
346 | return asset;
347 | }
348 |
349 | public string[] SplitNull() {
350 | string[] arr = { "\0" }; // lol why doesn't this have an overload for strings
351 | return assetReference.Split(arr, StringSplitOptions.RemoveEmptyEntries);
352 | }
353 |
354 | public string TrimAssetList(string marker = "map_asset_references") {
355 | var start = assetReference.IndexOf(marker);
356 | var end = assetReference.LastIndexOf(marker);
357 | var output = assetReference.Substring(start, end - start);
358 | return output;
359 | }
360 |
361 | // obsolete since we're doing prefab scans anyways
362 | public bool IsMapSkybox() {
363 | var splitStrings = this.SplitNull();
364 |
365 | for(var i = 0; i < splitStrings.Length; i++) {
366 | var item = splitStrings[i];
367 | if(item == "mapUsageType") {
368 | if(splitStrings[i + 1] == "skybox") { // skybox map type
369 | return true;
370 | }
371 | }
372 | }
373 | return false;
374 | }
375 | }
376 | }
377 |
--------------------------------------------------------------------------------
/MapPacker/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 | #FFD8DED3
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
88 |
89 |
102 |
103 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
163 |
164 |
165 |
166 |
167 |
178 |
179 |
183 |
216 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
342 |
347 |
352 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
--------------------------------------------------------------------------------
/MapPacker/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.IO;
3 | using System.Threading;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Documents;
7 | using System.Windows.Input;
8 | using System.Collections.Generic;
9 |
10 | namespace MapPacker {
11 | ///
12 | /// Interaction logic for MainWindow.xaml
13 | ///
14 | public partial class MainWindow : Window
15 | {
16 | private RichTextBox ConsoleOutput;
17 | private ProgressBar _ProgressBar;
18 | public CheckBox PackCheckBox;
19 | private bool _PackCheck = true;
20 | public bool Packing = false;
21 |
22 | public double Progress {
23 | get {
24 | return _ProgressBar.Value;
25 | }
26 | }
27 |
28 | public bool Pack {
29 | get {
30 | return _PackCheck;
31 | }
32 | }
33 |
34 | public void SetProgress(float progress) {
35 | Application.Current.Dispatcher.Invoke(() => {
36 | _ProgressBar.Value = progress;
37 | });
38 | }
39 |
40 | private Dictionary dictionary;
41 |
42 | public MainWindow() {
43 | InitializeComponent();
44 | _ProgressBar = (ProgressBar)this.FindName("ProgressBar");
45 | ConsoleOutput = (RichTextBox)this.FindName("ConsoleOutputText");
46 | PackCheckBox = (CheckBox)this.FindName("PackCheck");
47 |
48 | string[] args = System.Environment.GetCommandLineArgs();
49 |
50 | if(args.Length >= 3) { // cli args come in groups of two. first arg is always the source executing the program
51 | dictionary = new();
52 |
53 | for(int index = 1; index < args.Length; index += 2) {
54 | dictionary.Add(args[index], args[index + 1]);
55 | }
56 |
57 | //PrintToConsole($"args");
58 | bool sbox = false;
59 | bool vmap = false;
60 | bool assets = false;
61 |
62 | bool pack = true;
63 | bool pause = false;
64 |
65 | string sboxPath = "";
66 | string vmapPath = "";
67 | string assetsPath = "";
68 |
69 | PrintToConsole("MapPacker CLI usage");
70 |
71 | string value;
72 | if(dictionary.TryGetValue("-sbox", out value)) {
73 | PrintToConsole($"\n-sbox: {value}");
74 | if(!Directory.Exists(value)) {
75 | PrintToConsole("INVALID PATH");
76 | } else {
77 | PrintToConsole("VALID");
78 | var box = (RichTextBox)this.FindName("sboxLocation");
79 | box.Document.Blocks.Clear();
80 | box.Document.Blocks.Add(new Paragraph(new Run(value)));
81 | sboxPath = value;
82 | sbox = true;
83 | }
84 | }
85 | if(dictionary.TryGetValue("-vmap", out value)) {
86 | PrintToConsole($"\n-vmap: {value}");
87 | if(!File.Exists(value)) {
88 | PrintToConsole("INVALID PATH");
89 | } else {
90 | PrintToConsole("VALID");
91 | var box = (RichTextBox)this.FindName("vmapLocation");
92 | box.Document.Blocks.Clear();
93 | box.Document.Blocks.Add(new Paragraph(new Run(value)));
94 | vmapPath = value;
95 | vmap = true;
96 | }
97 | }
98 | if(dictionary.TryGetValue("-assets", out value)) {
99 | PrintToConsole($"\n-assets: {value}");
100 | if(!Directory.Exists(value)) {
101 | PrintToConsole("INVALID PATH");
102 | } else {
103 | PrintToConsole("VALID");
104 | var box = (RichTextBox)this.FindName("assetLocation");
105 | box.Document.Blocks.Clear();
106 | box.Document.Blocks.Add(new Paragraph(new Run(value)));
107 | assetsPath = value;
108 | assets = true;
109 | }
110 | }
111 | if(dictionary.TryGetValue("-pause", out value)) {
112 | if(bool.Parse(value))
113 | pause = true;
114 | }
115 | if(dictionary.TryGetValue("-pack", out value)) {
116 | if(!bool.Parse(value)) {
117 | PackCheckBox.IsEnabled = true;
118 | pack = false;
119 | }
120 | }
121 |
122 | if(sbox && vmap && assets) { // all main params valid
123 | PrintToConsole("\nAll parameters valid, continuing...\n");
124 | _PackCheck = pack;
125 | AssetPacker AssetPacker = new AssetPacker(assetsPath, sboxPath, vmapPath);
126 | AssetPacker.parentForm = this;
127 | if(pause)
128 | new Thread(AssetPacker.GetAssets).Start();
129 | else
130 | AssetPacker.GetAssets(true);
131 |
132 | if(!pause) {
133 | Close();
134 | return;
135 | }
136 | } else {
137 | PrintToConsole("\nNot enough valid parameters to continue!");
138 | if(!pause) {
139 | Close();
140 | return;
141 | }
142 | }
143 |
144 | } else {
145 | PrintToConsole("Welcome to the Eagle One Asset Packer!");
146 | PrintToConsole("\n\tHow to Use:");
147 | PrintToConsole("\n\t[+] Compile your map.");
148 | PrintToConsole("\t[+] Make sure your vmap and compiled vpk are in the same directory.");
149 | PrintToConsole("\t[+] Select your vmap, asset directory and sbox directory in the boxes above.");
150 | PrintToConsole("\t[+] Click pack. The found and packed assets will be listed below.");
151 | PrintToConsole("\n\t[+] In case you don't want to have your content packed, just check the box and it will instead appear in a folder called 'yourMap_content'.");
152 | PrintToConsole("\nFor any additional help, contact 'DoctorGurke#0007' or 'Josh Wilson#9332' on discord or make an issue on the Github.");
153 | }
154 | }
155 |
156 | private void Window_MouseDown(object sender, MouseButtonEventArgs e) {
157 | if (e.ChangedButton == MouseButton.Left)
158 | this.DragMove();
159 | }
160 |
161 | private void CloseButton_Click(object sender, RoutedEventArgs e) {
162 | Close();
163 | }
164 |
165 | private void MinimizeButton_Click(object sender, RoutedEventArgs e) {
166 | WindowState = WindowState.Minimized;
167 | }
168 |
169 | private void LinkLabel_Click(object sender, RoutedEventArgs e) {
170 | Hyperlink source = sender as Hyperlink;
171 |
172 | if(source != null) {
173 |
174 | Process.Start(new ProcessStartInfo("cmd", $"/c start {source.NavigateUri}") { CreateNoWindow = true });
175 | }
176 | }
177 |
178 | private void OpenVmapExplorer(object sender, RoutedEventArgs e) {
179 | var dialog = new Microsoft.Win32.OpenFileDialog();
180 | dialog.FileName = ""; // Default file name
181 | dialog.DefaultExt = ".vmap"; // Default file extension
182 | dialog.Filter = "Source 2 Map File (.vmap)|*.vmap"; // Filter files by extension
183 | dialog.Title = "Select your vmap File";
184 |
185 | // Show open file dialog box
186 | bool? result = dialog.ShowDialog();
187 |
188 | // Process open file dialog box results
189 | if(result == true) {
190 | // Open document
191 | string filename = dialog.FileName;
192 | var myTextBlock = (RichTextBox)this.FindName("vmapLocation");
193 | if(myTextBlock != null) {
194 | myTextBlock.Document.Blocks.Clear();
195 | myTextBlock.Document.Blocks.Add(new Paragraph(new Run(filename)));
196 | }
197 | }
198 | }
199 |
200 | private void OpenAssetExplorer(object sender, RoutedEventArgs e) {
201 | using(var dialog = new System.Windows.Forms.FolderBrowserDialog()) {
202 | dialog.Description = "Select your Asset Directory";
203 | dialog.UseDescriptionForTitle = true;
204 |
205 | System.Windows.Forms.DialogResult result = dialog.ShowDialog();
206 |
207 | var myTextBlock = (RichTextBox)this.FindName("assetLocation");
208 | if(myTextBlock != null && result.ToString() == "OK") {
209 | myTextBlock.Document.Blocks.Clear();
210 | myTextBlock.Document.Blocks.Add(new Paragraph(new Run(dialog.SelectedPath)));
211 | }
212 | }
213 | }
214 |
215 | private void OpenSboxExplorer(object sender, RoutedEventArgs e) {
216 | using(var dialog = new System.Windows.Forms.FolderBrowserDialog()) {
217 | dialog.Description = "Select your s&box Directory";
218 | dialog.UseDescriptionForTitle = true;
219 |
220 | System.Windows.Forms.DialogResult result = dialog.ShowDialog();
221 |
222 | var myTextBlock = (RichTextBox)this.FindName("sboxLocation");
223 | if(myTextBlock != null && result.ToString() == "OK") {
224 | myTextBlock.Document.Blocks.Clear();
225 | myTextBlock.Document.Blocks.Add(new Paragraph(new Run(dialog.SelectedPath)));
226 | }
227 | }
228 | }
229 |
230 | public void SetCheckBoxEnabled(bool state) {
231 | Application.Current.Dispatcher.Invoke(() => {
232 | PackCheckBox.IsEnabled = state;
233 | });
234 | }
235 |
236 | public void PrintToConsole(string text) {
237 | Application.Current.Dispatcher.Invoke(() => {
238 | ConsoleOutput.Document.Blocks.Add(new Paragraph(new Run(text)));
239 | ConsoleOutput.ScrollToEnd();
240 | });
241 | }
242 |
243 | public void PrintToConsole(string text, string resource) {
244 | // this is dumb
245 | Application.Current.Dispatcher.Invoke(() => {
246 | var paragraph = new Paragraph(new Run(text));
247 | var style = new Style(typeof(Paragraph));
248 | var foregroundSetter = new Setter(ForegroundProperty, TryFindResource(resource)); // apply Foreground setter with resource
249 | style.Setters.Add(foregroundSetter); // add setter to style
250 | paragraph.Margin = new Thickness(0); // readd margin override
251 | paragraph.Style = style; // add style to paragraph
252 | ConsoleOutput.Document.Blocks.Add(paragraph); // send paragraph to richtextbox
253 | ConsoleOutput.ScrollToEnd(); // scroll down
254 | });
255 | }
256 |
257 | private void ConfirmButton_Click(object sender, RoutedEventArgs e) {
258 |
259 | var text = (RichTextBox)this.FindName("vmapLocation");
260 | string vmapFile = new TextRange(text.Document.ContentStart, text.Document.ContentEnd).Text.Replace("\r\n", "");
261 | if(!File.Exists(vmapFile)) {
262 | MessageBox.Show("Vmap file path invalid!", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
263 | return;
264 | }
265 |
266 | text = (RichTextBox)this.FindName("assetLocation");
267 | string assetDir = new TextRange(text.Document.ContentStart, text.Document.ContentEnd).Text.Replace("\r\n", "");
268 | if(!Directory.Exists(assetDir)) {
269 | MessageBox.Show("Asset Directory path invalid!", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
270 | return;
271 | }
272 |
273 | text = (RichTextBox)this.FindName("sboxLocation");
274 | string sboxDir = new TextRange(text.Document.ContentStart, text.Document.ContentEnd).Text.Replace("\r\n", "");
275 | if(!Directory.Exists(sboxDir) || !File.Exists($"{sboxDir}\\sbox.exe")) {
276 | MessageBox.Show("s&box Directory path invalid!", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
277 | return;
278 | }
279 |
280 | ConsoleOutput.Document.Blocks.Clear();
281 |
282 | Packing = true;
283 | SetCheckBoxEnabled(false);
284 | AssetPacker AssetPacker = new AssetPacker(assetDir, sboxDir, vmapFile);
285 | AssetPacker.parentForm = this;
286 | new Thread(AssetPacker.GetAssets).Start();
287 | }
288 |
289 | private void PackCheck_Checked(object sender, RoutedEventArgs e) {
290 | //ConfirmButton
291 | var button = (Button)this.FindName("ConfirmButton");
292 | button.Content = "Copy Assets";
293 | _PackCheck = false;
294 | //PrintToConsole($"check changed to {(sender as CheckBox).IsChecked}", "steam2004ControlText");
295 | }
296 |
297 | private void PackCheck_Unchecked(object sender, RoutedEventArgs e) {
298 | var button = (Button)this.FindName("ConfirmButton");
299 | button.Content = "Pack Assets";
300 | _PackCheck = true;
301 | //PrintToConsole($"\tcheck changed to {(sender as CheckBox).IsChecked}");
302 | }
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/MapPacker/MapPacker.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net5.0-windows
6 | true
7 | MapPacker.App
8 | favicon.ico
9 |
10 |
11 |
12 | WinExe
13 | net5.0-windows
14 | MapPacker
15 | true
16 | true
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/MapPacker/MapPacker.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Designer
7 |
8 |
9 |
10 |
11 | Designer
12 |
13 |
14 |
--------------------------------------------------------------------------------
/MapPacker/MapPacker.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31321.278
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MapPacker", "MapPacker.csproj", "{5AE564CE-3830-4A08-89AC-468D98598048}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7EAB299B-2FE0-4C5C-8EBC-DCBD23AE8EB6}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | EndProjectSection
12 | EndProject
13 | Global
14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
15 | Debug|Any CPU = Debug|Any CPU
16 | Release|Any CPU = Release|Any CPU
17 | EndGlobalSection
18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
19 | {5AE564CE-3830-4A08-89AC-468D98598048}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
20 | {5AE564CE-3830-4A08-89AC-468D98598048}.Debug|Any CPU.Build.0 = Debug|Any CPU
21 | {5AE564CE-3830-4A08-89AC-468D98598048}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {5AE564CE-3830-4A08-89AC-468D98598048}.Release|Any CPU.Build.0 = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(SolutionProperties) = preSolution
25 | HideSolutionNode = FALSE
26 | EndGlobalSection
27 | GlobalSection(ExtensibilityGlobals) = postSolution
28 | SolutionGuid = {77ADEEDC-9095-4641-A84D-C52E1711E67C}
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/MapPacker/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eagle-One-Development/Asset-Packer/164dd5b82e9352e71d8b9cc5e48437ba13469d11/MapPacker/favicon.ico
--------------------------------------------------------------------------------
/MapPacker/icons/icon_eagleone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eagle-One-Development/Asset-Packer/164dd5b82e9352e71d8b9cc5e48437ba13469d11/MapPacker/icons/icon_eagleone.png
--------------------------------------------------------------------------------
/MapPacker/icons/icon_file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eagle-One-Development/Asset-Packer/164dd5b82e9352e71d8b9cc5e48437ba13469d11/MapPacker/icons/icon_file.png
--------------------------------------------------------------------------------
/MapPacker/icons/icon_folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eagle-One-Development/Asset-Packer/164dd5b82e9352e71d8b9cc5e48437ba13469d11/MapPacker/icons/icon_folder.png
--------------------------------------------------------------------------------
/MapPacker/icons/mappacker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eagle-One-Development/Asset-Packer/164dd5b82e9352e71d8b9cc5e48437ba13469d11/MapPacker/icons/mappacker.png
--------------------------------------------------------------------------------
/MapPacker/icons/resizeIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eagle-One-Development/Asset-Packer/164dd5b82e9352e71d8b9cc5e48437ba13469d11/MapPacker/icons/resizeIcon.png
--------------------------------------------------------------------------------
/MapPacker/sounds/steam-message.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Eagle-One-Development/Asset-Packer/164dd5b82e9352e71d8b9cc5e48437ba13469d11/MapPacker/sounds/steam-message.wav
--------------------------------------------------------------------------------
/MapPacker/style.txt:
--------------------------------------------------------------------------------
1 |
2 | // base colors
3 | "White" "255 255 255 255"
4 | "OffWhite" "221 221 221 255"
5 | "DullWhite" "211 211 211 255"
6 |
7 | // base colors
8 | "BaseText" "216 222 211 255" // used in text windows, lists
9 | "BrightBaseText" "255 255 255 255" // brightest text
10 | "DimBaseText" "160 170 149 255" // dim base text
11 | "LabelDimText" "160 170 149 255" // used for info text
12 | "ControlText" "216 222 211 255" // used in all text controls
13 | "BrightControlText" "196 181 80 255" // use for selected controls
14 | "DisabledText1" "117 128 111 255" // disabled text
15 | "DisabledText2" "40 46 34 255" // overlay color for disabled text (to give that inset look)
16 | "DimListText" "117 134 102 255" // offline friends, unsubscribed games, etc.
17 |
18 | // background colors
19 | "ControlBG" "76 88 68 255" // background color of controls
20 | "ControlDarkBG" "90 106 80 255" // darker background color; used for background of scrollbars
21 | "WindowBG" "62 70 55 255" // background color of text edit panes (chat, text entries, etc.)
22 | "SelectionBG" "149 136 49 255" // background color of any selected text or menu item
23 | "SelectionBG2" "40 46 34 255" // selection background in window w/o focus
24 | "ListBG" "62 70 55 255" // background of server browser, buddy list, etc.
25 |
26 | // titlebar colors
27 | "TitleText" "255 255 255 255"
28 | "TitleDimText" "136 145 128 255"
29 | "TitleBG" "76 88 68 0"
30 | "TitleDimBG" "76 88 68 0"
31 |
32 | // slider tick colors
33 | "SliderTickColor" "127 140 127 255"
34 | "SliderTrackColor" "31 31 31 255"
35 |
36 | // border colors
37 | "BorderBright" "136 145 128 255" // the lit side of a control
38 | "BorderDark" "40 46 34 255" // the dark/unlit side of a control
39 | "BorderSelection" "0 0 0 255" // the additional border color for displaying the default/selected button
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## THIS PROGRAM IS NOW OBSOLETE
2 |
3 | Sbox has its own proper map/addon uploader now that should be used instead of this. This Repository will remain archived.
4 |
5 | # Asset-Packer
6 |
7 | A program that packs or isolates compiled assets used in a Map.
8 |
9 | # Current Features:
10 |
11 | * Packing using the vmap and compiled vpk
12 | * 3D Skybox and prefab asset detection
13 | * Materials, textures, particles, post processing and Models are detected and packed
14 | * Proper support for legacy imported Models
15 | * Crashes s&box if it's running during packing :)
16 |
17 | # How to Use:
18 |
19 | 1. Compile your map.
20 | 2. Make sure your vmap and compiled vpk are in the same directory.
21 | 3. Select your vmap, asset directory and sbox directory in the boxes above.
22 | 4. Click pack. The found and packed assets will be listed below.
23 |
24 | In case you don't want to have your content packed, just check the box and it will instead appear in a folder called yourMap_content.
25 |
26 | For any additional help, contact `DoctorGurke#0007` or `Josh Wilson#9332` on discord or make an issue on the Github.
27 |
--------------------------------------------------------------------------------