开启辅助访问     
收藏本站

站内搜索

搜索

Minecraft(我的世界)苦力怕论坛

[软件开发讨论] 前端level.dat编辑器0&1

 发表于 6 天前 来自手机|显示全部楼层|阅读模式 IP:江苏省
本帖最后由 类下托泣匹 于 2026-7-1 14:33 编辑

建议在电脑端阅读https://klpbbs.com/thread-171663-1-1.html?mobile=no

前言

由于个人爱好,本教程使用 ClojureScript 编写。但请放心——文中所有二进制操作调用的都是标准 JavaScript APIArrayBufferDataViewUint8ArrayTextEncoder/TextDecoder等。ClojureScript 的语法差异很小,看到 js/DataView. 就是 new DataView()(.getInt32 dv 0 true) 就是 dv.getInt32(0, true),点号在前只是调用方式的写法不同。如果你熟悉 JS 的二进制操作,阅读本文自然不会有障碍。


首先先在页面里创建一个文件选择框
[:input {:type "file" :id "level"}]
然后把文件存到变量level里面去
(def level (js/document.getElementById "level"));;def是绑定变量

1,操作文件初步和读取头部

在js中操作二进制,只有三个对象
ArrayBuffer:一块原始内存,你不能直接读里面的数
DataView:用来从 ArrayBuffer 里按类型读数(Int8/Int16/Int32/Float)
Uint8Array:把 ArrayBuffer 当成字节数组来用,可以像数组一样取下标。

选了文件后,浏览器给的是 File 对象。用 FileReader 转成 ArrayBuffer:
(def reader (js/FileReader.))
(js是const reader = new FileReader();)

读取时不会保存到变量,所以我们需要设置:

(set! (.-onload reader)
      (fn [event]
        ;; event.target.result 就是 ArrayBuffer
        (def file-array (.. event -target -result))
        (println "读取完成,大小:" (.-byteLength file-array)))

js里面是

reader.onload = (evt) => {
    //赋值
  };

然后用readAsArrayBuffer方法读取:(.readAsArrayBuffer reader (-> level .-files (aget 0)))


我们可以包成 Uint8Array来查看这个二进制文件
(def file-bytes (js/Uint8Array. file-array))
(println (vec file-bytes))

Little-Endian 读取规则:低位在前。
[85 10 0 0] = 85 + 10×256 + 0×65536 + 0×16777216 = 2645。
在处理时用Uint8Array需要手动写代码计算不怎么方便。
所以我们用dataview来读取头部。

(defn parse-header [array-buffer]
  (let [dv (js/DataView. array-buffer 0 8)]
    {:version (.getInt32 dv 0 true);;等价于js里的dv.getInt32(…)
     :length  (.getInt32 dv 4 true)}))

我们用getInt32读取32位整数,也就是[x x x x],在js里的用法是dataview.getInt32(byteOffset [, littleEndian])。基岩版使用了littleEndian,故使用时要将littleEndian设为true。


等等,上面那是什么意思?接下来我们来剖析level.dat文件本身。

基岩版 level.dat 只有两层:
整个文件 = 头部(8字节) + 身体(N字节)

[10 0 0 0       ;; offset 0: 版本号 (Little-Endian Int32) → 10
 85 10 0 0       ;; offset 4: 身体长度 (Little-Endian Int32) → 2645
 10 0 0 ...]     ;; offset 8: 其他都是NBT 数据

NBT 标签的通用结构
每个 NBT 标签都是三段式:
[Type(1字节)  NameLength(2字节LE)  Name(UTF-8)  Payload(变长)]

示例:一个 Byte 标签 "Difficulty" = 1

[1                    ;; Type = 1 (Byte)
 10 0                 ;; NameLength = 10 (Little-Endian Int16)
68 105 102 102 105  ;; "Diffi"
99 117 108 116 121 ;; "culty"
 1]                   ;; Payload = 1

注意⚠️level.dat未被zlib压缩。

下一篇教程我们来读取Body部分的nbt。




苦力怕论坛,感谢有您~

本版积分规则

本站
关于我们
联系我们
坛史纲要
官方
哔哩哔哩
技术博客
下载
网易版
安卓版
JAVA
反馈
意见建议
教程中心
更多
捐助本站
QQ群
QQ群

QQ群

访问手机版

访问手机版

手机版|小黑屋|系统状态|klpbbs.com

| 由 木韩网络 提供支持 | GMT+8, 2026-7-7 00:49

声明:本站与Mojang以及微软公司没有从属关系

Powered by Discuz! X3.4