Add "Location" loading, draw on map

This commit is contained in:
Adam
2016-08-21 17:03:10 -04:00
parent aa15feb091
commit e609fc83ba
10 changed files with 595 additions and 103 deletions

2
cache/pom.xml vendored
View File

@@ -101,7 +101,7 @@
<version>2.16</version> <version>2.16</version>
<configuration> <configuration>
<enableAssertions>true</enableAssertions> <enableAssertions>true</enableAssertions>
<argLine>-Xmx1024m</argLine> <argLine>-Xmx2048m</argLine>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>

View File

@@ -29,18 +29,23 @@
*/ */
package net.runelite.cache; package net.runelite.cache;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.awt.Color; import java.awt.Color;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.runelite.cache.definitions.ObjectDefinition;
import net.runelite.cache.definitions.OverlayDefinition; import net.runelite.cache.definitions.OverlayDefinition;
import net.runelite.cache.definitions.SpriteDefinition; import net.runelite.cache.definitions.SpriteDefinition;
import net.runelite.cache.definitions.TextureDefinition; import net.runelite.cache.definitions.TextureDefinition;
import net.runelite.cache.definitions.UnderlayDefinition; import net.runelite.cache.definitions.UnderlayDefinition;
import net.runelite.cache.definitions.loaders.ObjectLoader;
import net.runelite.cache.definitions.loaders.OverlayLoader; import net.runelite.cache.definitions.loaders.OverlayLoader;
import net.runelite.cache.definitions.loaders.SpriteLoader; import net.runelite.cache.definitions.loaders.SpriteLoader;
import net.runelite.cache.definitions.loaders.TextureLoader; import net.runelite.cache.definitions.loaders.TextureLoader;
@@ -50,7 +55,9 @@ import net.runelite.cache.fs.File;
import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Store; import net.runelite.cache.fs.Store;
import net.runelite.cache.io.InputStream; import net.runelite.cache.io.InputStream;
import net.runelite.cache.region.Location;
import net.runelite.cache.region.Region; import net.runelite.cache.region.Region;
import net.runelite.cache.util.XteaKeyManager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -59,14 +66,20 @@ public class MapImageDumper
private static final Logger logger = LoggerFactory.getLogger(MapImageDumper.class); private static final Logger logger = LoggerFactory.getLogger(MapImageDumper.class);
private static final int MAX_REGION = 32768; private static final int MAX_REGION = 32768;
private static final int MAP_SCENE_SPRITE_ID = 317; // magic sprite id map icons are in
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;
private final Store store; private final Store store;
private final List<UnderlayDefinition> underlays = new ArrayList<>(); private final List<UnderlayDefinition> underlays = new ArrayList<>();
private final List<OverlayDefinition> overlays = new ArrayList<>(); private final List<OverlayDefinition> overlays = new ArrayList<>();
private final List<TextureDefinition> textures = new ArrayList<>(); private final List<TextureDefinition> textures = new ArrayList<>();
private final List<SpriteDefinition> sprites = new ArrayList<>(); private final Multimap<Integer, SpriteDefinition> sprites = HashMultimap.create();
private final Map<SpriteDefinition, Integer> averageColors = new HashMap<>(); private final Map<SpriteDefinition, Integer> averageColors = new HashMap<>();
private final Map<SpriteDefinition, Image> scaledMapIcons = new HashMap<>();
private final Map<Integer, ObjectDefinition> objects = new HashMap<>();
private final List<Region> regions = new ArrayList<>(); private final List<Region> regions = new ArrayList<>();
private Region lowestX = null, lowestY = null; private Region lowestX = null, lowestY = null;
@@ -80,17 +93,19 @@ public class MapImageDumper
this.store = store; this.store = store;
} }
public BufferedImage[] buildImages() throws IOException public void load() throws IOException
{ {
BufferedImage[] images = new BufferedImage[Region.Z];
loadUnderlays(store); loadUnderlays(store);
loadOverlays(store); loadOverlays(store);
loadTextures(store); loadTextures(store);
loadSprites(store); loadSprites(store);
loadObjects(store);
loadRegions(store); loadRegions(store);
}
public BufferedImage drawMap(int z) throws IOException
{
int minX = lowestX.getBaseX(); int minX = lowestX.getBaseX();
int minY = lowestY.getBaseY(); int minY = lowestY.getBaseY();
@@ -100,12 +115,12 @@ public class MapImageDumper
int dimX = maxX - minX; int dimX = maxX - minX;
int dimY = maxY - minY; int dimY = maxY - minY;
logger.info("Map image dimensions: {}px x {}px", dimX, dimY); dimX *= MAP_SCALE;
dimY *= MAP_SCALE;
for (int i = 0; i < images.length; ++i) logger.info("Map image dimensions: {}px x {}px, {}px per map square ({} MB)", dimX, dimY, MAP_SCALE, (dimX * dimY / 1024 / 1024));
{
images[i] = new BufferedImage(dimX, dimY, BufferedImage.TYPE_INT_RGB); BufferedImage image = new BufferedImage(dimX, dimY, BufferedImage.TYPE_INT_RGB);
}
for (Region region : regions) for (Region region : regions)
{ {
@@ -119,102 +134,176 @@ public class MapImageDumper
// region has the greaters y, so invert // region has the greaters y, so invert
int drawBaseY = highestY.getBaseY() - baseY; int drawBaseY = highestY.getBaseY() - baseY;
for (int z = 0; z < Region.Z; ++z) Graphics2D graphics = image.createGraphics();
for (int x = 0; x < Region.X; ++x)
{ {
BufferedImage image = images[z]; int drawX = drawBaseX + x;
Graphics2D graphics = image.createGraphics();
for (int x = 0; x < Region.X; ++x) for (int y = 0; y < Region.Y; ++y)
{ {
int drawX = drawBaseX + x; int drawY = drawBaseY + (Region.Y - 1 - y);
for (int y = 0; y < Region.Y; ++y) int overlayId = region.getOverlayId(z, x, y) - 1;
int underlayId = region.getUnderlayId(z, x, y) - 1;
int rgb = 0;
if (overlayId > -1)
{ {
int drawY = drawBaseY + (Region.Y - 1 - y); OverlayDefinition overlay = findOverlay(overlayId);
if (!overlay.isHideUnderlay() && underlayId > -1)
int overlayId = region.getOverlayId(z, x, y) - 1;
int underlayId = region.getUnderlayId(z, x, y) - 1;
int rgb = 0;
if (overlayId > -1)
{
OverlayDefinition overlay = findOverlay(overlayId);
if (!overlay.isHideUnderlay() && underlayId > -1)
{
UnderlayDefinition underlay = findUnderlay(underlayId);
rgb = underlay.getColor();
}
else
{
rgb = overlay.getRgbColor();
}
if (overlay.getSecondaryRgbColor() > -1)
{
rgb = overlay.getSecondaryRgbColor();
}
if (overlay.getTexture() > -1)
{
TextureDefinition texture = findTexture(overlay.getTexture());
assert texture.getFileIds().length == 1;
SpriteDefinition sprite = findSprite(texture.getFileIds()[0], 0);
assert sprite != null;
rgb = averageColors.get(sprite);
}
}
else if (underlayId > -1)
{ {
UnderlayDefinition underlay = findUnderlay(underlayId); UnderlayDefinition underlay = findUnderlay(underlayId);
rgb = underlay.getColor(); rgb = underlay.getColor();
} }
else
{
rgb = overlay.getRgbColor();
}
image.setRGB(drawX, drawY, rgb); if (overlay.getSecondaryRgbColor() > -1)
{
rgb = overlay.getSecondaryRgbColor();
}
if (overlay.getTexture() > -1)
{
TextureDefinition texture = findTexture(overlay.getTexture());
assert texture.getFileIds().length == 1;
SpriteDefinition sprite = findSprite(texture.getFileIds()[0], 0);
assert sprite != null;
rgb = averageColors.get(sprite);
}
}
else if (underlayId > -1)
{
UnderlayDefinition underlay = findUnderlay(underlayId);
rgb = underlay.getColor();
} }
}
if (labelRegions) drawMapSquare(image, drawX, drawY, rgb);
{
graphics.setColor(Color.WHITE);
graphics.drawString(baseX + "," + baseY, drawBaseX, drawBaseY + graphics.getFontMetrics().getHeight());
} }
if (outlineRegions)
{
graphics.setColor(Color.WHITE);
graphics.drawRect(drawBaseX, drawBaseY, Region.X, Region.Y);
}
graphics.dispose();
} }
drawLocations(graphics, region, z, drawBaseX, drawBaseY);
if (labelRegions)
{
graphics.setColor(Color.WHITE);
graphics.drawString(baseX + "," + baseY, drawBaseX * MAP_SCALE, drawBaseY * MAP_SCALE + graphics.getFontMetrics().getHeight());
}
if (outlineRegions)
{
graphics.setColor(Color.WHITE);
graphics.drawRect(drawBaseX * MAP_SCALE, drawBaseY * MAP_SCALE, Region.X * MAP_SCALE, Region.Y * MAP_SCALE);
}
graphics.dispose();
} }
return images; return image;
}
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);
}
}
}
private void drawLocations(Graphics2D graphics, Region region, int z, int drawBaseX, int drawBaseY)
{
for (Location location : region.getLocations())
{
// regions include locations on all planes, so check
if (location.getPosition().getZ() != z)
{
continue;
}
ObjectDefinition od = findObject(location.getId());
assert od != null;
int localX = location.getPosition().getX() - region.getBaseX();
int localY = location.getPosition().getY() - region.getBaseY();
int drawX = drawBaseX + localX;
int drawY = drawBaseY + (Region.Y - 1 - localY);
if (od.getMapSceneID() != -1)
{
SpriteDefinition sprite = findSprite(MAP_SCENE_SPRITE_ID, od.getMapSceneID());
assert sprite != null;
if (sprite.getHeight() <= 0 || sprite.getWidth() <= 0)
{
continue;
}
Image spriteImage = scaledMapIcons.get(sprite);
graphics.drawImage(spriteImage, drawX * MAP_SCALE, drawY * MAP_SCALE, null);
}
}
} }
private void loadRegions(Store store) throws IOException private void loadRegions(Store store) throws IOException
{ {
Index index = store.getIndex(IndexType.MAPS); Index index = store.getIndex(IndexType.MAPS);
XteaKeyManager keyManager = index.getXteaManager();
for (int i = 0; i < MAX_REGION; ++i) for (int i = 0; i < MAX_REGION; ++i)
{ {
Region region = new Region(i); int x = i >> 8;
int y = i & 0xFF;
Archive map = index.findArchiveByName("m" + (i >> 8) + "_" + (i & 0xFF)); Archive map = index.findArchiveByName("m" + x + "_" + y);
if (map == null) Archive land = index.findArchiveByName("l" + x + "_" + y);
assert (map == null) == (land == null);
if (map == null || land == null)
{ {
continue; continue;
} }
assert map.getFiles().size() == 1; assert map.getFiles().size() == 1;
assert land.getFiles().size() == 1;
map.decompressAndLoad(null); map.decompressAndLoad(null);
byte[] data = map.getFiles().get(0).getContents(); byte[] data = map.getFiles().get(0).getContents();
Region region = new Region(i);
region.loadTerrain(data); 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); regions.add(region);
if (lowestX == null || region.getBaseX() < lowestX.getBaseX()) if (lowestX == null || region.getBaseX() < lowestX.getBaseX())
@@ -347,18 +436,34 @@ public class MapImageDumper
for (SpriteDefinition sprite : sprites) for (SpriteDefinition sprite : sprites)
{ {
this.sprites.add(sprite); this.sprites.put(sprite.getId(), sprite);
averageColors.put(sprite, getAverageColor(sprite.getPixels())); averageColors.put(sprite, getAverageColor(sprite.getPixels()));
if (sprite.getId() == MAP_SCENE_SPRITE_ID)
{
if (sprite.getHeight() <= 0 || sprite.getWidth() <= 0)
{
continue;
}
BufferedImage spriteImage = new BufferedImage(sprite.getWidth(), sprite.getHeight(), BufferedImage.TYPE_INT_ARGB);
spriteImage.setRGB(0, 0, sprite.getWidth(), sprite.getHeight(), sprite.getPixels(), 0, sprite.getWidth());
// scale image down so it fits
Image scaledImage = spriteImage.getScaledInstance(MAPICON_MAX_WIDTH, MAPICON_MAX_HEIGHT, 0);
scaledMapIcons.put(sprite, scaledImage);
}
} }
} }
} }
private SpriteDefinition findSprite(int id, int frame) private SpriteDefinition findSprite(int id, int frame)
{ {
for (SpriteDefinition def : sprites) for (SpriteDefinition def : sprites.get(id))
{ {
if (def.getId() == id && def.getFrame() == frame) if (def.getFrame() == frame)
{ {
return def; return def;
} }
@@ -394,6 +499,25 @@ public class MapImageDumper
return color.getRGB(); return color.getRGB();
} }
private void loadObjects(Store store)
{
Index index = store.getIndex(IndexType.CONFIGS);
Archive archive = index.getArchive(ConfigType.OBJECT.getId());
ObjectLoader loader = new ObjectLoader();
for (File f : archive.getFiles())
{
ObjectDefinition def = loader.load(f.getFileId(), f.getContents());
objects.put(def.getId(), def);
}
}
private ObjectDefinition findObject(int id)
{
return objects.get(id);
}
public boolean isLabelRegions() public boolean isLabelRegions()
{ {
return labelRegions; return labelRegions;

View File

@@ -126,15 +126,7 @@ public class InputStream extends java.io.InputStream
public byte peek() public byte peek()
{ {
int position = buffer.position(); return buffer.get(buffer.position());
try
{
return buffer.get();
}
finally
{
buffer.position(position);
}
} }
public int readBigSmart() public int readBigSmart()
@@ -144,8 +136,14 @@ public class InputStream extends java.io.InputStream
public int readShortSmart() public int readShortSmart()
{ {
int var2 = this.peek() & 0xFF; int peek = this.peek() & 0xFF;
return var2 < 128 ? this.readUnsignedByte() - 64 : this.readUnsignedShort() - 0xc000; return peek < 128 ? this.readUnsignedByte() - 64 : this.readUnsignedShort() - 0xc000;
}
public int readUnsignedShortSmart()
{
int peek = this.peek() & 0xFF;
return peek < 128 ? this.readUnsignedByte() : this.readUnsignedShort() - 0x8000;
} }
public String readString() public String readString()

View File

@@ -0,0 +1,121 @@
/*
* 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.util.Objects;
public class Location
{
private final int id;
private final int type;
private final int orientation;
private final Position position;
public Location(int id, int type, int orientation, Position position)
{
this.id = id;
this.type = type;
this.orientation = orientation;
this.position = position;
}
@Override
public String toString()
{
return "GamwObject{" + "id=" + id + ", type=" + type + ", orientation=" + orientation + ", position=" + position + '}';
}
@Override
public int hashCode()
{
int hash = 5;
hash = 83 * hash + this.id;
hash = 83 * hash + this.type;
hash = 83 * hash + this.orientation;
hash = 83 * hash + Objects.hashCode(this.position);
return hash;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final Location other = (Location) obj;
if (this.id != other.id)
{
return false;
}
if (this.type != other.type)
{
return false;
}
if (this.orientation != other.orientation)
{
return false;
}
if (!Objects.equals(this.position, other.position))
{
return false;
}
return true;
}
public int getId()
{
return id;
}
public int getType()
{
return type;
}
public int getOrientation()
{
return orientation;
}
public Position getPosition()
{
return position;
}
}

View File

@@ -0,0 +1,107 @@
/*
* 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;
public class Position
{
private final int x;
private final int y;
private final int z;
public Position(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
@Override
public String toString()
{
return "Position{" + "x=" + x + ", y=" + y + ", z=" + z + '}';
}
@Override
public int hashCode()
{
int hash = 7;
hash = 67 * hash + this.x;
hash = 67 * hash + this.y;
hash = 67 * hash + this.z;
return hash;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final Position other = (Position) obj;
if (this.x != other.x)
{
return false;
}
if (this.y != other.y)
{
return false;
}
if (this.z != other.z)
{
return false;
}
return true;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public int getZ()
{
return z;
}
}

View File

@@ -21,6 +21,8 @@
*/ */
package net.runelite.cache.region; package net.runelite.cache.region;
import java.util.ArrayList;
import java.util.List;
import net.runelite.cache.io.InputStream; import net.runelite.cache.io.InputStream;
/** /**
@@ -46,6 +48,8 @@ public class Region
private final byte[][][] overlayRotations = new byte[Z][X][Y]; private final byte[][][] overlayRotations = new byte[Z][X][Y];
private final byte[][][] underlayIds = new byte[Z][X][Y]; private final byte[][][] underlayIds = new byte[Z][X][Y];
private final List<Location> locations = new ArrayList<>();
public Region(int id) public Region(int id)
{ {
this.regionID = id; this.regionID = id;
@@ -118,6 +122,37 @@ public class Region
} }
} }
public void loadLocations(byte[] b)
{
InputStream buf = new InputStream(b);
int id = -1;
int idOffset;
while ((idOffset = buf.readUnsignedShortSmart()) != 0)
{
id += idOffset;
int position = 0;
int positionOffset;
while ((positionOffset = buf.readUnsignedShortSmart()) != 0)
{
position += positionOffset - 1;
int localY = position & 0x3F;
int localX = position >> 6 & 0x3F;
int height = position >> 12 & 0x3;
int attributes = buf.readUnsignedByte();
int type = attributes >> 2;
int orientation = attributes & 0x3;
locations.add(new Location(id, type, orientation, new Position(getBaseX() + localX, getBaseY() + localY, height)));
}
}
}
public int getRegionID() public int getRegionID()
{ {
return regionID; return regionID;
@@ -162,4 +197,9 @@ public class Region
{ {
return underlayIds[z][x][y] & 0xFF; return underlayIds[z][x][y] & 0xFF;
} }
public List<Location> getLocations()
{
return locations;
}
} }

View File

@@ -27,15 +27,19 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package net.runelite.cache; package net.runelite.cache;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.runelite.cache.fs.Archive; import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Store; import net.runelite.cache.fs.Store;
import net.runelite.cache.region.Region;
import net.runelite.cache.util.XteaKeyManager; import net.runelite.cache.util.XteaKeyManager;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@@ -48,12 +52,15 @@ public class MapDumperTest
private static final Logger logger = LoggerFactory.getLogger(MapDumperTest.class); private static final Logger logger = LoggerFactory.getLogger(MapDumperTest.class);
private static final int MAX_REGIONS = 32768; private static final int MAX_REGIONS = 32768;
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
@Rule @Rule
public TemporaryFolder folder = StoreLocation.getTemporaryFolder(); public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
@Test private final List<Region> regions = new ArrayList<>();
public void dump() throws IOException
//@Test
public void dumpRaw() throws IOException
{ {
File base = StoreLocation.LOCATION, File base = StoreLocation.LOCATION,
outDir = folder.newFolder(); outDir = folder.newFolder();
@@ -68,17 +75,19 @@ public class MapDumperTest
for (int i = 0; i < MAX_REGIONS; i++) for (int i = 0; i < MAX_REGIONS; i++)
{ {
int[] keys = keyManager.getKeys(i); int[] keys = keyManager.getKeys(i);
int x = i >> 8; int x = i >> 8;
int y = i & 0xFF; int y = i & 0xFF;
Archive map = index.findArchiveByName("m" + x + "_" + y); Archive map = index.findArchiveByName("m" + x + "_" + y);
Archive land = index.findArchiveByName("l" + x + "_" + y); Archive land = index.findArchiveByName("l" + x + "_" + y);
assert (map == null) == (land == null); assert (map == null) == (land == null);
if (map == null || land == null) if (map == null || land == null)
{
continue; continue;
}
assert map.getFiles().size() == 1; assert map.getFiles().size() == 1;
assert land.getFiles().size() == 1; assert land.getFiles().size() == 1;
@@ -99,10 +108,12 @@ public class MapDumperTest
} }
catch (IOException ex) catch (IOException ex)
{ {
logger.warn("Unable to decompress and load land " + x + "/" + y + " (bad keys?)", ex); logger.info("Unable to decompress and load land " + x + "," + y + " (bad keys?)", ex);
continue; continue;
} }
logger.info("Decrypted region {} coords {},{}", i, x, y);
data = land.getFiles().get(0).getContents(); data = land.getFiles().get(0).getContents();
Files.write(data, new File(outDir, "l" + x + "_" + y + ".dat")); Files.write(data, new File(outDir, "l" + x + "_" + y + ".dat"));
@@ -111,4 +122,79 @@ public class MapDumperTest
} }
} }
private void loadRegions(Store store) throws IOException
{
Index index = store.getIndex(IndexType.MAPS);
XteaKeyManager keyManager = index.getXteaManager();
for (int i = 0; i < MAX_REGIONS; ++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);
}
catch (IOException ex)
{
continue;
}
data = land.getFiles().get(0).getContents();
region.loadLocations(data);
}
regions.add(region);
}
}
@Test
public void dunpJson() throws IOException
{
File base = StoreLocation.LOCATION,
outDir = folder.newFolder();
try (Store store = new Store(base))
{
store.load();
loadRegions(store);
for (Region region : regions)
{
if (region.getLocations().isEmpty())
{
continue;
}
Files.write(gson.toJson(region).getBytes(), new File(outDir, region.getBaseX() + "_" + region.getBaseY() + ".json"));
}
}
logger.info("Dumped regions to {}", outDir);
}
} }

View File

@@ -34,6 +34,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import net.runelite.cache.fs.Store; import net.runelite.cache.fs.Store;
import net.runelite.cache.region.Region;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
@@ -51,25 +52,24 @@ public class MapImageDumperTest
public void extract() throws IOException public void extract() throws IOException
{ {
File base = StoreLocation.LOCATION, File base = StoreLocation.LOCATION,
outFile = folder.newFolder(); outDir = folder.newFolder();
BufferedImage[] images;
try (Store store = new Store(base)) try (Store store = new Store(base))
{ {
store.load(); store.load();
MapImageDumper dumper = new MapImageDumper(store); MapImageDumper dumper = new MapImageDumper(store);
images = dumper.buildImages(); dumper.load();
}
int i = 0; for (int i = 0; i < Region.Z; ++i)
for (BufferedImage image : images) {
{ BufferedImage image = dumper.drawMap(i);
File imageFile = new File(outFile, "img-" + i++ + ".png");
ImageIO.write(image, "png", imageFile); File imageFile = new File(outDir, "img-" + i + ".png");
logger.info("Wrote image {}", imageFile);
ImageIO.write(image, "png", imageFile);
logger.info("Wrote image {}", imageFile);
}
} }
} }
} }

View File

@@ -40,7 +40,10 @@ public class StoreLocation
{ {
private static final Logger logger = LoggerFactory.getLogger(StoreLocation.class); private static final Logger logger = LoggerFactory.getLogger(StoreLocation.class);
private static final String TMP_DIR = "d:/temp";
public static File LOCATION; public static File LOCATION;
private static File TMP;
static static
{ {
@@ -55,11 +58,23 @@ public class StoreLocation
File tmp = new File("d:/temp"); File tmp = new File("d:/temp");
if (tmp.exists() || tmp.mkdir()) if (tmp.exists() || tmp.mkdir())
System.setProperty("java.io.tmpdir", "d:/temp"); {
System.setProperty("java.io.tmpdir", TMP_DIR);
TMP = tmp;
}
} }
public static TemporaryFolder getTemporaryFolder() public static TemporaryFolder getTemporaryFolder()
{ {
return new TemporaryFolder(); return new TemporaryFolder()
{
@Override
public void after()
{
// don't cleanup if using local tmpdir
if (TMP == null)
super.after();
}
};
} }
} }

View File

@@ -91,6 +91,7 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.19</version> <version>2.19</version>
<configuration> <configuration>
<enableAssertions>true</enableAssertions>
<argLine>-Xmx512m</argLine> <argLine>-Xmx512m</argLine>
</configuration> </configuration>
</plugin> </plugin>