diff --git a/NBToolkit/NBToolkit/ChunkFilter.cs b/NBToolkit/NBToolkit/ChunkFilter.cs
index 67259de..eafaa57 100644
--- a/NBToolkit/NBToolkit/ChunkFilter.cs
+++ b/NBToolkit/NBToolkit/ChunkFilter.cs
@@ -156,9 +156,15 @@ namespace NBToolkit
{
_options = new OptionSet () {
{ "cxr|ChunkXRange=", "Include chunks with X-chunk coord between {0:V1} and {1:V2}, inclusive. V1 or V2 may be left blank.",
- (v1, v2) => { _xAboveEq = Convert.ToInt32(v1); _xBelowEq = Convert.ToInt32(v2); } },
+ (v1, v2) => {
+ try { _xAboveEq = Convert.ToInt32(v1); } catch (FormatException) { }
+ try { _xBelowEq = Convert.ToInt32(v2); } catch (FormatException) { }
+ } },
{ "czr|ChunkZRange=", "Include chunks with Z-chunk coord between {0:V1} and {1:V2}, inclusive. V1 or V2 may be left blank.",
- (v1, v2) => { _zAboveEq = Convert.ToInt32(v1); _zBelowEq = Convert.ToInt32(v2); } },
+ (v1, v2) => {
+ try { _zAboveEq = Convert.ToInt32(v1); } catch (FormatException) { }
+ try { _zBelowEq = Convert.ToInt32(v2); } catch (FormatException) { }
+ } },
{ "crv|ChunkInvertXZ", "Inverts the chunk selection created by --cxr and --czr when both options are used.",
v => _invertXZ = true },
{ "ci|ChunkInclude=", "Include chunks that contain blocks of type {ID}. This option is repeatable.",
diff --git a/NBToolkit/NBToolkit/Harness.cs b/NBToolkit/NBToolkit/Harness.cs
deleted file mode 100644
index 982d90b..0000000
--- a/NBToolkit/NBToolkit/Harness.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.IO;
-using NBT;
-
-namespace NBToolkit
-{
- class Harness
- {
- public void Run (TKOptions opt, TKFilter filter) {
- World world = new World(opt.OPT_WORLD);
- RegionList regions = new RegionList(world.GetRegionManager());
-
- int r = 0;
- int c = 0;
- //foreach (Region region in regions) {
- // Console.WriteLine(region.GetFileName());
-
-
- foreach (Chunk chunk in new ChunkList(world.GetChunkManager())) {
- //Console.WriteLine("Chunk " + chunk.X + ", " + chunk.Z);
- c++;
- // }
-
- //foreach (Chunk chunk in new ChunkList(world.GetChunkManager(), region)) {
- // c++;
- // }
-
- //r++;
- /*for (int x = 0; x < ChunkManager.REGION_XLEN; x++) {
- for (int z = 0; z < ChunkManager.REGION_ZLEN; z++) {
- if (region.ChunkExists(x, z)) {
- c++;
- }
- }
- }*/
- }
- Console.WriteLine("Region Count: " + r);
- Console.WriteLine("Chunk Count: " + c);
-
- /*string[] regions = RegionFileCache.GetRegionFileList(opt.OPT_WORLD);
-
- foreach (string p in regions) {
- Console.WriteLine(p);
- RegionFile rfile = RegionFileCache.GetRegionFile(opt.OPT_WORLD, p);
-
- for (int x = 0; x < 32; x++) {
- for (int z = 0; z < 32; z++) {
- NBT_Tree tree = new NBT_Tree(rfile.GetChunkDataInputStream(x, z));
-
- // Check that tree exists
- if (tree == null || tree.getRoot() == null) {
- continue;
- }
-
- NBT_Tag level = tree.getRoot().findTagByName("Level");
- if (level == null) {
- continue;
- }
-
- NBT_Tag tagcx = level.findTagByName("xPos");
- NBT_Tag tagcz = level.findTagByName("zPos");
-
- if (tagcx == null || tagcz == null) {
- continue;
- }
-
- int cx = tagcx.value.toInt().data;
- int cz = tagcz.value.toInt().data;
-
- // Exclude chunks out of range
- if (opt.CH_X_GE != null && cx < opt.CH_X_GE) {
- continue;
- }
- if (opt.CH_X_LE != null && cx > opt.CH_X_LE) {
- continue;
- }
- if (opt.CH_Z_GE != null && cz < opt.CH_Z_GE) {
- continue;
- }
- if (opt.CH_Z_LE != null && cz > opt.CH_Z_LE) {
- continue;
- }
-
- // Verify that chunk contains all of the INCLUDE options
- bool fail = false;
- foreach (int v in opt.OPT_CH_INCLUDE) {
- if (!BlockScan(level, v)) {
- fail = true;
- break;
- }
- }
-
- // Verify that chunk does not contain any EXCLUDE options
- foreach (int v in opt.OPT_CH_EXCLUDE) {
- if (BlockScan(level, v)) {
- fail = true;
- break;
- }
- }
-
- if (fail) {
- continue;
- }
-
- if (opt.OPT_V) {
- Console.WriteLine("Processing Chunk ({0}, {1})", cx, cz);
- }
-
- filter.ApplyChunk(tree);
-
- Stream zipStream = RegionFileCache.getChunkDataOutputStream(opt.OPT_WORLD, cx, cz);
- tree.WriteTo(zipStream);
- zipStream.Close();
- }
- }
- }*/
- }
-
- protected bool BlockScan (NBT_Tag level, int val)
- {
- NBT_Tag blocks = level.findTagByName("Blocks");
- if (blocks == null || blocks.type != NBT_Type.TAG_BYTE_ARRAY) {
- return false;
- }
-
- NBT_ByteArray blocks_ary = blocks.value.toByteArray();
-
- for (int x = 0; x < 16; x++) {
- for (int z = 0; z < 16; z++) {
- for (int y = 0; y < 128; y++) {
- int index = BlockIndex(x, y, z);
- if (blocks_ary.data[index] == val) {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- int BlockIndex (int x, int y, int z)
- {
- return y + (z * 128 + x * 128 * 16);
- }
- }
-}
diff --git a/NBToolkit/NBToolkit/NBToolkit.csproj b/NBToolkit/NBToolkit/NBToolkit.csproj
index f6bd49c..2c491be 100644
--- a/NBToolkit/NBToolkit/NBToolkit.csproj
+++ b/NBToolkit/NBToolkit/NBToolkit.csproj
@@ -61,7 +61,6 @@
-
diff --git a/NBToolkit/NBToolkit/Replace.cs b/NBToolkit/NBToolkit/Replace.cs
index 88dd0a8..faff422 100644
--- a/NBToolkit/NBToolkit/Replace.cs
+++ b/NBToolkit/NBToolkit/Replace.cs
@@ -6,9 +6,10 @@ using NBT;
namespace NBToolkit
{
- public class ReplaceOptions : TKOptions
+ public class ReplaceOptions : TKOptions, IChunkFilterable
{
- private OptionSet filterOpt = null;
+ private OptionSet _filterOpt = null;
+ private ChunkFilter _chunkFilter = null;
public int? OPT_BEFORE = null;
public int? OPT_AFTER = null;
@@ -34,9 +35,10 @@ namespace NBToolkit
public int? OPT_NEIGHBOR_T = null;
public int? OPT_NEIGHBOR_B = null;
- public ReplaceOptions (string[] args) : base(args)
+ public ReplaceOptions ()
+ : base()
{
- filterOpt = new OptionSet()
+ _filterOpt = new OptionSet()
{
{ "b|before=", "Replace instances of block type {ID} with another block type",
v => OPT_BEFORE = Convert.ToInt32(v) % 256 },
@@ -48,25 +50,34 @@ namespace NBToolkit
v => { OPT_PROB = Convert.ToDouble(v);
OPT_PROB = Math.Max((double)OPT_PROB, 0.0);
OPT_PROB = Math.Min((double)OPT_PROB, 1.0); } },
- { "bxa=", "Update blocks with X-coord equal to or above {VAL}",
+ /*{ "bxa=", "Update blocks with X-coord equal to or above {VAL}",
v => BL_X_GE = Convert.ToInt32(v) },
{ "bxb=", "Update blocks with X-coord equal to or below {VAL}",
- v => BL_X_LE = Convert.ToInt32(v) },
- { "bxr=", "Update blocks with X-coord between {0:V1} and {1:V2} [inclusive]",
- (v1, v2) => { BL_X_GE = Convert.ToInt32(v1); BL_X_LE = Convert.ToInt32(v2); } },
- { "bya=", "Update blocks with Y-coord equal to or above {VAL}",
+ v => BL_X_LE = Convert.ToInt32(v) },*/
+ { "bxr|BlockXRange=", "Update blocks with X-coord between {0:V1} and {1:V2}, inclusive. V1 or V2 may be left blank.",
+ (v1, v2) => {
+ try { BL_X_GE = Convert.ToInt32(v1); } catch (FormatException) { }
+ try { BL_X_LE = Convert.ToInt32(v2); } catch (FormatException) { }
+ } },
+ /*{ "bya=", "Update blocks with Y-coord equal to or above {VAL}",
v => BL_Y_GE = Convert.ToInt32(v) },
{ "byb=", "Update blocks with Y-coord equal to or below {VAL}",
- v => BL_Y_LE = Convert.ToInt32(v) },
- { "byr=", "Update blocks with Y-coord between {0:V1} and {1:V2} [inclusive]",
- (v1, v2) => { BL_Y_GE = Convert.ToInt32(v1); BL_Y_LE = Convert.ToInt32(v2); } },
- { "bza=", "Update blocks with Z-coord equal to or above {VAL}",
+ v => BL_Y_LE = Convert.ToInt32(v) },*/
+ { "byr|BlockYRange=", "Update blocks with Y-coord between {0:V1} and {1:V2}, inclusive. V1 or V2 may be left blank",
+ (v1, v2) => {
+ try { BL_Y_GE = Convert.ToInt32(v1); } catch (FormatException) { }
+ try { BL_Y_LE = Convert.ToInt32(v2); } catch (FormatException) { }
+ } },
+ /*{ "bza=", "Update blocks with Z-coord equal to or above {VAL}",
v => BL_Z_GE = Convert.ToInt32(v) },
{ "bzb=", "Update blocks with Z-coord equal to or below {VAL}",
- v => BL_Z_LE = Convert.ToInt32(v) },
- { "bzr=", "Update blocks with Z-coord between {0:V1} and {1:V2} [inclusive]",
- (v1, v2) => { BL_Z_GE = Convert.ToInt32(v1); BL_Z_LE = Convert.ToInt32(v2); } },
- { "nb=", "Update blocks that have block type {ID} as any neighbor",
+ v => BL_Z_LE = Convert.ToInt32(v) },*/
+ { "bzr|BlockZRange=", "Update blocks with Z-coord between {0:V1} and {1:V2}, inclusive. V1 or V2 may be left blank",
+ (v1, v2) => {
+ try { BL_Z_GE = Convert.ToInt32(v1); } catch (FormatException) { }
+ try { BL_Z_LE = Convert.ToInt32(v2); } catch (FormatException) { }
+ } },
+ /*{ "nb=", "Update blocks that have block type {ID} as any neighbor",
v => OPT_NEIGHBOR = Convert.ToInt32(v) % 256 },
{ "nbs=", "Update blocks that have block type {ID} as any x/z neighbor",
v => OPT_NEIGHBOR_SIDE = Convert.ToInt32(v) % 256 },
@@ -81,10 +92,24 @@ namespace NBToolkit
{ "nbza=", "Update blocks that have block type {ID} as their west neighbor",
v => OPT_NEIGHBOR_W = Convert.ToInt32(v) % 256 },
{ "nbzb=", "Update blocks that have block type {ID} as their east neighbor",
- v => OPT_NEIGHBOR_E = Convert.ToInt32(v) % 256 },
+ v => OPT_NEIGHBOR_E = Convert.ToInt32(v) % 256 },*/
};
- filterOpt.Parse(args);
+ _chunkFilter = new ChunkFilter();
+ }
+
+ public ReplaceOptions (string[] args)
+ : this()
+ {
+ Parse(args);
+ }
+
+ public override void Parse (string[] args)
+ {
+ base.Parse(args);
+
+ _filterOpt.Parse(args);
+ _chunkFilter.Parse(args);
}
public override void PrintUsage ()
@@ -93,7 +118,10 @@ namespace NBToolkit
Console.WriteLine();
Console.WriteLine("Options for command 'replace':");
- filterOpt.WriteOptionDescriptions(Console.Out);
+ _filterOpt.WriteOptionDescriptions(Console.Out);
+
+ Console.WriteLine();
+ _chunkFilter.PrintUsage();
Console.WriteLine();
base.PrintUsage();
@@ -112,6 +140,11 @@ namespace NBToolkit
throw new TKOptionException();
}
}
+
+ public IChunkFilter GetChunkFilter ()
+ {
+ return _chunkFilter;
+ }
}
class Replace : TKFilter
@@ -127,41 +160,23 @@ namespace NBToolkit
public override void Run ()
{
- throw new NotImplementedException();
+ World world = new World(opt.OPT_WORLD);
+
+ int affectedChunks = 0;
+ foreach (Chunk chunk in new FilteredChunkList(world.GetChunkManager(), opt.GetChunkFilter())) {
+ affectedChunks++;
+
+ ApplyChunk(world, chunk);
+ chunk.Save();
+ }
+
+ Console.WriteLine("Affected Chunks: " + affectedChunks);
}
- public void ApplyChunk (NBT_Tree root)
+ public void ApplyChunk (World world, Chunk chunk)
{
- if (root == null || root.getRoot() == null) {
- return;
- }
-
- NBT_Tag level = root.getRoot().findTagByName("Level");
- if (level == null || level.type != NBT_Type.TAG_COMPOUND) {
- return;
- }
-
- NBT_Tag blocks = level.findTagByName("Blocks");
- if (blocks == null || blocks.type != NBT_Type.TAG_BYTE_ARRAY) {
- return;
- }
-
- NBT_Tag data = level.findTagByName("Data");
- if (data == null || data.type != NBT_Type.TAG_BYTE_ARRAY) {
- return;
- }
-
- NBT_Tag xPos = level.findTagByName("xPos");
- NBT_Tag zPos = level.findTagByName("zPos");
- if (xPos == null || zPos == null || xPos.type != NBT_Type.TAG_INT || zPos.type != NBT_Type.TAG_INT) {
- return;
- }
-
- int xBase = xPos.value.toInt().data * 16;
- int zBase = zPos.value.toInt().data * 16;
-
- NBT_ByteArray blocks_ary = blocks.value.toByteArray();
- NBT_ByteArray data_ary = data.value.toByteArray();
+ int xBase = chunk.X * BlockManager.CHUNK_XLEN;
+ int zBase = chunk.Z * BlockManager.CHUNK_ZLEN;
// Determine X range
int xmin = 0;
@@ -226,32 +241,26 @@ namespace NBToolkit
}
}
+ int lx = x & BlockManager.CHUNK_XMASK;
+ int ly = y & BlockManager.CHUNK_YMASK;
+ int lz = z & BlockManager.CHUNK_ZMASK;
+
// Attempt to replace block
- int index = BlockIndex(x, y, z);
- if (blocks_ary.data[index] == opt.OPT_BEFORE) {
- blocks_ary.data[index] = (byte)opt.OPT_AFTER;
+ int oldBlock = chunk.GetBlockID(lx , ly, lz);
+ if (oldBlock == opt.OPT_BEFORE) {
+ chunk.SetBlockID(lx, ly, lz, (int)opt.OPT_AFTER);
if (opt.OPT_VV) {
- Console.WriteLine("Replaced block at {0},{1},{2}", x, y, z);
+ Console.WriteLine("Replaced block at {0},{1},{2}", lx, ly, lz);
}
if (opt.OPT_DATA != null) {
- if (index % 2 == 0) {
- data_ary.data[index / 2] = (byte)((data_ary.data[index / 2] & 0xF0) | (int)opt.OPT_DATA);
- }
- else {
- data_ary.data[index / 2] = (byte)((data_ary.data[index / 2] & 0x0F) | ((int)opt.OPT_DATA << 4));
- }
+ chunk.SetBlockData(lx, ly, lz, (int)opt.OPT_DATA);
}
}
}
}
}
}
-
- int BlockIndex (int x, int y, int z)
- {
- return y + (z * 128 + x * 128 * 16);
- }
}
}
diff --git a/NBToolkit/NBToolkit/TKOptions.cs b/NBToolkit/NBToolkit/TKOptions.cs
index 67bb762..5d9796b 100644
--- a/NBToolkit/NBToolkit/TKOptions.cs
+++ b/NBToolkit/NBToolkit/TKOptions.cs
@@ -17,6 +17,7 @@ namespace NBToolkit
private OptionSet commonOpt = null;
public string OPT_WORLD = "";
+ public string OPT_REGION = "region";
// Verbosity
public bool OPT_V = false;
@@ -31,6 +32,8 @@ namespace NBToolkit
v => OPT_WORLD = v },
{ "h|help", "Print this help message",
v => OPT_HELP = true },
+ { "nether", "Update the Nether instead of the main region",
+ v => OPT_REGION = "DIM-1/region" },
{ "v", "Verbose output",
v => OPT_V = true },
{ "vv", "Very verbose output",
@@ -77,6 +80,14 @@ namespace NBToolkit
throw new TKOptionException();
}
+
+ if (!Directory.Exists(Path.Combine(OPT_WORLD, OPT_REGION))) {
+ Console.WriteLine("Error: The supplied world path does not contain region: " + OPT_REGION);
+ Console.WriteLine();
+ this.PrintUsage();
+
+ throw new TKOptionException();
+ }
}
}