Init commit

This commit is contained in:
Ling 2025-06-22 11:33:58 +08:00
commit 18dcce592d
15 changed files with 5491 additions and 0 deletions

71
.gitignore vendored Normal file
View File

@ -0,0 +1,71 @@
# 用户特定文件
*.suo
*.user
*.userosscache
*.sln.docstates
# 构建结果
[Dd]ebug/
[Rr]elease/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 缓存/选项目录
.vs/
# MSTest 测试结果
[Tt]est[Rr]esult*/
# .NET Core 项目
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
# NuGet 包
*.nupkg
**/packages/*
!**/packages/build/
# Microsoft Azure 构建输出
csx/
*.build.csdef
# Windows Store 应用包目录和文件
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio 缓存文件
*. [Cc]ache
!?*. [Cc]ache/
# 其他
ClientBin/
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# JetBrains Rider
idea/
*.sln.iml
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Visual Studio Code
vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

16
App.xaml Normal file
View File

@ -0,0 +1,16 @@
<Application x:Class="MCI18n.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MCI18n"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- 引入自定义按钮样式 -->
<ResourceDictionary Source="Theme/MCButtonStyle.xaml"/>
<ResourceDictionary Source="Theme/MCTabTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
<local:NullToVisibilityConverter x:Key="NullToVisibilityConverter"/>
</ResourceDictionary>
</Application.Resources>
</Application>

14
App.xaml.cs Normal file
View File

@ -0,0 +1,14 @@
using System.Configuration;
using System.Data;
using System.Windows;
namespace MCI18n
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

10
AssemblyInfo.cs Normal file
View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

19
Converters.cs Normal file
View File

@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MCI18n;
public class NullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

40
MCI18n.csproj Normal file
View File

@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UseWPF>true</UseWPF>
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>full</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
</ItemGroup>
<ItemGroup>
<None Update="einformation_7new_mechanics.snbt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharpNBT\SharpNBT\SharpNBT.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
</ItemGroup>
</Project>

31
MCI18n.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36212.18
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MCI18n", "MCI18n.csproj", "{471DA248-800A-4E64-94FA-F7054AEA248F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpNBT", "..\SharpNBT\SharpNBT\SharpNBT.csproj", "{173F7856-B894-5F24-D410-91B18378A44B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{471DA248-800A-4E64-94FA-F7054AEA248F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{471DA248-800A-4E64-94FA-F7054AEA248F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{471DA248-800A-4E64-94FA-F7054AEA248F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{471DA248-800A-4E64-94FA-F7054AEA248F}.Release|Any CPU.Build.0 = Release|Any CPU
{173F7856-B894-5F24-D410-91B18378A44B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{173F7856-B894-5F24-D410-91B18378A44B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{173F7856-B894-5F24-D410-91B18378A44B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{173F7856-B894-5F24-D410-91B18378A44B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DF86C6F0-F56B-49DD-BF07-99777173117F}
EndGlobalSection
EndGlobal

60
MainWindow.xaml Normal file
View File

@ -0,0 +1,60 @@
<Window
x:Class="MCI18n.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MCI18n"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="SNBT编辑器"
Width="900"
Height="600"
Background="#3d3d3d"
Foreground="White"
mc:Ignorable="d">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- 工具栏 -->
<StackPanel
Grid.Row="0"
Margin="10"
Orientation="Horizontal">
<Button
Width="100"
Height="30"
Command="{Binding LoadQuestsCommand}"
Content="打开文件夹"
Style="{StaticResource MinecraftButtonStyle}" />
<Button
Width="100"
Height="30"
Command="{Binding SaveQuestAsCommand}"
Content="另存为"
Style="{StaticResource MinecraftButtonStyle}" />
<Button
Width="100"
Height="30"
Command="{Binding SaveQuestCommand}"
Content="保存(覆盖)"
Style="{StaticResource MinecraftButtonStyle}" />
</StackPanel>
<TabControl Grid.Row="1">
<TabItem Header="任务列表" />
<TabItem Header="战利品列表" />
</TabControl>
<!-- 状态栏
<StatusBar Grid.Row="2" Background="Transparent">
<TextBlock Background="Transparent" Foreground="White" Text="{Binding CurrentFilePath, StringFormat={}当前文件: {0}, TargetNullValue=未打开任何文件}"/>
</StatusBar>-->
</Grid>
</Window>

30
MainWindow.xaml.cs Normal file
View File

@ -0,0 +1,30 @@

using MCI18n.Utilities;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace MCI18n
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MainWindowViewModel viewModel;
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel = new MainWindowViewModel();
}
}
}

89
MainWindowViewModel.cs Normal file
View File

@ -0,0 +1,89 @@

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MCI18n.Utilities;
using Microsoft.Win32;
using SharpNBT;
using SharpNBT.SNBT;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace MCI18n
{
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private string _currentFilePath;
[RelayCommand]
private void LoadQuests()
{
var openFileDialog = new OpenFileDialog
{
Filter = "SNBT Files (*.snbt)|*.snbt|All Files (*.*)|*.*",
Title = "选择SNBT文件"
};
if (openFileDialog.ShowDialog() == true)
{
try
{
CurrentFilePath = openFileDialog.FileName;
//var questData = SNBTParser.LoadFromFile<QuestData>(CurrentFilePath);
var questData = StringNbt.Parse(File.ReadAllText(CurrentFilePath));
//if (questData != null)
//{
// //QuestList = new ObservableCollection<QuestData> { questData };
// //SelectedQuest = questData;
//}
//else
//{
// //QuestList = new ObservableCollection<QuestData>();
// //SelectedQuest = null;
//}
//var fileContent = File.ReadAllText(CurrentFilePath, Encoding.UTF8);
//var parser = new NbtTagParser();
//var snbtData = parser.Parse(fileContent);
// var datas = SNBTParser.LoadFromFile(CurrentFilePath).Parse();
// 解析SNBT文件
//var data = SNBTParser.ParseFile(CurrentFilePath);
//NbtFile.Write(CurrentFilePath + ".r", questData, FormatOptions.None);
//StringNbt.SaveToFile(questData, CurrentFilePath + ".r",prettyPrint:true);
SnbtWriter.SaveToFile(questData, CurrentFilePath + ".r", prettyPrint: true);
}
catch (Exception ex)
{
MessageBox.Show($"加载文件失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
}
[RelayCommand]
private void SaveQuest()
{
}
[RelayCommand]
private void SaveQuestAs()
{
}
[RelayCommand]
private void CreateNewQuest()
{
}
}
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net8.0-windows\publish\win-x64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<_TargetId>Folder</_TargetId>
<TargetFramework>net8.0-windows</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>false</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<PublishReadyToRun>false</PublishReadyToRun>
</PropertyGroup>
</Project>

70
Theme/MCButtonStyle.xaml Normal file
View File

@ -0,0 +1,70 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Button" x:Key="MinecraftButtonStyle">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="#48494a"/>
<Setter Property="BorderBrush" Value="#373737"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Padding" Value="10,5"/>
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border
BorderBrush="Black"
BorderThickness="2"
>
<Grid>
<!-- 按钮底部阴影 -->
<Border x:Name="ShadowBorder"
Background="#313233"
CornerRadius="0" />
<!-- 按钮主体 -->
<Border x:Name="MainBorder"
Background="#5B5B5B"
BorderBrush="#373737"
BorderThickness="2,2,2,4"
CornerRadius="0"
Margin="0,0,0,0">
<ContentPresenter x:Name="ContentPresenter"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
TextElement.Foreground="{TemplateBinding Foreground}"/>
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- 鼠标悬停效果 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="MainBorder" Property="Background" Value="#6B6B6B"/>
<Setter TargetName="MainBorder" Property="BorderBrush" Value="#cccccc"/>
</Trigger>
<!-- 点击效果 -->
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="MainBorder" Property="Margin" Value="0,2,0,0"/>
<Setter TargetName="MainBorder" Property="BorderThickness" Value="2,2,2,2"/>
<Setter TargetName="ShadowBorder" Property="Visibility" Value="Hidden"/>
</Trigger>
<!-- 禁用效果 -->
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="MainBorder" Property="Background" Value="#3F3F3F"/>
<Setter TargetName="MainBorder" Property="BorderBrush" Value="#2A2A2A"/>
<Setter Property="Foreground" Value="#909090"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

78
Theme/MCTabTheme.xaml Normal file
View File

@ -0,0 +1,78 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="TabControl">
<Setter Property="Background" Value="#48494a" />
</Style>
<Style TargetType="TabItem">
<Setter Property="Background" Value="#48494a" />
<Setter Property="Foreground" Value="#fff" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<!--<Grid>
<Border Background="{TemplateBinding Background}" BorderBrush="#373737" BorderThickness="2">
<ContentPresenter x:Name="ContentSite"
ContentSource="Header"
Margin="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Border>
</Grid>-->
<Border
x:Name="OutBorder"
BorderBrush="Black"
BorderThickness="1">
<Grid>
<!-- 按钮底部阴影 -->
<Border
x:Name="ShadowBorder"
Background="#313233"
CornerRadius="0" />
<!-- 按钮主体 -->
<Border
x:Name="MainBorder"
Margin="0,0,0,0"
Background="#5B5B5B"
BorderBrush="#373737"
BorderThickness="1,1,1,1"
CornerRadius="0">
<ContentPresenter
x:Name="ContentSite"
Margin="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header" />
</Border>
</Grid>
</Border>
<ControlTemplate.Triggers>
<!-- 鼠标悬停效果 -->
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="MainBorder" Property="Background" Value="#6B6B6B" />
<Setter TargetName="MainBorder" Property="BorderBrush" Value="#cccccc" />
</Trigger>
<!-- 选中效果 -->
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="MainBorder" Property="BorderThickness" Value="1,1,1,4" />
<Setter TargetName="ShadowBorder" Property="Visibility" Value="Hidden" />
<Setter TargetName="OutBorder" Property="BorderBrush" Value="#cccccc" />
<Setter TargetName="OutBorder" Property="BorderThickness" Value="1,1,1,0" />
</Trigger>
<!-- 禁用效果 -->
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="MainBorder" Property="Background" Value="#3F3F3F" />
<Setter TargetName="MainBorder" Property="BorderBrush" Value="#2A2A2A" />
<Setter Property="Foreground" Value="#909090" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

211
Utilities/SnbtWriter.cs Normal file
View File

@ -0,0 +1,211 @@
using SharpNBT;
using SharpNBT.SNBT;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Numerics;
using System.Text;
namespace MCI18n.Utilities
{
/// <summary>
/// 提供用于将CompoundTag保存为SNBT格式文件的工具方法
/// </summary>
public static class SnbtWriter
{
/// <summary>
/// 将CompoundTag保存为SNBT格式文件
/// </summary>
/// <param name="tag">要保存的CompoundTag</param>
/// <param name="filePath">文件路径</param>
/// <param name="prettyPrint">是否进行美化格式输出</param>
/// <exception cref="ArgumentNullException">当tag为null时抛出</exception>
/// <exception cref="IOException">写入文件失败时抛出</exception>
public static void SaveToFile(CompoundTag tag, string filePath, bool prettyPrint = true)
{
if (tag == null)
throw new ArgumentNullException(nameof(tag));
try
{
string snbt = ConvertToSnbt(tag, prettyPrint);
File.WriteAllText(filePath, snbt, Encoding.UTF8);
}
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException or DirectoryNotFoundException)
{
throw new IOException($"保存SNBT文件失败 '{filePath}': {ex.Message}", ex);
}
}
/// <summary>
/// 将CompoundTag转换为SNBT格式字符串
/// </summary>
/// <param name="tag">要转换的CompoundTag</param>
/// <param name="prettyPrint">是否美化输出格式</param>
/// <returns>SNBT格式字符串</returns>
public static string ConvertToSnbt(CompoundTag tag, bool prettyPrint = true)
{
if (prettyPrint)
return FormatCompound(tag, 0);
else
return tag.Stringify(true);
}
private static string FormatCompound(CompoundTag tag, int indentLevel)
{
if (tag.Count == 0)
return "{}";
var sb = new StringBuilder();
sb.AppendLine("{");
int i = 0;
foreach (var child in tag)
{
sb.Append('\t', indentLevel + 1);
sb.Append($"{EscapeName(child.Name)}: ");
sb.Append(FormatTag(child, indentLevel + 1));
//if (i < tag.Count - 1)
// sb.AppendLine(",");
//else
// sb.AppendLine();
sb.AppendLine();
i++;
}
sb.Append('\t', indentLevel);
sb.Append("}");
return sb.ToString();
}
private static string FormatTag(Tag tag, int indentLevel)
{
switch (tag)
{
case CompoundTag compoundTag:
return FormatCompound(compoundTag, indentLevel);
case ListTag listTag:
return FormatList(listTag, indentLevel);
case ByteArrayTag byteArrayTag:
return FormatArray(byteArrayTag, 'B', 'b', indentLevel);
case IntArrayTag intArrayTag:
return FormatArray(intArrayTag, 'I', null, indentLevel);
case LongArrayTag longArrayTag:
return FormatArray(longArrayTag, 'L', 'L', indentLevel);
case StringTag stringTag:
return $"\"{EscapeString(stringTag.Value)}\"";
case ByteTag byteTag:
//return $"{byteTag.Value}b";
return byteTag.Bool ? "true" : "false";
case ShortTag shortTag:
return $"{shortTag.Value}s";
case IntTag intTag:
return intTag.Value.ToString();
case LongTag longTag:
return $"{longTag.Value}L";
case FloatTag floatTag:
return $"{floatTag.Value.ToString("0.#####", CultureInfo.InvariantCulture)}f";
case DoubleTag doubleTag:
return $"{doubleTag.Value.ToString("0.#####", CultureInfo.InvariantCulture)}d";
default:
return tag.Stringify(false);
}
}
private static string FormatList(ListTag listTag, int indentLevel)
{
if (listTag.Count == 0)
return "[]";
var sb = new StringBuilder();
sb.AppendLine("[");
for (int i = 0; i < listTag.Count; i++)
{
sb.Append('\t', indentLevel + 1);
sb.Append(FormatTag(listTag[i], indentLevel + 1));
//if (i < listTag.Count - 1)
// sb.AppendLine(",");
//else
// sb.AppendLine();
sb.AppendLine();
}
sb.Append('\t', indentLevel);
sb.Append("]");
return sb.ToString();
}
private static string FormatArray<T>(ArrayTag<T> arrayTag, char prefix, char? suffix, int indentLevel)
where T : unmanaged, INumber<T>
{
if (arrayTag.Count == 0)
return $"[{prefix};]";
var sb = new StringBuilder();
sb.Append($"[{prefix};");
// 对于数组类型,我们通常不使用多行格式
for (int i = 0; i < arrayTag.Count; i++)
{
if (i > 0)
sb.AppendLine();
//sb.Append("\r\n");
sb.Append(arrayTag[i]);
if (suffix != null)
sb.Append(suffix.Value);
}
sb.Append("]");
return sb.ToString();
}
private static string EscapeName(string name)
{
// 如果名称包含特殊字符,需要用引号括起来
if (string.IsNullOrEmpty(name) || NeedsQuotes(name))
return $"\"{EscapeString(name)}\"";
return name;
}
private static bool NeedsQuotes(string s)
{
foreach (char c in s)
{
if (!char.IsLetterOrDigit(c) && c != '_' && c != '-' && c != '.')
return true;
}
return false;
}
private static string EscapeString(string str)
{
if (string.IsNullOrEmpty(str))
return str;
return str.Replace("\\", "\\\\")
.Replace("\"", "\\\"")
.Replace("\n", "\\n")
.Replace("\r", "\\r")
.Replace("\t", "\\t");
}
}
}

File diff suppressed because it is too large Load Diff