Add map image dumper, mostly from @im-frizzy, but with some improvements

This commit is contained in:
Adam
2016-08-20 15:47:58 -04:00
parent 3706d40bc0
commit 6e6c59dc5b
16 changed files with 1397 additions and 10 deletions

View File

@@ -0,0 +1,416 @@
/*
* 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.Graphics2D;
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.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.OverlayLoader;
import net.runelite.cache.definitions.loaders.SpriteLoader;
import net.runelite.cache.definitions.loaders.TextureLoader;
import net.runelite.cache.definitions.loaders.UnderlayLoader;
import net.runelite.cache.fs.Archive;
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.Region;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MapImageDumper
{
private static final Logger logger = LoggerFactory.getLogger(MapImageDumper.class);
private static final int MAX_REGION = 32768;
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 Map<SpriteDefinition, Integer> averageColors = new HashMap<>();
private final List<Region> regions = new ArrayList<>();
private Region lowestX = null, lowestY = null;
private Region highestX = null, highestY = null;
private boolean labelRegions;
private boolean outlineRegions;
public MapImageDumper(Store store)
{
this.store = store;
}
public BufferedImage[] buildImages() throws IOException
{
BufferedImage[] images = new BufferedImage[Region.Z];
loadUnderlays(store);
loadOverlays(store);
loadTextures(store);
loadSprites(store);
loadRegions(store);
int minX = lowestX.getBaseX();
int minY = lowestY.getBaseY();
int maxX = highestX.getBaseX() + Region.X;
int maxY = highestY.getBaseY() + Region.Y;
int dimX = maxX - minX;
int dimY = maxY - minY;
logger.info("Map image dimensions: {}px x {}px", dimX, dimY);
for (int i = 0; i < images.length; ++i)
{
images[i] = new BufferedImage(dimX, dimY, BufferedImage.TYPE_INT_RGB);
}
for (Region region : regions)
{
int baseX = region.getBaseX();
int baseY = region.getBaseY();
// to pixel X
int drawBaseX = baseX - lowestX.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;
for (int z = 0; z < Region.Z; ++z)
{
BufferedImage image = images[z];
Graphics2D graphics = image.createGraphics();
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 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);
rgb = underlay.getColor();
}
image.setRGB(drawX, drawY, rgb);
}
}
if (labelRegions)
{
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();
}
}
return images;
}
private void loadRegions(Store store) throws IOException
{
Index index = store.getIndex(IndexType.MAPS);
for (int i = 0; i < MAX_REGION; ++i)
{
Region region = new Region(i);
Archive map = index.findArchiveByName("m" + (i >> 8) + "_" + (i & 0xFF));
if (map == null)
{
continue;
}
assert map.getFiles().size() == 1;
map.decompressAndLoad(null);
byte[] data = map.getFiles().get(0).getContents();
region.loadTerrain(data);
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());
}
private void loadUnderlays(Store store)
{
Index index = store.getIndex(IndexType.CONFIGS);
Archive archive = index.getArchive(ConfigType.UNDERLAY.getId());
for (File file : archive.getFiles())
{
UnderlayLoader loader = new UnderlayLoader();
UnderlayDefinition underlay = loader.load(file.getFileId(), file.getContents());
underlays.add(underlay);
}
}
private UnderlayDefinition findUnderlay(int id)
{
for (UnderlayDefinition u : underlays)
{
if (u.getId() == id)
{
return u;
}
}
return null;
}
private void loadOverlays(Store store)
{
Index index = store.getIndex(IndexType.CONFIGS);
Archive archive = index.getArchive(ConfigType.OVERLAY.getId());
for (File file : archive.getFiles())
{
OverlayLoader loader = new OverlayLoader();
OverlayDefinition underlay = loader.load(file.getFileId(), file.getContents());
overlays.add(underlay);
}
}
private OverlayDefinition findOverlay(int id)
{
for (OverlayDefinition u : overlays)
{
if (u.getId() == id)
{
return u;
}
}
return null;
}
private void loadTextures(Store store)
{
Index index = store.getIndex(IndexType.TEXTURES);
Archive archive = index.getArchive(0);
for (File file : archive.getFiles())
{
TextureLoader loader = new TextureLoader();
TextureDefinition texture = loader.load(file.getFileId(), file.getContents());
textures.add(texture);
}
}
private TextureDefinition findTexture(int id)
{
for (TextureDefinition t : textures)
{
if (t.getId() == id)
{
return t;
}
}
return null;
}
private void loadSprites(Store store)
{
Index index = store.getIndex(IndexType.SPRITES);
for (Archive a : index.getArchives())
{
List<File> files = a.getFiles();
assert files.size() == 1;
File file = files.get(0);
byte[] contents = file.getContents();
SpriteLoader loader = new SpriteLoader();
SpriteDefinition[] sprites = loader.load(a.getArchiveId(), new InputStream(contents));
for (SpriteDefinition sprite : sprites)
{
this.sprites.add(sprite);
averageColors.put(sprite, getAverageColor(sprite.getPixels()));
}
}
}
private SpriteDefinition findSprite(int id, int frame)
{
for (SpriteDefinition def : sprites)
{
if (def.getId() == id && def.getFrame() == frame)
{
return def;
}
}
return null;
}
private int getAverageColor(int[] pixels)
{
int redTotal = 0;
int greenTotal = 0;
int blueTotal = 0;
if (pixels.length == 0)
{
return 0;
}
for (int pixel : pixels)
{
Color color = new Color(pixel);
redTotal += color.getRed();
greenTotal += color.getGreen();
blueTotal += color.getBlue();
}
int averageRed = redTotal / pixels.length;
int averageGreen = greenTotal / pixels.length;
int averageBlue = blueTotal / pixels.length;
Color color = new Color(averageRed, averageGreen, averageBlue);
return color.getRGB();
}
public boolean isLabelRegions()
{
return labelRegions;
}
public void setLabelRegions(boolean labelRegions)
{
this.labelRegions = labelRegions;
}
public boolean isOutlineRegions()
{
return outlineRegions;
}
public void setOutlineRegions(boolean outlineRegions)
{
this.outlineRegions = outlineRegions;
}
}

View File

@@ -0,0 +1,90 @@
/*
* 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.definitions;
public class OverlayDefinition
{
private int id;
private int rgbColor = 0;
private int texture = -1;
private int secondaryRgbColor = -1;
private boolean hideUnderlay = true;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public int getRgbColor()
{
return rgbColor;
}
public void setRgbColor(int rgbColor)
{
this.rgbColor = rgbColor;
}
public int getTexture()
{
return texture;
}
public void setTexture(int texture)
{
this.texture = texture;
}
public int getSecondaryRgbColor()
{
return secondaryRgbColor;
}
public void setSecondaryRgbColor(int secondaryRgbColor)
{
this.secondaryRgbColor = secondaryRgbColor;
}
public boolean isHideUnderlay()
{
return hideUnderlay;
}
public void setHideUnderlay(boolean hideUnderlay)
{
this.hideUnderlay = hideUnderlay;
}
}

View File

@@ -32,6 +32,8 @@ package net.runelite.cache.definitions;
public class SpriteDefinition
{
private int id;
private int frame;
private int offsetX;
private int offsetY;
private int width;
@@ -40,6 +42,62 @@ public class SpriteDefinition
private int maxWidth;
private int maxHeight;
@Override
public int hashCode()
{
int hash = 7;
hash = 89 * hash + this.id;
hash = 89 * hash + this.frame;
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 SpriteDefinition other = (SpriteDefinition) obj;
if (this.id != other.id)
{
return false;
}
if (this.frame != other.frame)
{
return false;
}
return true;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public int getFrame()
{
return frame;
}
public void setFrame(int frame)
{
this.frame = frame;
}
public int getOffsetX()
{
return offsetX;
@@ -109,6 +167,4 @@ public class SpriteDefinition
{
this.maxHeight = maxHeight;
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.definitions;
public class TextureDefinition
{
private int id;
private int[] fileIds;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public int[] getFileIds()
{
return fileIds;
}
public void setFileIds(int[] fileIds)
{
this.fileIds = fileIds;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.definitions;
public class UnderlayDefinition
{
private int id;
private int color;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public int getColor()
{
return color;
}
public void setColor(int color)
{
this.color = color;
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.definitions.loaders;
import net.runelite.cache.definitions.OverlayDefinition;
import net.runelite.cache.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OverlayLoader
{
private static final Logger logger = LoggerFactory.getLogger(OverlayLoader.class);
public OverlayDefinition load(int id, byte[] b)
{
OverlayDefinition def = new OverlayDefinition();
InputStream is = new InputStream(b);
def.setId(id);
for (;;)
{
int opcode = is.readUnsignedByte();
if (opcode == 0)
{
break;
}
if (opcode == 1)
{
int color = is.read24BitInt();
def.setRgbColor(color);
}
else if (opcode == 2)
{
int texture = is.readUnsignedByte();
def.setTexture(texture);
}
else if (opcode == 5)
{
def.setHideUnderlay(false);
}
else if (opcode == 7)
{
int secondaryColor = is.read24BitInt();
def.setSecondaryRgbColor(secondaryColor);
}
}
return def;
}
}

View File

@@ -38,7 +38,7 @@ public class SpriteLoader
public static final int FLAG_VERTICAL = 0b01;
public static final int FLAG_ALPHA = 0b10;
public SpriteDefinition[] load(InputStream stream)
public SpriteDefinition[] load(int id, InputStream stream)
{
stream.setOffset(stream.getLength() - 2);
int spriteCount = stream.readUnsignedShort();
@@ -58,6 +58,8 @@ public class SpriteLoader
for (int i = 0; i < spriteCount; ++i)
{
sprites[i] = new SpriteDefinition();
sprites[i].setId(id);
sprites[i].setFrame(i);
sprites[i].setMaxWidth(width);
sprites[i].setMaxHeight(height);
}

View File

@@ -0,0 +1,59 @@
/*
* 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.definitions.loaders;
import net.runelite.cache.definitions.TextureDefinition;
import net.runelite.cache.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TextureLoader
{
private static final Logger logger = LoggerFactory.getLogger(TextureLoader.class);
public TextureDefinition load(int id, byte[] b)
{
TextureDefinition def = new TextureDefinition();
InputStream is = new InputStream(b);
is.skip(3);
def.setId(id);
int count = is.readUnsignedByte();
int[] files = new int[count];
for (int i = 0; i < count; ++i)
files[i] = is.readUnsignedShort();
def.setFileIds(files);
return def;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.definitions.loaders;
import net.runelite.cache.definitions.UnderlayDefinition;
import net.runelite.cache.io.InputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UnderlayLoader
{
private static final Logger logger = LoggerFactory.getLogger(UnderlayLoader.class);
public UnderlayDefinition load(int id, byte[] b)
{
UnderlayDefinition def = new UnderlayDefinition();
InputStream is = new InputStream(b);
def.setId(id);
for (;;)
{
int opcode = is.readUnsignedByte();
if (opcode == 0)
{
break;
}
if (opcode == 1)
{
int color = is.read24BitInt();
def.setColor(color);
}
}
return def;
}
}

View File

@@ -0,0 +1,102 @@
/**
* Copyright (c) 2015 Kyle Friz
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.runelite.cache.region;
/**
* @author Kyle Friz
* @since Feb 20, 2016
*/
public class HeightCalc
{
private static final int JAGEX_CIRCULAR_ANGLE = 2048;
private static final double ANGULAR_RATIO = 360D / JAGEX_CIRCULAR_ANGLE;
private static final double JAGEX_RADIAN = Math.toRadians(ANGULAR_RATIO);
private static final int[] SIN = new int[JAGEX_CIRCULAR_ANGLE];
private static final int[] COS = new int[JAGEX_CIRCULAR_ANGLE];
static
{
for (int i = 0; i < JAGEX_CIRCULAR_ANGLE; i++)
{
SIN[i] = (int) (65536.0D * Math.sin((double) i * JAGEX_RADIAN));
COS[i] = (int) (65536.0D * Math.cos((double) i * JAGEX_RADIAN));
}
}
public static int calculate(int baseX, int baseY, int x, int y)
{
int xc = (baseX >> 3) + 932731 + x;
int yc = (baseY >> 3) + 556238 + y;
int n = interpolateNoise(xc + 45365, yc + 91923, 4) - 128
+ (interpolateNoise(10294 + xc, yc + 37821, 2) - 128 >> 1)
+ (interpolateNoise(xc, yc, 1) - 128 >> 2);
n = 35 + (int) ((double) n * 0.3D);
if (n < 10)
{
n = 10;
}
else if (n > 60)
{
n = 60;
}
return n;
}
public static int interpolateNoise(int x, int y, int frequency)
{
int intX = x / frequency;
int fracX = x & frequency - 1;
int intY = y / frequency;
int fracY = y & frequency - 1;
int v1 = smoothedNoise1(intX, intY);
int v2 = smoothedNoise1(intX + 1, intY);
int v3 = smoothedNoise1(intX, intY + 1);
int v4 = smoothedNoise1(1 + intX, 1 + intY);
int i1 = interpolate(v1, v2, fracX, frequency);
int i2 = interpolate(v3, v4, fracX, frequency);
return interpolate(i1, i2, fracY, frequency);
}
public static int smoothedNoise1(int x, int y)
{
int corners = noise(x - 1, y - 1) + noise(x + 1, y - 1) + noise(x - 1, 1 + y) + noise(x + 1, y + 1);
int sides = noise(x - 1, y) + noise(1 + x, y) + noise(x, y - 1) + noise(x, 1 + y);
int center = noise(x, y);
return center / 4 + sides / 8 + corners / 16;
}
public static int noise(int x, int y)
{
int n = x + y * 57;
n ^= n << 13;
return ((n * (n * n * 15731 + 789221) + 1376312589) & Integer.MAX_VALUE) >> 19 & 255;
}
public static int interpolate(int a, int b, int x, int y)
{
int f = 65536 - COS[1024 * x / y] >> 1;
return (f * b >> 16) + (a * (65536 - f) >> 16);
}
}

View File

@@ -0,0 +1,165 @@
/**
* Copyright (c) 2015 Kyle Friz
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package net.runelite.cache.region;
import net.runelite.cache.io.InputStream;
/**
*
* @author Kyle Friz
* @since Jun 30, 2015
*/
public class Region
{
public static final int X = 64;
public static final int Y = 64;
public static final int Z = 4;
private final int regionID;
private final int baseX;
private final int baseY;
private final int[][][] tileHeights = new int[Z][X][Y];
private final byte[][][] renderRules = new byte[Z][X][Y];
private final byte[][][] overlayIds = new byte[Z][X][Y];
private final byte[][][] overlayPaths = new byte[Z][X][Y];
private final byte[][][] overlayRotations = new byte[Z][X][Y];
private final byte[][][] underlayIds = new byte[Z][X][Y];
public Region(int id)
{
this.regionID = id;
this.baseX = ((id >> 8) & 0xFF) << 6;
this.baseY = (id & 0xFF) << 6;
}
public void loadTerrain(byte[] buf)
{
InputStream in = new InputStream(buf);
for (int z = 0; z < Z; z++)
{
for (int x = 0; x < X; x++)
{
for (int y = 0; y < Y; y++)
{
while (true)
{
int attribute = in.readUnsignedByte();
if (attribute == 0)
{
if (z == 0)
{
tileHeights[0][x][y] = -HeightCalc.calculate(baseX, baseY, x, y) * 8;
}
else
{
tileHeights[z][x][y] = tileHeights[z - 1][x][y] - 240;
}
break;
}
else if (attribute == 1)
{
int height = in.readUnsignedByte();
if (height == 1)
{
height = 0;
}
if (z == 0)
{
tileHeights[0][x][y] = -height * 8;
}
else
{
tileHeights[z][x][y] = tileHeights[z - 1][x][y] - height * 8;
}
break;
}
else if (attribute <= 49)
{
overlayIds[z][x][y] = in.readByte();
overlayPaths[z][x][y] = (byte) ((attribute - 2) / 4);
overlayRotations[z][x][y] = (byte) (attribute - 2 & 3);
}
else if (attribute <= 81)
{
renderRules[z][x][y] = (byte) (attribute - 49);
}
else
{
underlayIds[z][x][y] = (byte) (attribute - 81);
}
}
}
}
}
}
public int getRegionID()
{
return regionID;
}
public int getBaseX()
{
return baseX;
}
public int getBaseY()
{
return baseY;
}
public int getTileHeight(int z, int x, int y)
{
return tileHeights[z][x][y];
}
public byte getRenderRule(int z, int x, int y)
{
return renderRules[z][x][y];
}
public int getOverlayId(int z, int x, int y)
{
return overlayIds[z][x][y] & 0xFF;
}
public byte getOverlayPath(int z, int x, int y)
{
return overlayPaths[z][x][y];
}
public byte getOverlayRotation(int z, int x, int y)
{
return overlayRotations[z][x][y];
}
public int getUnderlayId(int z, int x, int y)
{
return underlayIds[z][x][y] & 0xFF;
}
}

View File

@@ -0,0 +1,75 @@
/*
* 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.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MapImageDumperTest
{
private static final Logger logger = LoggerFactory.getLogger(MapImageDumperTest.class);
@Rule
public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
@Test
public void extract() throws IOException
{
File base = StoreLocation.LOCATION,
outFile = folder.newFile();
BufferedImage[] images;
try (Store store = new Store(base))
{
store.load();
MapImageDumper dumper = new MapImageDumper(store);
images = dumper.buildImages();
}
int i = 0;
for (BufferedImage image : images)
{
File imageFile = new File(outFile, "img-" + i++ + ".png");
ImageIO.write(image, "png", imageFile);
logger.info("Wrote image {}", imageFile);
}
}
}

View File

@@ -79,9 +79,7 @@ public class SpriteDumperTest
byte[] contents = file.getContents();
SpriteLoader loader = new SpriteLoader();
SpriteDefinition[] sprites = loader.load(new InputStream(contents));
int frame = 0;
SpriteDefinition[] sprites = loader.load(a.getArchiveId(), new InputStream(contents));
for (SpriteDefinition def : sprites)
{
@@ -90,12 +88,11 @@ public class SpriteDumperTest
continue;
BufferedImage image = getBufferedImage(def);
java.io.File targ = new java.io.File(outDir, a.getArchiveId() + "-" + frame + ".png");
java.io.File targ = new java.io.File(outDir, def.getId() + "-" + def.getFrame() + ".png");
targ.mkdirs();
ImageIO.write(image, "png", targ);
++count;
++frame;
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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 com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.nio.charset.Charset;
import net.runelite.cache.definitions.TextureDefinition;
import net.runelite.cache.definitions.loaders.TextureLoader;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.File;
import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Store;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TextureDumper
{
private static final Logger logger = LoggerFactory.getLogger(TextureDumper.class);
@Rule
public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
@Test
public void extract() throws IOException
{
java.io.File base = StoreLocation.LOCATION,
outDir = folder.newFile();
int count = 0;
try (Store store = new Store(base))
{
store.load();
Index index = store.getIndex(IndexType.TEXTURES);
Archive archive = index.getArchive(0);
for (File file : archive.getFiles())
{
TextureLoader loader = new TextureLoader();
TextureDefinition texture = loader.load(file.getFileId(), file.getContents());
Files.write(gson.toJson(texture), new java.io.File(outDir, file.getFileId() + ".json"), Charset.defaultCharset());
++count;
}
}
logger.info("Dumped {} textures to {}", count, outDir);
}
}

View File

@@ -32,8 +32,6 @@ package net.runelite.cache;
import java.io.IOException;
import java.nio.file.Files;
import net.runelite.cache.IndexType;
import net.runelite.cache.StoreLocation;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.File;
import net.runelite.cache.fs.Index;

View File

@@ -0,0 +1,85 @@
/*
* 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 com.google.common.io.Files;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.nio.charset.Charset;
import net.runelite.cache.definitions.UnderlayDefinition;
import net.runelite.cache.definitions.loaders.UnderlayLoader;
import net.runelite.cache.fs.Archive;
import net.runelite.cache.fs.File;
import net.runelite.cache.fs.Index;
import net.runelite.cache.fs.Store;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UnderlayDumper
{
private static final Logger logger = LoggerFactory.getLogger(UnderlayDumper.class);
@Rule
public TemporaryFolder folder = StoreLocation.getTemporaryFolder();
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
@Test
public void extract() throws IOException
{
java.io.File base = StoreLocation.LOCATION,
outDir = folder.newFile();
int count = 0;
try (Store store = new Store(base))
{
store.load();
Index index = store.getIndex(IndexType.CONFIGS);
Archive archive = index.getArchive(ConfigType.UNDERLAY.getId());
for (File file : archive.getFiles())
{
UnderlayLoader loader = new UnderlayLoader();
UnderlayDefinition underlay = loader.load(file.getFileId(), file.getContents());
Files.write(gson.toJson(underlay), new java.io.File(outDir, file.getFileId() + ".json"), Charset.defaultCharset());
++count;
}
}
logger.info("Dumped {} underlays to {}", count, outDir);
}
}