From a073e571785532d75e9082684b27d025f1d16a42 Mon Sep 17 00:00:00 2001 From: Georgii Korogodov Date: Sat, 4 Jun 2022 19:21:57 +0400 Subject: [PATCH] Added project --- .gitmodules | 3 + NbtScrapper/NbtScrapper.sln | 112 ++++++++ NbtScrapper/NbtScrapper/ControlExtension.cs | 22 ++ NbtScrapper/NbtScrapper/MainForm.Designer.cs | 158 +++++++++++ NbtScrapper/NbtScrapper/MainForm.cs | 282 +++++++++++++++++++ NbtScrapper/NbtScrapper/MainForm.resx | 63 +++++ NbtScrapper/NbtScrapper/NbtScrapper.csproj | 15 + NbtScrapper/NbtScrapper/Program.cs | 16 ++ README.md | 10 +- nbt-studio | 1 + screenshot.png | Bin 0 -> 29000 bytes 11 files changed, 681 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 100644 NbtScrapper/NbtScrapper.sln create mode 100644 NbtScrapper/NbtScrapper/ControlExtension.cs create mode 100644 NbtScrapper/NbtScrapper/MainForm.Designer.cs create mode 100644 NbtScrapper/NbtScrapper/MainForm.cs create mode 100644 NbtScrapper/NbtScrapper/MainForm.resx create mode 100644 NbtScrapper/NbtScrapper/NbtScrapper.csproj create mode 100644 NbtScrapper/NbtScrapper/Program.cs create mode 160000 nbt-studio create mode 100644 screenshot.png diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4b6aa52 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "nbt-studio"] + path = nbt-studio + url = https://github.com/tryashtar/nbt-studio diff --git a/NbtScrapper/NbtScrapper.sln b/NbtScrapper/NbtScrapper.sln new file mode 100644 index 0000000..84fb4b3 --- /dev/null +++ b/NbtScrapper/NbtScrapper.sln @@ -0,0 +1,112 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NbtScrapper", "NbtScrapper\NbtScrapper.csproj", "{526B879F-318A-4897-8FF3-1723E7B86FB7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NbtStudio", "..\nbt-studio\NbtStudio\NbtStudio.csproj", "{1A816035-1E3C-4A7E-B3ED-084B4C3F758F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fNbt", "..\nbt-studio\fNbt\fNbt\fNbt.csproj", "{83FEE996-5211-4EA7-B463-FA67BC0909B2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Submodules", "Submodules", "{E388A29D-7419-4E6F-855F-FB0F986F5B8E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Be.Windows.Forms.HexBox", "..\nbt-studio\HexBox\Be.Windows.Forms.HexBox.csproj", "{3C8A5458-7E45-4B60-AED9-B340F82DC0C5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aga.Controls", "..\nbt-studio\TreeViewAdv\Aga.Controls.csproj", "{F798ECF6-F8E5-4656-AD63-1BC66E606779}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TryashtarUtils.Forms", "..\nbt-studio\utils.forms\TryashtarUtils.Forms.csproj", "{05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TryashtarUtils.Nbt", "..\nbt-studio\utils.nbt\TryashtarUtils.Nbt.csproj", "{AD53023C-AED3-412D-9B0D-16694C904942}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TryashtarUtils.Utility", "..\nbt-studio\utils.utility\TryashtarUtils.Utility.csproj", "{35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + PublishNormal|Any CPU = PublishNormal|Any CPU + PublishSuperStandalone|Any CPU = PublishSuperStandalone|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {526B879F-318A-4897-8FF3-1723E7B86FB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {526B879F-318A-4897-8FF3-1723E7B86FB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {526B879F-318A-4897-8FF3-1723E7B86FB7}.PublishNormal|Any CPU.ActiveCfg = Debug|Any CPU + {526B879F-318A-4897-8FF3-1723E7B86FB7}.PublishNormal|Any CPU.Build.0 = Debug|Any CPU + {526B879F-318A-4897-8FF3-1723E7B86FB7}.PublishSuperStandalone|Any CPU.ActiveCfg = Debug|Any CPU + {526B879F-318A-4897-8FF3-1723E7B86FB7}.PublishSuperStandalone|Any CPU.Build.0 = Debug|Any CPU + {526B879F-318A-4897-8FF3-1723E7B86FB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {526B879F-318A-4897-8FF3-1723E7B86FB7}.Release|Any CPU.Build.0 = Release|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.PublishNormal|Any CPU.ActiveCfg = PublishNormal|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.PublishNormal|Any CPU.Build.0 = PublishNormal|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.PublishSuperStandalone|Any CPU.ActiveCfg = PublishSuperStandalone|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.PublishSuperStandalone|Any CPU.Build.0 = PublishSuperStandalone|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F}.Release|Any CPU.Build.0 = Release|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.PublishNormal|Any CPU.ActiveCfg = Debug|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.PublishNormal|Any CPU.Build.0 = Debug|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.PublishSuperStandalone|Any CPU.ActiveCfg = Debug|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.PublishSuperStandalone|Any CPU.Build.0 = Debug|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83FEE996-5211-4EA7-B463-FA67BC0909B2}.Release|Any CPU.Build.0 = Release|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.PublishNormal|Any CPU.ActiveCfg = Debug|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.PublishNormal|Any CPU.Build.0 = Debug|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.PublishSuperStandalone|Any CPU.ActiveCfg = Debug|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.PublishSuperStandalone|Any CPU.Build.0 = Debug|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5}.Release|Any CPU.Build.0 = Release|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.PublishNormal|Any CPU.ActiveCfg = Debug|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.PublishNormal|Any CPU.Build.0 = Debug|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.PublishSuperStandalone|Any CPU.ActiveCfg = Debug|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.PublishSuperStandalone|Any CPU.Build.0 = Debug|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F798ECF6-F8E5-4656-AD63-1BC66E606779}.Release|Any CPU.Build.0 = Release|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.PublishNormal|Any CPU.ActiveCfg = Debug|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.PublishNormal|Any CPU.Build.0 = Debug|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.PublishSuperStandalone|Any CPU.ActiveCfg = Debug|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.PublishSuperStandalone|Any CPU.Build.0 = Debug|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2}.Release|Any CPU.Build.0 = Release|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.PublishNormal|Any CPU.ActiveCfg = Debug|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.PublishNormal|Any CPU.Build.0 = Debug|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.PublishSuperStandalone|Any CPU.ActiveCfg = Debug|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.PublishSuperStandalone|Any CPU.Build.0 = Debug|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD53023C-AED3-412D-9B0D-16694C904942}.Release|Any CPU.Build.0 = Release|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.PublishNormal|Any CPU.ActiveCfg = Debug|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.PublishNormal|Any CPU.Build.0 = Debug|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.PublishSuperStandalone|Any CPU.ActiveCfg = Debug|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.PublishSuperStandalone|Any CPU.Build.0 = Debug|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1A816035-1E3C-4A7E-B3ED-084B4C3F758F} = {E388A29D-7419-4E6F-855F-FB0F986F5B8E} + {83FEE996-5211-4EA7-B463-FA67BC0909B2} = {E388A29D-7419-4E6F-855F-FB0F986F5B8E} + {3C8A5458-7E45-4B60-AED9-B340F82DC0C5} = {E388A29D-7419-4E6F-855F-FB0F986F5B8E} + {F798ECF6-F8E5-4656-AD63-1BC66E606779} = {E388A29D-7419-4E6F-855F-FB0F986F5B8E} + {05B64EE5-FB9F-4B23-BB35-C6F4223CAFC2} = {E388A29D-7419-4E6F-855F-FB0F986F5B8E} + {AD53023C-AED3-412D-9B0D-16694C904942} = {E388A29D-7419-4E6F-855F-FB0F986F5B8E} + {35EE0EB2-C1AC-449C-8B5A-8B00A75FB24D} = {E388A29D-7419-4E6F-855F-FB0F986F5B8E} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5D00C4A3-454C-47BB-AC9A-138C19172F10} + EndGlobalSection +EndGlobal diff --git a/NbtScrapper/NbtScrapper/ControlExtension.cs b/NbtScrapper/NbtScrapper/ControlExtension.cs new file mode 100644 index 0000000..aff8401 --- /dev/null +++ b/NbtScrapper/NbtScrapper/ControlExtension.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NbtScrapper; + +public static class ControlExtension +{ + public static void InvokeIfRequired(this Control control, MethodInvoker action) + { + if (control.InvokeRequired) + { + control.Invoke(action); + } + else + { + action(); + } + } +} \ No newline at end of file diff --git a/NbtScrapper/NbtScrapper/MainForm.Designer.cs b/NbtScrapper/NbtScrapper/MainForm.Designer.cs new file mode 100644 index 0000000..1163b8d --- /dev/null +++ b/NbtScrapper/NbtScrapper/MainForm.Designer.cs @@ -0,0 +1,158 @@ +namespace NbtScrapper +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.inputPathTextBox = new System.Windows.Forms.TextBox(); + this.inputOpenButton = new System.Windows.Forms.Button(); + this.extractButton = new System.Windows.Forms.Button(); + this.progressBar = new System.Windows.Forms.ProgressBar(); + this.outputPathTextBox = new System.Windows.Forms.TextBox(); + this.outputOpenButton = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.infoTextBox = new System.Windows.Forms.TextBox(); + this.backgroundWorker = new System.ComponentModel.BackgroundWorker(); + this.SuspendLayout(); + // + // inputPathTextBox + // + this.inputPathTextBox.Location = new System.Drawing.Point(95, 37); + this.inputPathTextBox.Name = "inputPathTextBox"; + this.inputPathTextBox.Size = new System.Drawing.Size(368, 27); + this.inputPathTextBox.TabIndex = 0; + // + // inputOpenButton + // + this.inputOpenButton.Location = new System.Drawing.Point(478, 37); + this.inputOpenButton.Name = "inputOpenButton"; + this.inputOpenButton.Size = new System.Drawing.Size(94, 29); + this.inputOpenButton.TabIndex = 1; + this.inputOpenButton.Text = "Open..."; + this.inputOpenButton.UseVisualStyleBackColor = true; + this.inputOpenButton.Click += new System.EventHandler(this.inputOpenButton_Click); + // + // extractButton + // + this.extractButton.Location = new System.Drawing.Point(361, 220); + this.extractButton.Name = "extractButton"; + this.extractButton.Size = new System.Drawing.Size(113, 38); + this.extractButton.TabIndex = 2; + this.extractButton.Text = "Extract"; + this.extractButton.UseVisualStyleBackColor = true; + this.extractButton.Click += new System.EventHandler(this.extractButton_Click); + // + // progressBar + // + this.progressBar.Location = new System.Drawing.Point(95, 145); + this.progressBar.Name = "progressBar"; + this.progressBar.Size = new System.Drawing.Size(477, 32); + this.progressBar.TabIndex = 3; + // + // outputPathTextBox + // + this.outputPathTextBox.Location = new System.Drawing.Point(95, 92); + this.outputPathTextBox.Name = "outputPathTextBox"; + this.outputPathTextBox.Size = new System.Drawing.Size(368, 27); + this.outputPathTextBox.TabIndex = 4; + // + // outputOpenButton + // + this.outputOpenButton.Location = new System.Drawing.Point(478, 90); + this.outputOpenButton.Name = "outputOpenButton"; + this.outputOpenButton.Size = new System.Drawing.Size(94, 29); + this.outputOpenButton.TabIndex = 5; + this.outputOpenButton.Text = "Open..."; + this.outputOpenButton.UseVisualStyleBackColor = true; + this.outputOpenButton.Click += new System.EventHandler(this.outputOpenButton_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(34, 40); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(39, 20); + this.label1.TabIndex = 6; + this.label1.Text = "Map"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(34, 94); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(55, 20); + this.label2.TabIndex = 7; + this.label2.Text = "Output"; + // + // infoTextBox + // + this.infoTextBox.Location = new System.Drawing.Point(600, 37); + this.infoTextBox.Multiline = true; + this.infoTextBox.Name = "infoTextBox"; + this.infoTextBox.ReadOnly = true; + this.infoTextBox.Size = new System.Drawing.Size(185, 140); + this.infoTextBox.TabIndex = 8; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(809, 270); + this.Controls.Add(this.infoTextBox); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.outputOpenButton); + this.Controls.Add(this.outputPathTextBox); + this.Controls.Add(this.progressBar); + this.Controls.Add(this.extractButton); + this.Controls.Add(this.inputOpenButton); + this.Controls.Add(this.inputPathTextBox); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "MainForm"; + this.ShowIcon = false; + this.Text = "NBT Scrapper by Megageorgio"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private TextBox inputPathTextBox; + private Button inputOpenButton; + private Button extractButton; + private ProgressBar progressBar; + private TextBox outputPathTextBox; + private Button outputOpenButton; + private Label label1; + private Label label2; + private TextBox infoTextBox; + private System.ComponentModel.BackgroundWorker backgroundWorker; + } +} \ No newline at end of file diff --git a/NbtScrapper/NbtScrapper/MainForm.cs b/NbtScrapper/NbtScrapper/MainForm.cs new file mode 100644 index 0000000..5787c63 --- /dev/null +++ b/NbtScrapper/NbtScrapper/MainForm.cs @@ -0,0 +1,282 @@ +using System.Collections.Concurrent; +using System.Security.Cryptography; +using System.Text; +using fNbt; +using NbtStudio; +using TryashtarUtils.Nbt; + +namespace NbtScrapper; + +public partial class MainForm : Form +{ + public MainForm() + { + InitializeComponent(); + } + + private void inputOpenButton_Click(object sender, EventArgs e) + { + var folderBrowserDialog = new FolderBrowserDialog(); + folderBrowserDialog.InitialDirectory = inputPathTextBox.Text; + var result = folderBrowserDialog.ShowDialog(); + if (result == DialogResult.OK) + { + inputPathTextBox.Text = folderBrowserDialog.SelectedPath; + } + } + + private void outputOpenButton_Click(object sender, EventArgs e) + { + var folderBrowserDialog = new FolderBrowserDialog(); + folderBrowserDialog.InitialDirectory = outputPathTextBox.Text; + var result = folderBrowserDialog.ShowDialog(); + if (result == DialogResult.OK) + { + outputPathTextBox.Text = folderBrowserDialog.SelectedPath; + } + } + + private void extractButton_Click(object sender, EventArgs e) + { + extractButton.Enabled = false; + var regionFiles = new List(); + var playerFiles = new List(); + var regionPath = Path.Combine(inputPathTextBox.Text, "region"); + if (Directory.Exists(regionPath)) + { + regionFiles.AddRange(Directory.GetFiles(regionPath)); + } + + var entitiesPath = Path.Combine(inputPathTextBox.Text, "entities"); + if (Directory.Exists(entitiesPath)) + { + regionFiles.AddRange(Directory.GetFiles(entitiesPath)); + } + + var playerDataPath = Path.Combine(inputPathTextBox.Text, "playerdata"); + if (Directory.Exists(playerDataPath)) + { + playerFiles.AddRange(Directory.GetFiles(playerDataPath)); + } + + Task.Run(() => FindBooksAndSigns(regionFiles, playerFiles)); + } + + private void FindBooksAndSigns(ICollection regionFiles, ICollection playerFiles) + { + var signFile = File.Create(Path.Combine(outputPathTextBox.Text, "sign.txt")); + var bookFile = File.Create(Path.Combine(outputPathTextBox.Text, "book.txt")); + var signFileWriter = new StreamWriter(signFile); + var bookFileWriter = new StreamWriter(bookFile); + var hashes = new List(); + var signCount = 0; + var bookCount = 0; + progressBar.InvokeIfRequired(() => + { + progressBar.Maximum = playerFiles.Count; + progressBar.Value = 0; + }); + + foreach (var file in playerFiles) + { + progressBar.InvokeIfRequired(() => { progressBar.Value++; }); + try + { + var playerData = new fNbt.NbtFile(file); + infoTextBox.InvokeIfRequired(() => + { + infoTextBox.Text = + $@"Players: {progressBar.Value}/{playerFiles.Count}\nSigns found: {signCount}\nBooks found: {bookCount}"; + }); + CyclicSearch(playerData.RootTag as NbtContainerTag, SearchMethod); + GC.Collect(); + } + catch + { + continue; + } + } + + progressBar.InvokeIfRequired(() => + { + progressBar.Maximum = regionFiles.Count; + progressBar.Value = 0; + }); + foreach (var file in regionFiles) + { + progressBar.InvokeIfRequired(() => { progressBar.Value++; }); + try + { + var region = new RegionFile(file); + var j = 0; + var count = region.AllChunks.Count(); + foreach (var chunk in region.AllChunks) + { + j++; + try + { + infoTextBox.InvokeIfRequired(() => + { + infoTextBox.Text = + $@"Regions: {progressBar.Value}/{regionFiles.Count}{Environment.NewLine}Current: x={region.Coords.X}, z={region.Coords.Z}{Environment.NewLine}Chunks: {j}/{count}{Environment.NewLine}Signs found: {signCount}{Environment.NewLine}Books found: {bookCount}"; + }); + + if (chunk == null) + { + continue; + } + + chunk.Load(); + CyclicSearch(chunk.Data, SearchMethod); + chunk.Remove(); + GC.Collect(); + } + catch + { + continue; + } + } + + GC.Collect(); + } + catch + { + continue; + } + } + + signFileWriter.Close(); + bookFileWriter.Close(); + extractButton.InvokeIfRequired(() => { extractButton.Enabled = true; }); + + void SearchMethod(NbtContainerTag container, NbtTag tag) + { + if (tag.Name == "id") + { + if (tag.StringValue is "minecraft:written_book" or "minecraft:writable_book") + { + var book = container["tag"] as NbtCompound; + if (book == null) + { + return; + } + + var pages = book["pages"] as NbtList; + + if (pages != null && pages.Count != 0) + { + var text = ""; + foreach (var page in pages) + { + var isSnbt = SnbtParser.ClassicTryParse(page.StringValue, false, out var result); + text += isSnbt ? result["text"].StringValue : page.StringValue; + } + + var bookHash = HashString(text); + if (hashes.Any(hash => Enumerable.SequenceEqual(hash, bookHash))) + { + return; + } + + hashes.Add(bookHash); + + bookFileWriter.WriteLine("====================================="); + bookFileWriter.WriteLine( + $@"Book ""{(book["title"] == null ? "No name" : book["title"].StringValue)}"" by {(book["author"] == null ? "Unknown" : book["author"].StringValue)}"); + bookFileWriter.WriteLine(); + var i = 0; + foreach (var page in pages) + { + bookFileWriter.WriteLine($"--- Page {i} ---"); + var isSnbt = SnbtParser.ClassicTryParse(page.StringValue, false, out var result); + bookFileWriter.WriteLine(isSnbt ? result["text"].StringValue : page.StringValue); + i++; + } + } + + bookCount++; + return; + } + + if (tag.StringValue is "minecraft:sign") + { + var list = new[] { "Text1", "Text2", "Text3", "Text4" }; + var rows = new List(); + foreach (var item in list) + { + var row = SnbtParser.Parse(container[item].StringValue, false); + var extra = row["extra"]; + if (extra != null) + { + rows.Add(extra[0]["text"].StringValue); + } + else + { + rows.Add(row["text"].StringValue); + } + } + + if (rows.All(string.IsNullOrEmpty)) + { + return; + } + + signFileWriter.WriteLine("====================================="); + signFileWriter.WriteLine( + $"Sign at x:{container["x"].IntValue}, y:{container["y"].IntValue}, z:{container["z"].IntValue}:"); + foreach (var row in rows) + { + signFileWriter.WriteLine(row); + } + + signCount++; + } + } + } + } + + private void RecursiveSearch(NbtContainerTag container, Action action) + { + foreach (var tag in container) + { + action(container, tag); + if (tag is NbtContainerTag cTag) + { + if (cTag.Count > 0) + { + RecursiveSearch(cTag, action); + } + } + } + } + + private void CyclicSearch(NbtContainerTag rootTag, Action action) + { + var stack = new ConcurrentStack(); + stack.Push(rootTag); + while (stack.TryPop(out var container)) + { + foreach (var tag in container) + { + action(container, tag); + if (tag is NbtContainerTag cTag) + { + if (cTag.Count > 0) + { + stack.Push(cTag); + } + } + } + + } + } + + byte[] HashString(string text, string salt = "") + { + using var sha = SHA256.Create(); + var textBytes = Encoding.UTF8.GetBytes(text + salt); + var hashBytes = sha.ComputeHash(textBytes); + + return hashBytes; + } +} \ No newline at end of file diff --git a/NbtScrapper/NbtScrapper/MainForm.resx b/NbtScrapper/NbtScrapper/MainForm.resx new file mode 100644 index 0000000..e8e686e --- /dev/null +++ b/NbtScrapper/NbtScrapper/MainForm.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/NbtScrapper/NbtScrapper/NbtScrapper.csproj b/NbtScrapper/NbtScrapper/NbtScrapper.csproj new file mode 100644 index 0000000..fdebf98 --- /dev/null +++ b/NbtScrapper/NbtScrapper/NbtScrapper.csproj @@ -0,0 +1,15 @@ + + + + WinExe + net6.0-windows + enable + true + enable + + + + + + + \ No newline at end of file diff --git a/NbtScrapper/NbtScrapper/Program.cs b/NbtScrapper/NbtScrapper/Program.cs new file mode 100644 index 0000000..7721d68 --- /dev/null +++ b/NbtScrapper/NbtScrapper/Program.cs @@ -0,0 +1,16 @@ +namespace NbtScrapper; + +internal static class Program +{ + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new MainForm()); + } +} \ No newline at end of file diff --git a/README.md b/README.md index c26609d..f4d8dec 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ # nbt-scrapper -Extract books and signs from minecraft map +Extract books and signs from minecraft map, supports 1.18.2 + +![Screenshot](./screenshot.png) + +- Requires: [.NET 6 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0 ".NET 6 Runtime"), Windows 7+ +- Based on: [nbt-studio](https://github.com/tryashtar/nbt-studio "nbt-studio") +- Supports: [mc-book-publisher](https://github.com/jarekt/mc-book-publisher "mc-book-publisher") + +Code quality is pretty bad but at least it works... \ No newline at end of file diff --git a/nbt-studio b/nbt-studio new file mode 160000 index 0000000..8c4516a --- /dev/null +++ b/nbt-studio @@ -0,0 +1 @@ +Subproject commit 8c4516ad2f1651d4ea1f2265c07b2ae8f24b0852 diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c18bae4eb6905ba0c9ea57817d05538303e95951 GIT binary patch literal 29000 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 literal 0 HcmV?d00001