├── images
├── image1.png
└── image2.png
├── NBTEditor
├── Assets.xcassets
│ ├── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── icon.png
│ │ ├── icon 7.png
│ │ └── Contents.json
│ └── AccentColor.colorset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── NBTEditor.entitlements
├── NBTEditorRelease.entitlements
├── ContentView.swift
├── NBTEditorApp.swift
├── NBTDocument.swift
└── NBTView.swift
├── NBTEditor.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
├── xcuserdata
│ └── tuhaotian.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── README.md
└── .gitignore
/images/image1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thtTNT/NBTEditor/HEAD/images/image1.png
--------------------------------------------------------------------------------
/images/image2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thtTNT/NBTEditor/HEAD/images/image2.png
--------------------------------------------------------------------------------
/NBTEditor/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/NBTEditor/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/NBTEditor/Assets.xcassets/AppIcon.appiconset/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thtTNT/NBTEditor/HEAD/NBTEditor/Assets.xcassets/AppIcon.appiconset/icon.png
--------------------------------------------------------------------------------
/NBTEditor/Assets.xcassets/AppIcon.appiconset/icon 7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thtTNT/NBTEditor/HEAD/NBTEditor/Assets.xcassets/AppIcon.appiconset/icon 7.png
--------------------------------------------------------------------------------
/NBTEditor.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/NBTEditor/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/NBTEditor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/NBTEditor/NBTEditor.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-write
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/NBTEditor/NBTEditorRelease.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-write
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/NBTEditor/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // NBTEditor
4 | //
5 | // Created by 屠昊天 on 8/6/2024.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 | import MinecraftNBT
11 |
12 |
13 | struct ContentView: View {
14 |
15 | @Binding var document : NBTDocument
16 |
17 | var body: some View {
18 | NBTView(data: $document.nbt)
19 | }
20 |
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/NBTEditor.xcodeproj/xcuserdata/tuhaotian.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | NBTEditor.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/NBTEditor/NBTEditorApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NBTEditorApp.swift
3 | // NBTEditor
4 | //
5 | // Created by 屠昊天 on 8/6/2024.
6 | //
7 |
8 | import SwiftUI
9 | import MinecraftNBT
10 |
11 | class AppDelegate : NSObject, NSApplicationDelegate {
12 |
13 | }
14 |
15 | @main
16 | struct NBTEditorApp: App {
17 |
18 | @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate;
19 |
20 |
21 | var body: some Scene {
22 | DocumentGroup(newDocument: NBTDocument()) {configuration in
23 | ContentView(document: configuration.$document)
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/NBTEditor.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "68b72d5e07500e0953fdf41f00bdd8e619a04f07ae98f9b13b805a377f5f16ad",
3 | "pins" : [
4 | {
5 | "identity" : "gzipswift",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/1024jp/GzipSwift.git",
8 | "state" : {
9 | "revision" : "7a7f17761c76a932662ab77028a4329f67d645a4",
10 | "version" : "5.2.0"
11 | }
12 | },
13 | {
14 | "identity" : "swift-collections",
15 | "kind" : "remoteSourceControl",
16 | "location" : "https://github.com/apple/swift-collections.git",
17 | "state" : {
18 | "revision" : "ee97538f5b81ae89698fd95938896dec5217b148",
19 | "version" : "1.1.1"
20 | }
21 | },
22 | {
23 | "identity" : "swift-nbt",
24 | "kind" : "remoteSourceControl",
25 | "location" : "https://github.com/ezfe/swift-nbt",
26 | "state" : {
27 | "branch" : "main",
28 | "revision" : "b7457ff8426066010ef748a5077fd50e7fa19459"
29 | }
30 | }
31 | ],
32 | "version" : 3
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NBTEditor
2 |
3 | This is a MacOS application that allows users to read and edit NBT files in Minecraft.
4 | If you find this project useful give me a Star.
5 | **This project is still in development and all bug reports and feature requests are welcome.**
6 |
7 | The goal of this project is:
8 |
9 | - Complete
10 |
11 | - Lightweight
12 |
13 | - Friendly UI
14 |
15 | 
16 |
17 | 
18 |
19 | ## Download
20 |
21 | This pre-released version can be found on the release page.
22 |
23 | ## To-Do
24 |
25 | - [x] Read NBT
26 |
27 | - [x] Edit Value
28 |
29 | - [x] Rename
30 |
31 | - [x] Delete Value
32 |
33 | - [ ] Add Value
34 |
35 | - [ ] Better UI
36 |
37 | ## Contribute
38 |
39 | The contribution to this project is welcomed. If you have some suggestions or bug reports, please create an issue.
40 |
41 | If you are a developer, feel free to create a PR with the issue with the mark `to-do` instead of `processing`
42 |
43 | ## Acknowledgement
44 |
45 | Thanks for the contributor of the following library:
46 |
47 | Swift-NBT [GitHub - ezfe/swift-nbt](https://github.com/ezfe/swift-nbt)
48 |
--------------------------------------------------------------------------------
/NBTEditor/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "scale" : "1x",
6 | "size" : "16x16"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "scale" : "2x",
11 | "size" : "16x16"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "scale" : "1x",
16 | "size" : "32x32"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "scale" : "2x",
21 | "size" : "32x32"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "scale" : "1x",
26 | "size" : "128x128"
27 | },
28 | {
29 | "filename" : "icon.png",
30 | "idiom" : "mac",
31 | "scale" : "2x",
32 | "size" : "128x128"
33 | },
34 | {
35 | "filename" : "icon 7.png",
36 | "idiom" : "mac",
37 | "scale" : "1x",
38 | "size" : "256x256"
39 | },
40 | {
41 | "idiom" : "mac",
42 | "scale" : "2x",
43 | "size" : "256x256"
44 | },
45 | {
46 | "idiom" : "mac",
47 | "scale" : "1x",
48 | "size" : "512x512"
49 | },
50 | {
51 | "idiom" : "mac",
52 | "scale" : "2x",
53 | "size" : "512x512"
54 | }
55 | ],
56 | "info" : {
57 | "author" : "xcode",
58 | "version" : 1
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/NBTEditor/NBTDocument.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NBTDocument.swift
3 | // NBTEditor
4 | //
5 | // Created by 屠昊天 on 17/6/2024.
6 | //
7 |
8 | import SwiftUI
9 | import UniformTypeIdentifiers
10 | import MinecraftNBT
11 |
12 | func isDataGzip(data : Data) -> Bool{
13 | guard data.count >= 2 else {
14 | return false;
15 | }
16 |
17 | return data[0] == 0x1F && data[1] == 0x8B;
18 | }
19 |
20 | struct NBTDocument : FileDocument {
21 | static var readableContentTypes: [UTType] = [UTType.data]
22 | var nbt : NBTStructure
23 |
24 | init(){
25 | self.nbt = NBTStructure()
26 | }
27 |
28 | init(configuration: ReadConfiguration) throws {
29 | guard let binaryData = configuration.file.regularFileContents else {
30 | throw NSError()
31 | }
32 |
33 | let structure: NBTStructure?
34 |
35 | if isDataGzip(data: binaryData) {
36 | structure = NBTStructure(compressed: binaryData)
37 | } else {
38 | structure = NBTStructure(decompressed: binaryData)
39 | }
40 |
41 | guard structure != nil else {
42 | throw NSError()
43 | }
44 |
45 | self.nbt = structure!
46 | }
47 |
48 | func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
49 | return FileWrapper(regularFileWithContents: self.nbt.data)
50 | }
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/swift,xcode,macos
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=swift,xcode,macos
3 |
4 | ### macOS ###
5 | # General
6 | .DS_Store
7 | .AppleDouble
8 | .LSOverride
9 |
10 | # Icon must end with two \r
11 | Icon
12 |
13 |
14 | # Thumbnails
15 | ._*
16 |
17 | # Files that might appear in the root of a volume
18 | .DocumentRevisions-V100
19 | .fseventsd
20 | .Spotlight-V100
21 | .TemporaryItems
22 | .Trashes
23 | .VolumeIcon.icns
24 | .com.apple.timemachine.donotpresent
25 |
26 | # Directories potentially created on remote AFP share
27 | .AppleDB
28 | .AppleDesktop
29 | Network Trash Folder
30 | Temporary Items
31 | .apdisk
32 |
33 | ### macOS Patch ###
34 | # iCloud generated files
35 | *.icloud
36 |
37 | ### Swift ###
38 | # Xcode
39 | #
40 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
41 |
42 | ## User settings
43 | xcuserdata/
44 |
45 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
46 | *.xcscmblueprint
47 | *.xccheckout
48 |
49 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
50 | build/
51 | DerivedData/
52 | *.moved-aside
53 | *.pbxuser
54 | !default.pbxuser
55 | *.mode1v3
56 | !default.mode1v3
57 | *.mode2v3
58 | !default.mode2v3
59 | *.perspectivev3
60 | !default.perspectivev3
61 |
62 | ## Obj-C/Swift specific
63 | *.hmap
64 |
65 | ## App packaging
66 | *.ipa
67 | *.dSYM.zip
68 | *.dSYM
69 |
70 | ## Playgrounds
71 | timeline.xctimeline
72 | playground.xcworkspace
73 |
74 | # Swift Package Manager
75 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
76 | # Packages/
77 | # Package.pins
78 | # Package.resolved
79 | # *.xcodeproj
80 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
81 | # hence it is not needed unless you have added a package configuration file to your project
82 | # .swiftpm
83 |
84 | .build/
85 |
86 | # CocoaPods
87 | # We recommend against adding the Pods directory to your .gitignore. However
88 | # you should judge for yourself, the pros and cons are mentioned at:
89 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
90 | # Pods/
91 | # Add this line if you want to avoid checking in source code from the Xcode workspace
92 | # *.xcworkspace
93 |
94 | # Carthage
95 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
96 | # Carthage/Checkouts
97 |
98 | Carthage/Build/
99 |
100 | # Accio dependency management
101 | Dependencies/
102 | .accio/
103 |
104 | # fastlane
105 | # It is recommended to not store the screenshots in the git repo.
106 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
107 | # For more information about the recommended setup visit:
108 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
109 |
110 | fastlane/report.xml
111 | fastlane/Preview.html
112 | fastlane/screenshots/**/*.png
113 | fastlane/test_output
114 |
115 | # Code Injection
116 | # After new code Injection tools there's a generated folder /iOSInjectionProject
117 | # https://github.com/johnno1962/injectionforxcode
118 |
119 | iOSInjectionProject/
120 |
121 | ### Xcode ###
122 |
123 | ## Xcode 8 and earlier
124 |
125 | ### Xcode Patch ###
126 | *.xcodeproj/*
127 | !*.xcodeproj/project.pbxproj
128 | !*.xcodeproj/xcshareddata/
129 | !*.xcodeproj/project.xcworkspace/
130 | !*.xcworkspace/contents.xcworkspacedata
131 | /*.gcno
132 | **/xcshareddata/WorkspaceSettings.xcsettings
133 |
134 | # End of https://www.toptal.com/developers/gitignore/api/swift,xcode,macos
135 |
--------------------------------------------------------------------------------
/NBTEditor/NBTView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NBTView.swift
3 | // NBTEditor
4 | //
5 | // Created by 屠昊天 on 8/6/2024.
6 | //
7 |
8 | import SwiftUI
9 | import MinecraftNBT
10 |
11 | struct NBTView : View {
12 |
13 | @Binding var data: NBTStructure
14 |
15 | @State var selectKey : [String] = []
16 |
17 | @State var editSheetVisible = false
18 | @State var renameSheetVisible = false
19 |
20 | var body: some View {
21 | let tag = data.tag
22 | List {
23 | ForEach(tag.contents.keys, id: \.self){ name in
24 | generateTree(key: [name], tag: tag[name]!)
25 | }
26 | }.sheet(isPresented: $editSheetVisible){
27 | EditValueSheetView(
28 | nbt: $data,
29 | key: $selectKey,
30 | visible: $editSheetVisible
31 | )
32 | }.sheet(isPresented: $renameSheetVisible){
33 | RenameSheetView(
34 | nbt: $data,
35 | key: $selectKey,
36 | visible: $renameSheetVisible
37 | )
38 | }
39 | }
40 |
41 | func getMenuItems(key: [String], tag : any NBTTag) -> some View{
42 | let parent = try! self.data.read(Array(key[0.. AnyView{
63 | let name = key.last!
64 | var letterView : LetterView
65 | switch tag {
66 | case is NBTByte:
67 | letterView = LetterView(letter: "B", color: Color(red: 158 / 255, green: 39 / 255, blue: 50 / 255))
68 | case is NBTShort:
69 | letterView = LetterView(letter: "S", color: Color(red: 156 / 255,green: 31 / 255, blue: 179 / 255))
70 | case is NBTInt:
71 | letterView = LetterView(letter: "I", color: Color(red: 50 / 255, green : 42 / 255, blue: 173 / 255))
72 | case is NBTLong:
73 | letterView = LetterView(letter: "L", color: Color(red: 54 / 255, green: 123 / 255, blue: 138 / 255))
74 | case is NBTDouble:
75 | letterView = LetterView(letter: "D", color: Color(red: 87 / 255, green: 175 / 255, blue: 83 / 255))
76 | case is NBTString:
77 | letterView = LetterView(letter: "S", color: Color(red: 62 / 255, green: 62 / 255, blue: 62 / 255))
78 | default:
79 | letterView = LetterView(letter: "U", color: Color.pink)
80 | }
81 | return AnyView(
82 | HStack {
83 | letterView
84 | Text(name + ":").bold()
85 | Text(tag.description)
86 | }
87 | .contextMenu{
88 | getMenuItems(key: key, tag: tag)
89 | }
90 |
91 | )
92 | }
93 |
94 | func generateTree(key: [String], tag: any NBTTag) -> AnyView{
95 | let name = key.last!
96 | switch tag{
97 | case is NBTByte:
98 | return generatePrimitiveElementLine(key: key, tag: tag)
99 | case is NBTShort:
100 | return generatePrimitiveElementLine(key: key, tag: tag)
101 | case is NBTInt:
102 | return generatePrimitiveElementLine(key: key, tag: tag)
103 | case is NBTLong:
104 | return generatePrimitiveElementLine(key: key, tag: tag)
105 | case is NBTFloat:
106 | return generatePrimitiveElementLine(key: key, tag: tag)
107 | case is NBTDouble:
108 | return generatePrimitiveElementLine(key: key, tag: tag)
109 | case is NBTString:
110 | return generatePrimitiveElementLine(key: key, tag: tag)
111 | case is NBTList:
112 | let list = tag as! NBTList
113 | return AnyView(DisclosureGroup(
114 | content: {
115 | if list.elements.count != 0 {
116 | ForEach(0..