Skip to content

Commit cc21d4f

Browse files
committed
[CLI] Add support of Extract mode
1 parent e3e3433 commit cc21d4f

File tree

3 files changed

+125
-9
lines changed

3 files changed

+125
-9
lines changed

AssetStudioCLI/Options/CLIOptions.cs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ internal enum HelpGroups
2020

2121
internal enum WorkMode
2222
{
23+
Extract,
2324
Export,
2425
ExportRaw,
2526
Dump,
@@ -136,7 +137,7 @@ private static void OptionGrouping(string name, string desc, string example, Hel
136137
}
137138

138139
var optionDesc = desc + example.Color(ColorConsole.BrightBlack);
139-
var optionDict = new Dictionary<string, string>() { { name, optionDesc } };
140+
var optionDict = new Dictionary<string, string> { { name, optionDesc } };
140141
if (optionGroups.TryGetValue(helpGroup, out Dictionary<string, string> groupDict))
141142
{
142143
groupDict.Add(name, optionDesc);
@@ -188,7 +189,8 @@ private static void InitOptions()
188189
optionDefaultValue: WorkMode.Export,
189190
optionName: "-m, --mode <value>",
190191
optionDescription: "Specify working mode\n" +
191-
"<Value: export(default) | exportRaw | dump | info | live2d | splitObjects>\n" +
192+
"<Value: extract | export(default) | exportRaw | dump | info | live2d | splitObjects>\n" +
193+
"Extract - Extracts(Decompresses) asset bundles\n" +
192194
"Export - Exports converted assets\n" +
193195
"ExportRaw - Exports raw data\n" +
194196
"Dump - Makes asset dumps\n" +
@@ -551,6 +553,9 @@ public static void ParseArgs(string[] args)
551553
var value = resplittedArgs[workModeOptionIndex + 1];
552554
switch (value.ToLower())
553555
{
556+
case "extract":
557+
o_workMode.Value = WorkMode.Extract;
558+
break;
554559
case "export":
555560
o_workMode.Value = WorkMode.Export;
556561
break;
@@ -649,7 +654,7 @@ public static void ParseArgs(string[] args)
649654
#endregion
650655

651656
#region Parse Options
652-
for (int i = 0; i < resplittedArgs.Count; i++)
657+
for (var i = 0; i < resplittedArgs.Count; i++)
653658
{
654659
var option = resplittedArgs[i].ToLower();
655660
try
@@ -1084,7 +1089,10 @@ public static void ParseArgs(string[] args)
10841089
}
10851090
if (o_outputFolder.Value == o_outputFolder.DefaultValue)
10861091
{
1087-
var fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, o_outputFolder.DefaultValue + Path.DirectorySeparatorChar);
1092+
var defaultFolder = o_workMode.Value == WorkMode.Extract
1093+
? "ASExtract"
1094+
: o_outputFolder.DefaultValue;
1095+
var fullPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, defaultFolder + Path.DirectorySeparatorChar);
10881096
if (!Directory.Exists(fullPath))
10891097
{
10901098
Directory.CreateDirectory(fullPath);
@@ -1205,14 +1213,20 @@ public static void ShowCurrentOptions()
12051213
{
12061214
sb.AppendLine($"# Custom Compression Type: {o_customCompressionType}");
12071215
}
1208-
sb.AppendLine($"# Parse Assets Using TypeTree: {!f_avoidLoadingViaTypetree.Value}");
1216+
if (o_workMode.Value != WorkMode.Extract)
1217+
{
1218+
sb.AppendLine($"# Parse Assets Using TypeTree: {!f_avoidLoadingViaTypetree.Value}");
1219+
}
12091220
sb.AppendLine($"# Input Path: \"{inputPath}\"");
1221+
if (o_workMode.Value != WorkMode.Info)
1222+
{
1223+
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
1224+
}
12101225
switch (o_workMode.Value)
12111226
{
12121227
case WorkMode.Export:
12131228
case WorkMode.ExportRaw:
12141229
case WorkMode.Dump:
1215-
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
12161230
if (o_workMode.Value != WorkMode.Export)
12171231
{
12181232
sb.AppendLine($"# Load All Assets: {f_loadAllAssets}");
@@ -1248,7 +1262,6 @@ public static void ShowCurrentOptions()
12481262
break;
12491263
case WorkMode.Live2D:
12501264
case WorkMode.SplitObjects:
1251-
sb.AppendLine($"# Output Path: \"{o_outputFolder}\"");
12521265
sb.AppendLine($"# Log Level: {o_logLevel}");
12531266
sb.AppendLine($"# Log Output: {o_logOutput}");
12541267
sb.AppendLine($"# Export Asset List: {o_exportAssetList}");
@@ -1260,7 +1273,7 @@ public static void ShowCurrentOptions()
12601273
else
12611274
{
12621275
sb.AppendLine($"# Model Group Option: {o_l2dGroupOption}");
1263-
sb.AppendFormat("# Search model-related assets by: {0}\n", f_l2dAssetSearchByFilename.Value ? "FileName" : "Container");
1276+
sb.AppendFormat("# Search Model-related Assets by: {0}\n", f_l2dAssetSearchByFilename.Value ? "FileName" : "Container");
12641277
sb.AppendLine($"# Motion Export Method: {o_l2dMotionMode}");
12651278
sb.AppendLine($"# Force Bezier: {f_l2dForceBezier }");
12661279
sb.AppendLine($"# Assembly Path: \"{o_assemblyPath}\"");

AssetStudioCLI/Program.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ private static void CLIRun()
3232

3333
try
3434
{
35-
if (Studio.LoadAssets())
35+
if (CLIOptions.o_workMode.Value == WorkMode.Extract)
36+
{
37+
Studio.ExtractBundles();
38+
}
39+
else if (Studio.LoadAssets())
3640
{
3741
Studio.ParseAssets();
3842
if (CLIOptions.filterBy != FilterBy.None)

AssetStudioCLI/Studio.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,105 @@ private static void ShowCurProgressValue(int value)
3535
Console.Write($"[{value:000}%]\r");
3636
}
3737

38+
public static void ExtractBundles()
39+
{
40+
var extractedCount = 0;
41+
var path = CLIOptions.inputPath;
42+
var savePath = CLIOptions.o_outputFolder.Value;
43+
Progress.Reset();
44+
if (Directory.Exists(path))
45+
{
46+
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
47+
var totalCount = files.Length;
48+
for (var i = 0; i < totalCount; i++)
49+
{
50+
var file = files[i];
51+
var fileOriPath = Path.GetDirectoryName(file);
52+
var fileSavePath = fileOriPath.Replace(path, savePath);
53+
extractedCount += ExtractFile(file, fileSavePath);
54+
Progress.Report(i + 1, totalCount);
55+
}
56+
}
57+
else if (File.Exists(path))
58+
{
59+
extractedCount += ExtractFile(path, savePath);
60+
}
61+
62+
var status = extractedCount > 0
63+
? $"Finished extracting {extractedCount} file(s) to \"{savePath.Color(Ansi.BrightCyan)}\""
64+
: "Nothing extracted (not extractable or file(s) already exist)";
65+
Logger.Default.Log(LoggerEvent.Info, status, ignoreLevel: true);
66+
}
67+
68+
public static int ExtractFile(string fileName, string savePath)
69+
{
70+
var extractedCount = 0;
71+
var reader = new FileReader(fileName);
72+
switch (reader.FileType)
73+
{
74+
case FileType.BundleFile:
75+
extractedCount += ExtractBundleFile(reader, savePath);
76+
break;
77+
case FileType.WebFile:
78+
extractedCount += ExtractWebDataFile(reader, savePath);
79+
break;
80+
default:
81+
reader.Dispose();
82+
break;
83+
}
84+
return extractedCount;
85+
}
86+
87+
private static int ExtractBundleFile(FileReader reader, string savePath)
88+
{
89+
Logger.Info($"Decompressing {reader.FileName} ...");
90+
var bundleFile = new BundleFile(reader, assetsManager.ZstdEnabled, assetsManager.SpecifyUnityVersion);
91+
reader.Dispose();
92+
if (bundleFile.fileList.Length > 0)
93+
{
94+
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
95+
return ExtractStreamFile(extractPath, bundleFile.fileList);
96+
}
97+
return 0;
98+
}
99+
100+
private static int ExtractWebDataFile(FileReader reader, string savePath)
101+
{
102+
Logger.Info($"Decompressing {reader.FileName} ...");
103+
var webFile = new WebFile(reader);
104+
reader.Dispose();
105+
if (webFile.fileList.Length > 0)
106+
{
107+
var extractPath = Path.Combine(savePath, reader.FileName + "_unpacked");
108+
return ExtractStreamFile(extractPath, webFile.fileList);
109+
}
110+
return 0;
111+
}
112+
113+
private static int ExtractStreamFile(string extractPath, StreamFile[] fileList)
114+
{
115+
var extractedCount = 0;
116+
foreach (var file in fileList)
117+
{
118+
var filePath = Path.Combine(extractPath, file.path);
119+
var fileDirectory = Path.GetDirectoryName(filePath);
120+
if (!Directory.Exists(fileDirectory))
121+
{
122+
Directory.CreateDirectory(fileDirectory);
123+
}
124+
if (!File.Exists(filePath))
125+
{
126+
using (var fileStream = File.Create(filePath))
127+
{
128+
file.stream.CopyTo(fileStream);
129+
}
130+
extractedCount += 1;
131+
}
132+
file.stream.Dispose();
133+
}
134+
return extractedCount;
135+
}
136+
38137
public static bool LoadAssets()
39138
{
40139
var isLoaded = false;

0 commit comments

Comments
 (0)