找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 3456|回复: 3

【文件格式】【傻瓜式】MCEdit存档Schematic格式

[复制链接]

12

主题

35

回帖

959

积分

用户组: 大·技术宅

UID
3517
精华
1
威望
36 点
宅币
802 个
贡献
33 次
宅之契约
0 份
在线时间
207 小时
注册时间
2018-3-2
发表于 2020-3-16 22:51:01 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 Tao0Lu 于 2020-8-22 20:23 编辑

之前帖忘记写Schematic格式了,现在就氵一下吧
其实Wiki里有关于Schematic的格式的介绍,但是我自己通过导出MCEdit发现和Wiki中有些不一样,比如有些项目我导出来并没有,这里我就按我导出的的文件格式讲一下Schematic的格式。

对象 作用 大小
Schematic 识别Schematic格式 12字节
Height 高度Y 最大64K(mod突破高度限制?) 2字节
Length 长度Z 最大64K 2字节
Width 宽度X 最大64K 2字节
Entities 实体 不固定默认8字节
TileEntities NBT 不固定默认8字节
TileTicks 方块要更新的数据 不固定默认8字节
Materials 版本 默认Alpha 随意填其他没有事 不固定
Data 方块数据 也就是setblock后面那个数字 X × Y × Z字节
Biomes 每个方块的生物群系 X × Z字节
Block 方块(数字ID, 1. 8及之后可以使用MCEdit-Unified查看) X × Y × Z字节
  • 如果生物群系对你有作用的话,请看下表
名称 数字ID 数字ID(16进制)
海洋 0 0
平原 1 1
沙漠 2 2
山地 3 3
森林 4 4
针叶林 5 5
沼泽 6 6
河流 7 7
下界荒地 8 8
末地 9 9
冻洋 10 A
冻河 11 B
积雪的冻原 12 C
雪山 13 D
蘑菇岛 14 E
蘑菇岛岸 15 F
沙滩 16 10
沙漠丘陵 17 11
繁茂的丘陵 18 12
针叶林丘陵 19 13
山地边缘 20 14
丛林 21 15
丛林丘陵 22 16
丛林边缘 23 17
深海 24 18
石岸 25 19
积雪的沙滩 26 1A
桦木森林 27 1B
桦木森林丘陵 28 1C
黑森林 29 1D
积雪的针叶林 30 1E
积雪的针叶林丘陵 31 1F
巨型针叶林 32 20
巨型针叶林丘陵 33 21
繁茂的山地 34 22
热带草原 35 23
热带高原 36 24
恶地 37 25
繁茂的恶地高原 38 26
恶地高原 39 27
末地小型岛屿 40 28
末地中型岛屿 41 29
末地高岛 42 2A
末地荒岛 43 2B
暖水海洋 44 2C
温水海洋 45 2D
冷水海洋 46 2E
暖水深海 47 2F
温水深海 48 30
冷水深海 49 31
封冻深海 50 32
虚空 127 7F
向日葵平原 129 81
沙漠湖泊 130 82
沙砾山地 131 83
繁花森林 132 84
针叶林山地 133 85
沼泽山丘 134 86
冰刺平原 140 8C
丛林变种 149 95
丛林边缘变种 151 97
高大桦木森林 155 9B
高大桦木丘陵 156 9C
黑森林丘陵 157 9D
积雪的针叶林山地 158 9E
巨型云杉针叶林 160 A0
巨型云杉针叶林丘陵 161 A1
沙砾山地+ 162 A2
破碎的热带草原 163 A3
破碎的热带高原 164 A4
被风蚀的恶地 165 A5
繁茂的恶地高原变种 166 A6
恶地高原变种 167 A7
竹林 168 A8
竹林丘陵 169 A9
灵魂沙峡谷 170 AA
绯红森林 171 AB
诡异森林 172 AC
玄武岩三角洲 173 AD
  • 谜?
    对于其中的高度限制,我还是不太清楚。虽然说有些Mod可以突破高度限制,但是似乎Mcedit也有高度限制。几次实验后发现如果高度大于255,那么高度好像会从第一行重新写入,也就是说把从第一行已经生成的方块给覆盖掉。如果只有255方块,那么为什么大小要设置成两个字节,难道一个字节不就能用完呢?希望有人能帮我解惑。

  • 格式中每个数据(姑且就这么叫吧)之间有指定的字符串去隔开,而在指定的字符串后还要声明此数据的长度。所以每一个数据的格式如下

    指定的分隔字节(两字节)+数据标题长度(一字节)+数据标题+数据长度(+数据)(当为Data,Biomes,Block...时)

识别对象
0x0A 0x00 Schematic文件
0x02 0x00 Height,Length,Width
0x09 0x00 Entities,TileEntities,TileTicks
0x08 0x00 Materials
0x07 0x00 Data,Biomes,Blocks
  • 知道了格式接下来就好做了。
    将数据写入文件,再通过同目录下的gzip.exe压缩成x.gz文件,最后重命名为Schematic文件即可用MCEdit打开。

temp(gzip压缩前)


1.png

temp(gzip压缩前)


2.png
  • 那么这里就讲一下data和block数据吧
    通过立体坐标计算那么坐标为(X,Y,Z)的方块的data和block数据位于第

    (Y - 1) × (Width × Height) + ( Z - 1) × Width + X

    个数据内(Width 和 Height为总的长和宽),只要在这个位置写入Block数据和Data数据就可以了。

可能你不怎么理解,如果看一下这张图和实例也许就知道了


QQ截图20200822202207.png
  • 按X和Z轴来进行,这里有一个5*5的二维平面。我们从第一个方块开始,给每一个方块标记数字。在二维中公式是这样的

    (所在行数-1)×总的列数+所在列数

    可见图中红色方块在第5行第4列,代入公式:

    (5-1)×5+4=24 成立

    你也可以去试试其他方块,得到的结果是一样的。
    其实也能想象得出来,多了一维的情况下,公式变成了这样

    (所在高度-1)×总的行数×总的列数+(所在行数-1)×总的列数+所在列数

VB代码

  1. Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long
  2. Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
  3. Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long

  4. Private Sub Gen()
  5. ....
  6. LoopDB = BmpWidth * BmpHeight * BY '长*宽*高
  7. LoopBI = BmpWidth * BmpHeight '长*宽
  8. TempHX = Replace(Format(Hex(BmpWidth), "@@@@"), " ", "0") '转换成16进制的字符串,以便于写入文件
  9. TempHY = Replace(Format(Hex(BY), "@@@@"), " ", "0")
  10. TempHZ = Replace(Format(Hex(BmpHeight), "@@@@"), " ", "0")
  11. TempHLoopDB = Replace(Format(Hex(LoopDB), "@@@@@@@@"), " ", "0")
  12. TempHLoopBI = Replace(Format(Hex(LoopBI), "@@@@@@@@"), " ", "0")
  13. HX = TempHX
  14. HY = TempHY
  15. HZ = TempHZ
  16. HLoopDB = TempHLoopDB
  17. HLoopBI = TempHLoopBI

  18. ReDim StrDT(LoopDB)
  19. ReDim StrBL(LoopDB)

  20. For Y=1 To BY
  21. For X=1 To BmpWidth
  22. For Z=1 To BmpHeight
  23. StrP = (Y - 1) * (BmpWidth * BmpHeight) + (Z - 1) * BmpWidth  + X '3维方块对应Block和Data数据的位置
  24. StrBL(StrP) = m_Names(Block) '也许用结构体会好点?
  25. StrDT(StrP) = m_Nbt(Block)
  26. '....Block的处理
  27. Next
  28. Next
  29. Next

  30. Open App.Path & "\temp" For Binary As #1 '在当前目录下新建temp文件
  31. Put #1, , Chr$(10) & Chr$(0) & Chr$(9) & "Schematic" '指定的分隔字符串(两字节)+数据标题长度(一字节)+数据标题+数据长度(+数据)
  32. Put #1, , Chr$(2) & Chr$(0) & Chr$(6) & "Height"
  33. Put #1, , CByte("&H" & CStr(Left(HY, 2))) '数据的长度(要以二进制的文件写入)
  34. Put #1, , CByte("&H" & CStr(Right(HY, 2)))
  35. Put #1, , Chr$(2) & Chr$(0) & Chr$(6) & "Length"
  36. Put #1, , CByte("&H" & CStr(Left(HZ, 2)))
  37. Put #1, , CByte("&H" & CStr(Right(HZ, 2)))
  38. Put #1, , Chr$(2) & Chr$(0) & Chr$(5) & "Width"
  39. Put #1, , CByte("&H" & CStr(Left(HX, 2)))
  40. Put #1, , CByte("&H" & CStr(Right(HX, 2)))
  41. Put #1, , Chr$(9) & Chr$(0) & Chr$(8) & "Entities" & Chr$(1) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(9) & Chr$(0) & Chr$(12) & "TileEntities" & Chr$(1) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(9) & Chr$(0) & Chr$(9) & "TileTicks" & Chr$(1) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(0) & Chr$(8) & Chr$(0) & Chr$(9) & "Materials" & Chr$(0) & Chr$(5) & "Alpha" & Chr$(7) & Chr$(0) & Chr$(4) & "Data"
  42. Put #1, , CByte("&H" & CStr(Left(HLoopDB, 2)))
  43. Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 3, 2)))
  44. Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 5, 2)))
  45. Put #1, , CByte("&H" & CStr(Right(HLoopDB, 2)))

  46. For I = 0 To LoopDB - 1
  47.     If Len(StrDT(I)) = 0 Then
  48.         Put #1, , Chr$(0)
  49.     Else
  50.         Put #1, , CByte(StrDT(I)) 'VB的bug?直接Put #1, , StrDT每两个字节之间会多出00
  51.     End If
  52. Next

  53. Put #1, , Chr$(7) & Chr$(0) & Chr$(6) & "Biomes"
  54. Put #1, , CByte("&H" & CStr(Left(HLoopBI, 2)))
  55. Put #1, , CByte("&H" & CStr(Mid(HLoopBI, 3, 2)))
  56. Put #1, , CByte("&H" & CStr(Mid(HLoopBI, 5, 2)))
  57. Put #1, , CByte("&H" & CStr(Right(HLoopBI, 2)))

  58. For I = 0 To LoopBI - 1
  59.         Put #1, , Chr$(0)
  60. Next

  61. Put #1, , Chr$(7) & Chr$(0) & Chr$(6) & "Blocks"
  62. Put #1, , CByte("&H" & CStr(Left(HLoopDB, 2)))
  63. Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 3, 2)))
  64. Put #1, , CByte("&H" & CStr(Mid(HLoopDB, 5, 2)))
  65. Put #1, , CByte("&H" & CStr(Right(HLoopDB, 2)))

  66. For I = 0 To LoopDB - 1
  67.     If Len(StrBL(I)) = 0 Then
  68.         Put #1, , Chr$(0)
  69.     Else
  70.         Put #1, , CByte(StrBL(I))
  71.     End If
  72. Next

  73. Put #1, , Chr$(0)

  74. Close #1

  75. OutPut

  76. End Sub

  77. Private Sub OutPut()
  78. Dim Path As String
  79. Dim I As Long, R As Long, P As Long
  80.     Path = SaveFile()'文件对话框,这里不多讲了,此时Path已经成为一个Schematic的目录
  81.    
  82.     I = Shell(App.Path & "\gzip.exe -f """ & App.Path & "\temp""", vbNormalFocus) '调用Gzip压缩(输出为.gz文件)
  83.     P = OpenProcess(SYNCHRONIZE, False, I) '等待进程(文件都没有生成怎么进行下一步啊喂)
  84.     R = WaitForSingleObject(P, INFINITE)
  85.     R = CloseHandle(P)
  86.    
  87.     I = Shell("cmd /c copy """ & App.Path & "\temp.gz"" " & Path & " /y", vbNormalFocus)'重命名成.Schematic文件
  88.     P = OpenProcess(SYNCHRONIZE, False, I)
  89.     R = WaitForSingleObject(P, INFINITE)
  90.     R = CloseHandle(P)
  91.    
  92.     I = Shell("cmd /c del /f /q """ & App.Path & "\temp.gz""", vbNormalFocus)'删除Temp
  93.     P = OpenProcess(SYNCHRONIZE, False, I)
  94.     R = WaitForSingleObject(P, INFINITE)
  95.     R = CloseHandle(P)
  96. End Sub
复制代码

注意

  • 在VB写入文件时,方块等数据不能使用Chr,这是因为当Chr的Acsii编码超过128的都会被翻译成"?",所以需要通过写入Binary文件,调用CByte来写入文件。
  • 除数据标题以外以上数据都以二进制的方式写入文件。
  • 数据可以调换顺序。








回复

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
发表于 2020-3-21 19:09:21 | 显示全部楼层
同为邯郸老乡在线支持大佬!
Passion Coding!
回复 赞! 靠!

使用道具 举报

12

主题

35

回帖

959

积分

用户组: 大·技术宅

UID
3517
精华
1
威望
36 点
宅币
802 个
贡献
33 次
宅之契约
0 份
在线时间
207 小时
注册时间
2018-3-2
 楼主| 发表于 2020-3-21 19:17:25 | 显示全部楼层
watermelon 发表于 2020-3-21 19:09
同为邯郸老乡在线支持大佬!

? 话说这个不是显示自己所在的IP吗,或者我发布的时候魔法上网了?
回复 赞! 靠!

使用道具 举报

29

主题

315

回帖

1561

积分

用户组: 上·技术宅

UID
3808
精华
11
威望
105 点
宅币
702 个
贡献
165 次
宅之契约
0 份
在线时间
404 小时
注册时间
2018-5-6
发表于 2020-3-21 19:19:37 | 显示全部楼层
Tao0Lu 发表于 2020-3-21 19:17
? 话说这个不是显示自己所在的IP吗,或者我发布的时候魔法上网了?

哦,哈哈哈,我是看的地下的那个签名图片上的“Network”项的,应该是根据IP地址写出来的所在地。
Passion Coding!
回复 赞! 靠!

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-20 18:25 , Processed in 0.046920 second(s), 36 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表