Add height map dumper

This commit is contained in:
Adam
2016-12-25 20:48:34 -05:00
parent 6ac4116d70
commit dfad8c087a
4 changed files with 405 additions and 101 deletions

View File

@@ -0,0 +1,153 @@
/*
* Copyright (c) 2016, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Adam <Adam@sigterm.info>
* 4. Neither the name of the Adam <Adam@sigterm.info> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Adam <Adam@sigterm.info> ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Adam <Adam@sigterm.info> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.cache;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import net.runelite.cache.fs.Store;
import net.runelite.cache.region.Region;
import net.runelite.cache.region.RegionLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HeightMapDumper
{
private static final Logger logger = LoggerFactory.getLogger(HeightMapDumper.class);
private static final int MAP_SCALE = 1;
private final Store store;
private RegionLoader regionLoader;
public HeightMapDumper(Store store)
{
this.store = store;
}
public void load() throws IOException
{
regionLoader = new RegionLoader();
regionLoader.loadRegions(store);
regionLoader.calculateBounds();
}
public BufferedImage drawHeightMap(int z)
{
int minX = regionLoader.getLowestX().getBaseX();
int minY = regionLoader.getLowestY().getBaseY();
int maxX = regionLoader.getHighestX().getBaseX() + Region.X;
int maxY = regionLoader.getHighestY().getBaseY() + Region.Y;
int dimX = maxX - minX;
int dimY = maxY - minY;
dimX *= MAP_SCALE;
dimY *= MAP_SCALE;
logger.info("Map image dimensions: {}px x {}px, {}px per map square ({} MB)", dimX, dimY, MAP_SCALE, (dimX * dimY / 1024 / 1024));
BufferedImage image = new BufferedImage(dimX, dimY, BufferedImage.TYPE_INT_RGB);
draw(image, z);
return image;
}
private void draw(BufferedImage image, int z)
{
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
for (int x = 0; x < Region.X; ++x)
{
int drawX = drawBaseX + x;
for (int y = 0; y < Region.Y; ++y)
{
int drawY = drawBaseY + (Region.Y - 1 - y);
int height = region.getTileHeight(z, x, y);
if (height > max)
{
max = height;
}
if (height < min)
{
min = height;
}
int rgb = toColor(height);
drawMapSquare(image, drawX, drawY, rgb);
}
}
}
System.out.println("max " + max);
System.out.println("min " + min);
}
private int toColor(int height)
{
// height seems to be between -2040 and 0, inclusive
height = -height;
// Convert to between 0 and 1
float color = (float) height / 2040f;
assert color >= 0.0f && color <= 1.0f;
return new Color(color, color, color).getRGB();
}
private void drawMapSquare(BufferedImage image, int x, int y, int rgb)
{
x *= MAP_SCALE;
y *= MAP_SCALE;
for (int i = 0; i < MAP_SCALE; ++i)
{
for (int j = 0; j < MAP_SCALE; ++j)
{
image.setRGB(x + i, y + j, rgb);
}
}
}
}

View File

@@ -57,8 +57,8 @@ import net.runelite.cache.fs.Store;
import net.runelite.cache.io.InputStream;
import net.runelite.cache.region.Location;
import net.runelite.cache.region.Region;
import net.runelite.cache.region.RegionLoader;
import net.runelite.cache.util.Djb2;
import net.runelite.cache.util.XteaKeyManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,7 +66,6 @@ public class MapImageDumper
{
private static final Logger logger = LoggerFactory.getLogger(MapImageDumper.class);
private static final int MAX_REGION = 32768;
private static final int MAP_SCALE = 2; // this squared is the number of pixels per map square
private static final int MAPICON_MAX_WIDTH = 5; // scale minimap icons down to this size so they fit..
private static final int MAPICON_MAX_HEIGHT = 6;
@@ -82,9 +81,7 @@ public class MapImageDumper
private final Map<Integer, ObjectDefinition> objects = new HashMap<>();
private final Map<Integer, Image> mapFunctions = new HashMap<>(); // quest, water, etc
private final List<Region> regions = new ArrayList<>();
private Region lowestX = null, lowestY = null;
private Region highestX = null, highestY = null;
private RegionLoader regionLoader;
private boolean labelRegions;
private boolean outlineRegions;
@@ -107,11 +104,11 @@ public class MapImageDumper
public BufferedImage drawMap(int z) throws IOException
{
int minX = lowestX.getBaseX();
int minY = lowestY.getBaseY();
int minX = regionLoader.getLowestX().getBaseX();
int minY = regionLoader.getLowestY().getBaseY();
int maxX = highestX.getBaseX() + Region.X;
int maxY = highestY.getBaseY() + Region.Y;
int maxX = regionLoader.getHighestX().getBaseX() + Region.X;
int maxY = regionLoader.getHighestY().getBaseY() + Region.Y;
int dimX = maxX - minX;
int dimY = maxY - minY;
@@ -127,17 +124,17 @@ public class MapImageDumper
drawOverlay(image, z);
// objects
for (Region region : regions)
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - lowestX.getBaseX();
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greaters y, so invert
int drawBaseY = highestY.getBaseY() - baseY;
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
Graphics2D graphics = image.createGraphics();
@@ -147,17 +144,17 @@ public class MapImageDumper
}
// map icons
for (Region region : regions)
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - lowestX.getBaseX();
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greaters y, so invert
int drawBaseY = highestY.getBaseY() - baseY;
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
Graphics2D graphics = image.createGraphics();
@@ -184,17 +181,17 @@ public class MapImageDumper
private void drawUnderlay(BufferedImage image, int z)
{
// pass 1
for (Region region : regions)
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - lowestX.getBaseX();
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greaters y, so invert
int drawBaseY = highestY.getBaseY() - baseY;
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
for (int x = 0; x < Region.X; ++x)
{
@@ -231,17 +228,17 @@ public class MapImageDumper
private void drawOverlay(BufferedImage image, int z)
{
for (Region region : regions)
for (Region region : regionLoader.getRegions())
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - lowestX.getBaseX();
int drawBaseX = baseX - regionLoader.getLowestX().getBaseX();
// to pixel Y. top most y is 0, but the top most
// region has the greaters y, so invert
int drawBaseY = highestY.getBaseY() - baseY;
// region has the greatest y, so invert
int drawBaseY = regionLoader.getHighestY().getBaseY() - baseY;
for (int x = 0; x < Region.X; ++x)
{
@@ -354,83 +351,14 @@ public class MapImageDumper
private void loadRegions(Store store) throws IOException
{
Index index = store.getIndex(IndexType.MAPS);
XteaKeyManager keyManager = index.getXteaManager();
regionLoader = new RegionLoader();
regionLoader.loadRegions(store);
regionLoader.calculateBounds();
for (int i = 0; i < MAX_REGION; ++i)
{
int x = i >> 8;
int y = i & 0xFF;
Archive map = index.findArchiveByName("m" + x + "_" + y);
Archive land = index.findArchiveByName("l" + x + "_" + y);
assert (map == null) == (land == null);
if (map == null || land == null)
{
continue;
}
assert map.getFiles().size() == 1;
assert land.getFiles().size() == 1;
map.decompressAndLoad(null);
byte[] data = map.getFiles().get(0).getContents();
Region region = new Region(i);
region.loadTerrain(data);
int[] keys = keyManager.getKeys(i);
if (keys != null)
{
try
{
land.decompressAndLoad(keys);
data = land.getFiles().get(0).getContents();
region.loadLocations(data);
}
catch (IOException ex)
{
logger.debug("Can't decrypt region " + i, ex);
}
}
regions.add(region);
if (lowestX == null || region.getBaseX() < lowestX.getBaseX())
{
lowestX = region;
}
if (highestX == null || region.getBaseX() > highestX.getBaseX())
{
highestX = region;
}
if (lowestY == null || region.getBaseY() < lowestY.getBaseY())
{
lowestY = region;
}
if (highestY == null || region.getBaseY() > highestY.getBaseY())
{
highestY = region;
}
}
assert lowestX != null;
assert lowestY != null;
assert highestX != null;
assert highestY != null;
logger.info("North most region: {}", lowestY.getBaseY());
logger.info("South most region: {}", highestY.getBaseY());
logger.info("West most region: {}", lowestX.getBaseX());
logger.info("East most region: {}", highestX.getBaseX());
logger.info("North most region: {}", regionLoader.getLowestY().getBaseY());
logger.info("South most region: {}", regionLoader.getHighestY().getBaseY());
logger.info("West most region: {}", regionLoader.getLowestX().getBaseX());
logger.info("East most region: {}", regionLoader.getHighestX().getBaseX());
}
private void loadUnderlays(Store store)

View File

@@ -0,0 +1,153 @@
/*
* Copyright (c) 2016, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Adam <Adam@sigterm.info>
* 4. Neither the name of the Adam <Adam@sigterm.info> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Adam <Adam@sigterm.info> ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Adam <Adam@sigterm.info> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.cache.region;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.runelite.cache.IndexType;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Store;
import net.runelite.cache.util.XteaKeyManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RegionLoader
{
private static final Logger logger = LoggerFactory.getLogger(RegionLoader.class);
private static final int MAX_REGION = 32768;
private final List<Region> regions = new ArrayList<>();
private Region lowestX = null, lowestY = null;
private Region highestX = null, highestY = null;
public void loadRegions(Store store) throws IOException
{
Index index = store.getIndex(IndexType.MAPS);
XteaKeyManager keyManager = index.getXteaManager();
for (int i = 0; i < MAX_REGION; ++i)
{
int x = i >> 8;
int y = i & 0xFF;
Archive map = index.findArchiveByName("m" + x + "_" + y);
Archive land = index.findArchiveByName("l" + x + "_" + y);
assert (map == null) == (land == null);
if (map == null || land == null)
{
continue;
}
assert map.getFiles().size() == 1;
assert land.getFiles().size() == 1;
map.decompressAndLoad(null);
byte[] data = map.getFiles().get(0).getContents();
Region region = new Region(i);
region.loadTerrain(data);
int[] keys = keyManager.getKeys(i);
if (keys != null)
{
try
{
land.decompressAndLoad(keys);
data = land.getFiles().get(0).getContents();
region.loadLocations(data);
}
catch (IOException ex)
{
logger.debug("Can't decrypt region " + i, ex);
}
}
regions.add(region);
}
}
public void calculateBounds()
{
for (Region region : regions)
{
if (lowestX == null || region.getBaseX() < lowestX.getBaseX())
{
lowestX = region;
}
if (highestX == null || region.getBaseX() > highestX.getBaseX())
{
highestX = region;
}
if (lowestY == null || region.getBaseY() < lowestY.getBaseY())
{
lowestY = region;
}
if (highestY == null || region.getBaseY() > highestY.getBaseY())
{
highestY = region;
}
}
}
public List<Region> getRegions()
{
return regions;
}
public Region getLowestX()
{
return lowestX;
}
public Region getLowestY()
{
return lowestY;
}
public Region getHighestX()
{
return highestX;
}
public Region getHighestY()
{
return highestY;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (c) 2016, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Adam <Adam@sigterm.info>
* 4. Neither the name of the Adam <Adam@sigterm.info> nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY Adam <Adam@sigterm.info> ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Adam <Adam@sigterm.info> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.cache;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import net.runelite.cache.fs.Store;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HeightMapDumperTest
{
private static final Logger logger = LoggerFactory.getLogger(HeightMapDumperTest.class);
@Rule
public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
//@Test
public void extract() throws IOException
{
File base = StoreLocation.LOCATION,
outDir = folder.newFolder();
try (Store store = new Store(base))
{
store.load();
HeightMapDumper dumper = new HeightMapDumper(store);
dumper.load();
BufferedImage image = dumper.drawHeightMap(0);
File imageFile = new File(outDir, "heightmap-0.png");
ImageIO.write(image, "png", imageFile);
logger.info("Wrote image {}", imageFile);
}
}
}