Add "Location" loading, draw on map
This commit is contained in:
2
cache/pom.xml
vendored
2
cache/pom.xml
vendored
@@ -101,7 +101,7 @@
|
||||
<version>2.16</version>
|
||||
<configuration>
|
||||
<enableAssertions>true</enableAssertions>
|
||||
<argLine>-Xmx1024m</argLine>
|
||||
<argLine>-Xmx2048m</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
||||
@@ -29,18 +29,23 @@
|
||||
*/
|
||||
package net.runelite.cache;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.runelite.cache.definitions.ObjectDefinition;
|
||||
import net.runelite.cache.definitions.OverlayDefinition;
|
||||
import net.runelite.cache.definitions.SpriteDefinition;
|
||||
import net.runelite.cache.definitions.TextureDefinition;
|
||||
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.SpriteLoader;
|
||||
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.Store;
|
||||
import net.runelite.cache.io.InputStream;
|
||||
import net.runelite.cache.region.Location;
|
||||
import net.runelite.cache.region.Region;
|
||||
import net.runelite.cache.util.XteaKeyManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -59,14 +66,20 @@ public class MapImageDumper
|
||||
private static final Logger logger = LoggerFactory.getLogger(MapImageDumper.class);
|
||||
|
||||
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 List<UnderlayDefinition> underlays = new ArrayList<>();
|
||||
private final List<OverlayDefinition> overlays = 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, Image> scaledMapIcons = new HashMap<>();
|
||||
private final Map<Integer, ObjectDefinition> objects = new HashMap<>();
|
||||
|
||||
private final List<Region> regions = new ArrayList<>();
|
||||
private Region lowestX = null, lowestY = null;
|
||||
@@ -80,17 +93,19 @@ public class MapImageDumper
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public BufferedImage[] buildImages() throws IOException
|
||||
public void load() throws IOException
|
||||
{
|
||||
BufferedImage[] images = new BufferedImage[Region.Z];
|
||||
|
||||
loadUnderlays(store);
|
||||
loadOverlays(store);
|
||||
loadTextures(store);
|
||||
loadSprites(store);
|
||||
loadObjects(store);
|
||||
|
||||
loadRegions(store);
|
||||
}
|
||||
|
||||
public BufferedImage drawMap(int z) throws IOException
|
||||
{
|
||||
int minX = lowestX.getBaseX();
|
||||
int minY = lowestY.getBaseY();
|
||||
|
||||
@@ -100,12 +115,12 @@ public class MapImageDumper
|
||||
int dimX = maxX - minX;
|
||||
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)
|
||||
{
|
||||
images[i] = new BufferedImage(dimX, dimY, BufferedImage.TYPE_INT_RGB);
|
||||
}
|
||||
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);
|
||||
|
||||
for (Region region : regions)
|
||||
{
|
||||
@@ -119,102 +134,176 @@ public class MapImageDumper
|
||||
// region has the greaters y, so invert
|
||||
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];
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
int drawX = drawBaseX + x;
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
OverlayDefinition overlay = findOverlay(overlayId);
|
||||
if (!overlay.isHideUnderlay() && underlayId > -1)
|
||||
{
|
||||
UnderlayDefinition underlay = findUnderlay(underlayId);
|
||||
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)
|
||||
{
|
||||
graphics.setColor(Color.WHITE);
|
||||
graphics.drawString(baseX + "," + baseY, drawBaseX, drawBaseY + graphics.getFontMetrics().getHeight());
|
||||
drawMapSquare(image, drawX, drawY, rgb);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
Index index = store.getIndex(IndexType.MAPS);
|
||||
XteaKeyManager keyManager = index.getXteaManager();
|
||||
|
||||
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));
|
||||
if (map == null)
|
||||
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())
|
||||
@@ -347,18 +436,34 @@ public class MapImageDumper
|
||||
|
||||
for (SpriteDefinition sprite : sprites)
|
||||
{
|
||||
this.sprites.add(sprite);
|
||||
this.sprites.put(sprite.getId(), sprite);
|
||||
|
||||
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)
|
||||
{
|
||||
for (SpriteDefinition def : sprites)
|
||||
for (SpriteDefinition def : sprites.get(id))
|
||||
{
|
||||
if (def.getId() == id && def.getFrame() == frame)
|
||||
if (def.getFrame() == frame)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
@@ -394,6 +499,25 @@ public class MapImageDumper
|
||||
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()
|
||||
{
|
||||
return labelRegions;
|
||||
|
||||
@@ -126,15 +126,7 @@ public class InputStream extends java.io.InputStream
|
||||
|
||||
public byte peek()
|
||||
{
|
||||
int position = buffer.position();
|
||||
try
|
||||
{
|
||||
return buffer.get();
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer.position(position);
|
||||
}
|
||||
return buffer.get(buffer.position());
|
||||
}
|
||||
|
||||
public int readBigSmart()
|
||||
@@ -144,8 +136,14 @@ public class InputStream extends java.io.InputStream
|
||||
|
||||
public int readShortSmart()
|
||||
{
|
||||
int var2 = this.peek() & 0xFF;
|
||||
return var2 < 128 ? this.readUnsignedByte() - 64 : this.readUnsignedShort() - 0xc000;
|
||||
int peek = this.peek() & 0xFF;
|
||||
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()
|
||||
|
||||
121
cache/src/main/java/net/runelite/cache/region/Location.java
vendored
Normal file
121
cache/src/main/java/net/runelite/cache/region/Location.java
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
107
cache/src/main/java/net/runelite/cache/region/Position.java
vendored
Normal file
107
cache/src/main/java/net/runelite/cache/region/Position.java
vendored
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@
|
||||
*/
|
||||
package net.runelite.cache.region;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
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[][][] underlayIds = new byte[Z][X][Y];
|
||||
|
||||
private final List<Location> locations = new ArrayList<>();
|
||||
|
||||
public Region(int 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()
|
||||
{
|
||||
return regionID;
|
||||
@@ -162,4 +197,9 @@ public class Region
|
||||
{
|
||||
return underlayIds[z][x][y] & 0xFF;
|
||||
}
|
||||
|
||||
public List<Location> getLocations()
|
||||
{
|
||||
return locations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,15 +27,19 @@
|
||||
* (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 com.google.common.io.Files;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.runelite.cache.fs.Archive;
|
||||
import net.runelite.cache.fs.Index;
|
||||
import net.runelite.cache.fs.Store;
|
||||
import net.runelite.cache.region.Region;
|
||||
import net.runelite.cache.util.XteaKeyManager;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -48,12 +52,15 @@ public class MapDumperTest
|
||||
private static final Logger logger = LoggerFactory.getLogger(MapDumperTest.class);
|
||||
|
||||
private static final int MAX_REGIONS = 32768;
|
||||
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void dump() throws IOException
|
||||
private final List<Region> regions = new ArrayList<>();
|
||||
|
||||
//@Test
|
||||
public void dumpRaw() throws IOException
|
||||
{
|
||||
File base = StoreLocation.LOCATION,
|
||||
outDir = folder.newFolder();
|
||||
@@ -68,17 +75,19 @@ public class MapDumperTest
|
||||
for (int i = 0; i < MAX_REGIONS; i++)
|
||||
{
|
||||
int[] keys = keyManager.getKeys(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;
|
||||
@@ -99,10 +108,12 @@ public class MapDumperTest
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
logger.info("Decrypted region {} coords {},{}", i, x, y);
|
||||
|
||||
data = land.getFiles().get(0).getContents();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import net.runelite.cache.fs.Store;
|
||||
import net.runelite.cache.region.Region;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
@@ -51,25 +52,24 @@ public class MapImageDumperTest
|
||||
public void extract() throws IOException
|
||||
{
|
||||
File base = StoreLocation.LOCATION,
|
||||
outFile = folder.newFolder();
|
||||
|
||||
BufferedImage[] images;
|
||||
outDir = folder.newFolder();
|
||||
|
||||
try (Store store = new Store(base))
|
||||
{
|
||||
store.load();
|
||||
|
||||
MapImageDumper dumper = new MapImageDumper(store);
|
||||
images = dumper.buildImages();
|
||||
}
|
||||
dumper.load();
|
||||
|
||||
int i = 0;
|
||||
for (BufferedImage image : images)
|
||||
{
|
||||
File imageFile = new File(outFile, "img-" + i++ + ".png");
|
||||
for (int i = 0; i < Region.Z; ++i)
|
||||
{
|
||||
BufferedImage image = dumper.drawMap(i);
|
||||
|
||||
ImageIO.write(image, "png", imageFile);
|
||||
logger.info("Wrote image {}", imageFile);
|
||||
File imageFile = new File(outDir, "img-" + i + ".png");
|
||||
|
||||
ImageIO.write(image, "png", imageFile);
|
||||
logger.info("Wrote image {}", imageFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,10 @@ public class StoreLocation
|
||||
{
|
||||
private static final Logger logger = LoggerFactory.getLogger(StoreLocation.class);
|
||||
|
||||
private static final String TMP_DIR = "d:/temp";
|
||||
|
||||
public static File LOCATION;
|
||||
private static File TMP;
|
||||
|
||||
static
|
||||
{
|
||||
@@ -55,11 +58,23 @@ public class StoreLocation
|
||||
|
||||
File tmp = new File("d:/temp");
|
||||
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()
|
||||
{
|
||||
return new TemporaryFolder();
|
||||
return new TemporaryFolder()
|
||||
{
|
||||
@Override
|
||||
public void after()
|
||||
{
|
||||
// don't cleanup if using local tmpdir
|
||||
if (TMP == null)
|
||||
super.after();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user