Merge pull request #45 from P0nk/fix-tools

Rework tools:
All tools are now part of the main module, thereby avoiding copies of required class files.
This should result in quicker builds and searches, since there are no longer a bunch of redundant files to process.
This commit is contained in:
Ponk
2021-07-11 15:16:59 +02:00
committed by GitHub
1103 changed files with 4100 additions and 163889 deletions

133
.gitignore vendored
View File

@@ -4,142 +4,9 @@
/target
# build files
/build/
/dist/
/nbproject/
/tools/MapleArrowFetcher/build/
/tools/MapleArrowFetcher/dist/
/tools/MapleArrowFetcher/nbproject/
/tools/MapleBossHpBarFetcher/build/
/tools/MapleBossHpBarFetcher/dist/
/tools/MapleBossHpBarFetcher/nbproject/
/tools/MapleCashCosmeticsChecker/build/
/tools/MapleCashCosmeticsChecker/dist/
/tools/MapleCashCosmeticsChecker/nbproject/
/tools/MapleCashCosmeticsFetcher/build/
/tools/MapleCashCosmeticsFetcher/dist/
/tools/MapleCashCosmeticsFetcher/nbproject/
/tools/MapleCashDropFetcher/build/
/tools/MapleCashDropFetcher/dist/
/tools/MapleCashDropFetcher/nbproject/
/tools/MapleCashVegaChecker/build/
/tools/MapleCashVegaChecker/dist/
/tools/MapleCashVegaChecker/nbproject/
/tools/MapleCodeCouponGenerator/build/
/tools/MapleCodeCouponGenerator/dist/
/tools/MapleCodeCouponGenerator/nbproject/
/tools/MapleCouponInstaller/build/
/tools/MapleCouponInstaller/dist/
/tools/MapleCouponInstaller/nbproject/
/tools/MapleDojoUpdater/build/
/tools/MapleDojoUpdater/dist/
/tools/MapleDojoUpdater/nbproject/
/tools/MapleEmptyItemWzChecker/build/
/tools/MapleEmptyItemWzChecker/dist/
/tools/MapleEmptyItemWzChecker/nbproject/
/tools/MapleEquipmentOmnileveler/build/
/tools/MapleEquipmentOmnileveler/dist/
/tools/MapleEquipmentOmnileveler/nbproject/
/tools/MapleEventMethodFiller/build/
/tools/MapleEventMethodFiller/dist/
/tools/MapleEventMethodFiller/nbproject/
/tools/MapleGachaponItemidRetriever/build/
/tools/MapleGachaponItemidRetriever/dist/
/tools/MapleGachaponItemidRetriever/nbproject/
/tools/MapleIdRetriever/build/
/tools/MapleIdRetriever/dist/
/tools/MapleIdRetriever/nbproject/
/tools/MapleInvalidItemIdFetcher/build/
/tools/MapleInvalidItemIdFetcher/dist/
/tools/MapleInvalidItemIdFetcher/nbproject/
/tools/MapleInvalidItemWithNoNameFetcher/build/
/tools/MapleInvalidItemWithNoNameFetcher/dist/
/tools/MapleInvalidItemWithNoNameFetcher/nbproject/
/tools/MapleMapFieldLimitChecker/build/
/tools/MapleMapFieldLimitChecker/dist/
/tools/MapleMapFieldLimitChecker/nbproject/
/tools/MapleMapInfoRetriever/build/
/tools/MapleMapInfoRetriever/dist/
/tools/MapleMapInfoRetriever/nbproject/
/tools/MapleMapLootLimitChecker/build/
/tools/MapleMapLootLimitChecker/dist/
/tools/MapleMapLootLimitChecker/nbproject/
/tools/MapleMesoFetcher/build/
/tools/MapleMesoFetcher/dist/
/tools/MapleMesoFetcher/nbproject/
/tools/MapleMobBookIndexer/build/
/tools/MapleMobBookIndexer/dist/
/tools/MapleMobBookIndexer/nbproject/
/tools/MapleMobBookUpdate/build/
/tools/MapleMobBookUpdate/dist/
/tools/MapleMobBookUpdate/nbproject/
/tools/MapleQuestItemCountFetcher/build/
/tools/MapleQuestItemCountFetcher/dist/
/tools/MapleQuestItemCountFetcher/nbproject/
/tools/MapleQuestItemFetcher/build/
/tools/MapleQuestItemFetcher/dist/
/tools/MapleQuestItemFetcher/nbproject/
/tools/MapleQuestlineFetcher/build/
/tools/MapleQuestlineFetcher/dist/
/tools/MapleQuestlineFetcher/nbproject/
/tools/MapleQuestMesoFetcher/build/
/tools/MapleQuestMesoFetcher/dist/
/tools/MapleQuestMesoFetcher/nbproject/
/tools/MapleReactorDropFetcher/build/
/tools/MapleReactorDropFetcher/dist/
/tools/MapleReactorDropFetcher/nbproject/
/tools/MapleSkillbookChanceFetcher/build/
/tools/MapleSkillbookChanceFetcher/dist/
/tools/MapleSkillbookChanceFetcher/nbproject/
/tools/MapleSkillbookStackUpdate/build/
/tools/MapleSkillbookStackUpdate/dist/
/tools/MapleSkillbookStackUpdate/nbproject/
/tools/MapleSkillMakerFetcher/build/
/tools/MapleSkillMakerFetcher/dist/
/tools/MapleSkillMakerFetcher/nbproject/
/tools/MapleSkillMakerReagentIndexer/build/
/tools/MapleSkillMakerReagentIndexer/dist/
/tools/MapleSkillMakerReagentIndexer/nbproject/
/tools/MapleWorldmapChecker/build/
/tools/MapleWorldmapChecker/dist/
/tools/MapleWorldmapChecker/nbproject/
/tools/SpiderDropFetcher/build/
/tools/SpiderDropFetcher/dist/
/tools/SpiderDropFetcher/nbproject/
/out
*.onetoc2

View File

@@ -47,6 +47,11 @@
<artifactId>jcip-annotations</artifactId>
<version>1.0</version>
</dependency>
<dependency> <!-- only used for some tools -->
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>

View File

@@ -23,16 +23,16 @@ package client;
import constants.skills.*;
import provider.*;
import provider.wz.WZFiles;
import server.MapleStatEffect;
import server.life.Element;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
public class SkillFactory {
private static volatile Map<Integer, Skill> skills = new HashMap<>();
private static final MapleDataProvider datasource = MapleDataProviderFactory.getDataProvider(MapleDataProviderFactory.fileInWZPath("Skill.wz"));
private static final MapleDataProvider datasource = MapleDataProviderFactory.getDataProvider(WZFiles.SKILL);
public static Skill getSkill(int id) {
return skills.get(id);
@@ -330,7 +330,7 @@ public class SkillFactory {
}
public static String getSkillName(int skillid) {
MapleData data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img");
MapleData data = MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img");
StringBuilder skill = new StringBuilder();
skill.append(skillid);
if (skill.length() == 4) {

View File

@@ -27,8 +27,7 @@ import client.*;
import client.command.Command;
import provider.MapleData;
import provider.MapleDataProviderFactory;
import java.io.File;
import provider.wz.WZFiles;
public class MaxSkillCommand extends Command {
{
@@ -38,7 +37,7 @@ public class MaxSkillCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) {
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
try {
Skill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName()));
player.changeSkillLevel(skill, (byte) skill.getMaxLevel(), skill.getMaxLevel(), -1);

View File

@@ -27,8 +27,7 @@ import client.*;
import client.command.Command;
import provider.MapleData;
import provider.MapleDataProviderFactory;
import java.io.File;
import provider.wz.WZFiles;
public class ResetSkillCommand extends Command {
{
@@ -38,7 +37,7 @@ public class ResetSkillCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) {
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
try {
Skill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName()));
player.changeSkillLevel(skill, (byte) 0, skill.getMaxLevel(), -1);

View File

@@ -30,12 +30,11 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import server.MapleItemInformationProvider;
import server.quest.MapleQuest;
import tools.Pair;
import java.io.File;
public class SearchCommand extends Command {
private static MapleData npcStringData;
private static MapleData mobStringData;
@@ -45,7 +44,7 @@ public class SearchCommand extends Command {
{
setDescription("Search String.wz.");
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
npcStringData = dataProvider.getData("Npc.img");
mobStringData = dataProvider.getData("Mob.img");
skillStringData = dataProvider.getData("Skill.img");

View File

@@ -25,8 +25,8 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@@ -35,7 +35,7 @@ import java.util.Map;
* @author Danny (Leifde)
*/
public class PetDataFactory {
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
private static Map<String, PetCommand> petCommands = new HashMap<>();
private static Map<Integer, Integer> petHunger = new HashMap<>();

View File

@@ -20,24 +20,25 @@
package client.processor.action;
import client.MapleCharacter;
import java.awt.Point;
import client.MapleClient;
import client.SkillFactory;
import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import client.SkillFactory;
import provider.MapleDataTool;
import client.inventory.manipulator.MapleInventoryManipulator;
import java.io.File;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import tools.MaplePacketCreator;
import java.awt.*;
/**
*
* @author RonanLana - just added locking on OdinMS' SpawnPetHandler method body
*/
public class SpawnPetProcessor {
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
private static final MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
public static void processSpawnPet(MapleClient c, byte slot, boolean lead) {
if (c.tryacquireClient()) {

View File

@@ -5,11 +5,11 @@ import client.MapleJob;
import config.YamlConfig;
import constants.skills.Aran;
import provider.*;
import provider.wz.WZFiles;
import server.maps.FieldLimit;
import server.maps.MapleMap;
import server.quest.MapleQuest;
import java.io.File;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.*;
@@ -676,7 +676,7 @@ public class GameConstants {
}
private static int getMaxObstacleMobDamageFromWz() {
MapleDataProvider mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
MapleDataProvider mapSource = MapleDataProviderFactory.getDataProvider(WZFiles.MAP);
int maxMobDmg = 0;
MapleDataDirectoryEntry root = mapSource.getRoot();

View File

@@ -969,7 +969,6 @@ public class Server {
}
public static void main(String[] args) {
System.setProperty("wzpath", "wz");
Security.setProperty("crypto.policy", "unlimited");
AutoJCE.removeCryptographyRestrictions();
Server.getInstance().init();

View File

@@ -34,10 +34,10 @@ import net.server.world.MapleParty;
import provider.MapleData;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import tools.MaplePacketCreator;
import tools.Pair;
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
@@ -68,7 +68,7 @@ public class MaplePartySearchCoordinator {
private static Map<Integer, Set<Integer>> fetchNeighbouringMaps() {
Map<Integer, Set<Integer>> mapLinks = new HashMap<>();
MapleData data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Etc.wz")).getData("MapNeighbors.img");
MapleData data = MapleDataProviderFactory.getDataProvider(WZFiles.ETC).getData("MapNeighbors.img");
if (data != null) {
for (MapleData mapdata : data.getChildren()) {
int mapid = Integer.parseInt(mapdata.getName());

View File

@@ -21,18 +21,18 @@
*/
package provider;
import java.io.File;
import java.io.IOException;
import provider.wz.WZFile;
import provider.wz.WZFiles;
import provider.wz.XMLWZFile;
public class MapleDataProviderFactory {
private final static String wzPath = System.getProperty("wzpath");
import java.io.File;
import java.io.IOException;
private static MapleDataProvider getWZ(File in, boolean provideImages) {
public class MapleDataProviderFactory {
private static MapleDataProvider getWZ(File in) {
if (in.getName().toLowerCase().endsWith("wz") && !in.isDirectory()) {
try {
return new WZFile(in, provideImages);
return new WZFile(in, false);
} catch (IOException e) {
throw new RuntimeException("Loading WZ File failed", e);
}
@@ -41,15 +41,7 @@ public class MapleDataProviderFactory {
}
}
public static MapleDataProvider getDataProvider(File in) {
return getWZ(in, false);
}
public static MapleDataProvider getImageProvidingDataProvider(File in) {
return getWZ(in, true);
}
public static File fileInWZPath(String filename) {
return new File(wzPath, filename);
public static MapleDataProvider getDataProvider(WZFiles in) {
return getWZ(in.getFile());
}
}

View File

@@ -21,7 +21,6 @@
*/
package provider.wz;
import provider.MapleDataProviderFactory;
import tools.data.input.GenericLittleEndianAccessor;
import tools.data.input.InputStreamByteStream;
import tools.data.input.LittleEndianAccessor;
@@ -69,7 +68,7 @@ public class ListWZFile {
if (listWz != null) {
ListWZFile listwz;
try {
listwz = new ListWZFile(MapleDataProviderFactory.fileInWZPath("List.wz"));
listwz = new ListWZFile(WZFiles.LIST.getFile());
modernImgs = new HashSet<>(listwz.getEntries());
} catch (FileNotFoundException e) {
e.printStackTrace();

View File

@@ -0,0 +1,35 @@
package provider.wz;
import java.io.File;
public enum WZFiles {
QUEST("Quest"),
ETC("Etc"),
ITEM("Item"),
CHARACTER("Character"),
STRING("String"),
LIST("List"),
MOB("Mob"),
MAP("Map"),
NPC("Npc"),
REACTOR("Reactor"),
SKILL("Skill"),
SOUND("Sound"),
UI("UI");
public static final String DIRECTORY = "wz";
private final String fileName;
WZFiles(String name) {
this.fileName = name + ".wz";
}
public File getFile() {
return new File(DIRECTORY, fileName);
}
public String getFilePath() {
return getFile().getPath();
}
}

View File

@@ -38,6 +38,7 @@ import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import provider.MapleData;
import provider.MapleDataProviderFactory;
import provider.wz.WZFiles;
import scripting.AbstractPlayerInteraction;
import server.*;
import server.MapleSkillbookInformationProvider.SkillBookEntry;
@@ -62,7 +63,6 @@ import tools.MaplePacketCreator;
import tools.packets.Wedding;
import java.awt.*;
import java.io.File;
import java.sql.SQLException;
import java.util.List;
import java.util.*;
@@ -382,7 +382,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
}
public void maxMastery() {
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "String.wz")).getData("Skill.img").getChildren()) {
for (MapleData skill_ : MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Skill.img").getChildren()) {
try {
Skill skill = SkillFactory.getSkill(Integer.parseInt(skill_.getName()));
getPlayer().changeSkillLevel(skill, (byte) 0, skill.getMaxLevel(), -1);

View File

@@ -31,10 +31,10 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import tools.DatabaseConnection;
import tools.Pair;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -149,7 +149,7 @@ public class CashShop {
private static volatile List<SpecialCashItem> specialcashitems = new ArrayList<>();
public static void loadAllCashItems() {
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
Map<Integer, CashItem> loadedItems = new HashMap<>();
List<Integer> onSaleItems = new ArrayList<>();

View File

@@ -32,12 +32,12 @@ import constants.skills.Gunslinger;
import constants.skills.NightWalker;
import net.server.Server;
import provider.*;
import provider.wz.WZFiles;
import server.MakerItemFactory.MakerItemCreateEntry;
import server.life.MapleLifeFactory;
import server.life.MapleMonsterInformationProvider;
import tools.*;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -116,10 +116,10 @@ public class MapleItemInformationProvider {
private MapleItemInformationProvider() {
loadCardIdData();
itemData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
equipData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Character.wz"));
stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
etcData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Etc.wz"));
itemData = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
equipData = MapleDataProviderFactory.getDataProvider(WZFiles.CHARACTER);
stringData = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
etcData = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
cashStringData = stringData.getData("Cash.img");
consumeStringData = stringData.getData("Consume.img");
eqpStringData = stringData.getData("Eqp.img");

View File

@@ -24,6 +24,7 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import tools.DatabaseConnection;
import java.io.File;
@@ -115,7 +116,7 @@ public class MapleSkillbookInformationProvider {
}
private static Map<Integer, SkillBookEntry> fetchSkillbooksFromQuests() {
MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz"));
MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.QUEST);
MapleData actData = questDataProvider.getData("Act.img");
MapleData checkData = questDataProvider.getData("Check.img");

View File

@@ -29,12 +29,12 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import tools.DatabaseConnection;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Pair;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -310,7 +310,7 @@ public class MapleStorage {
if(fee == null) {
fee = 100;
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(WZFiles.NPC);
MapleData npcData = npc.getData(npcId + ".img");
if(npcData != null) {
fee = MapleDataTool.getIntConvert("info/trunkPut", npcData, 100);
@@ -328,7 +328,7 @@ public class MapleStorage {
if(fee == null) {
fee = 0;
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
MapleDataProvider npc = MapleDataProviderFactory.getDataProvider(WZFiles.NPC);
MapleData npcData = npc.getData(npcId + ".img");
if(npcData != null) {
fee = MapleDataTool.getIntConvert("info/trunkGet", npcData, 0);

View File

@@ -25,12 +25,12 @@ import client.MapleCharacter;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import server.TimerManager;
import server.maps.MapleMap;
import tools.MaplePacketCreator;
import tools.Randomizer;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -43,7 +43,7 @@ public final class MapleOxQuiz {
private int question = 1;
private MapleMap map = null;
private int expGain = 200;
private static MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Etc.wz"));
private static MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
public MapleOxQuiz(MapleMap map) {
this.map = map;

View File

@@ -26,18 +26,18 @@ import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.MapleDataType;
import provider.wz.WZFiles;
import tools.Pair;
import tools.StringUtil;
import java.awt.*;
import java.io.File;
import java.util.List;
import java.util.*;
public class MapleLifeFactory {
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.MOB);
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
private static MapleData mobStringData = stringDataWZ.getData("Mob.img");
private static MapleData npcStringData = stringDataWZ.getData("Npc.img");
private static Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
@@ -46,7 +46,7 @@ public class MapleLifeFactory {
private static Set<Integer> getHpBarBosses() {
Set<Integer> ret = new HashSet<>();
MapleDataProvider uiDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/UI.wz"));
MapleDataProvider uiDataWZ = MapleDataProviderFactory.getDataProvider(WZFiles.UI);
for (MapleData bossData : uiDataWZ.getData("UIWindow.img").getChildByPath("MobGage/Mob").getChildren()) {
ret.add(Integer.valueOf(bossData.getName()));
}
@@ -322,7 +322,7 @@ public class MapleLifeFactory {
private int id;
private byte chance, x;
private loseItem(int id, byte chance, byte x) {
public loseItem(int id, byte chance, byte x) {
this.id = id;
this.chance = chance;
this.x = x;
@@ -347,7 +347,7 @@ public class MapleLifeFactory {
private int removeAfter;
private int hp;
private selfDestruction(byte action, int removeAfter, int hp) {
public selfDestruction(byte action, int removeAfter, int hp) {
this.action = action;
this.removeAfter = removeAfter;
this.hp = hp;

View File

@@ -26,12 +26,12 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import server.MapleItemInformationProvider;
import tools.DatabaseConnection;
import tools.Pair;
import tools.Randomizer;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -227,7 +227,7 @@ public class MapleMonsterInformationProvider {
}
public static ArrayList<Pair<Integer, String>> getMobsIDsFromName(String search) {
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
ArrayList<Pair<Integer, String>> retMobs = new ArrayList<>();
MapleData data = dataProvider.getData("Mob.img");
List<Pair<Integer, String>> mobPairList = new LinkedList<>();
@@ -267,7 +267,7 @@ public class MapleMonsterInformationProvider {
public String getMobNameFromId(int id) {
String mobName = mobNameCache.get(id);
if (mobName == null) {
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
MapleData mobData = dataProvider.getData("Mob.img");
mobName = MapleDataTool.getString(mobData.getChildByPath(id + "/name"), "");

View File

@@ -24,8 +24,8 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import java.io.File;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -37,7 +37,7 @@ import java.util.Map;
*/
public class MaplePlayerNPCFactory {
private static MapleDataProvider npcData = MapleDataProviderFactory.getDataProvider(new File("wz/Npc.wz"));
private static MapleDataProvider npcData = MapleDataProviderFactory.getDataProvider(WZFiles.NPC);
private static final Map<Integer, List<MaplePlayerNPC>> dnpcMaps = new HashMap<>();
private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough
@@ -49,11 +49,11 @@ public class MaplePlayerNPCFactory {
private static void loadDeveloperRoomMetadata(MapleDataProvider npc) {
MapleData thisData = npc.getData("9977777.img");
if(thisData != null) {
MapleDataProvider map = MapleDataProviderFactory.getDataProvider(new File("wz/Map.wz"));
MapleDataProvider map = MapleDataProviderFactory.getDataProvider(WZFiles.MAP);
thisData = map.getData("Map/Map7/777777777.img");
if(thisData != null) {
MapleDataProvider sound = MapleDataProviderFactory.getDataProvider(new File("wz/Sound.wz"));
MapleDataProvider sound = MapleDataProviderFactory.getDataProvider(WZFiles.SOUND);
thisData = sound.getData("Field.img");
if(thisData != null) {
@@ -70,7 +70,7 @@ public class MaplePlayerNPCFactory {
MapleDataProvider npc = npcData;
loadDeveloperRoomMetadata(npc);
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(new File("wz/Etc.wz"));
MapleDataProvider etc = MapleDataProviderFactory.getDataProvider(WZFiles.ETC);
MapleData dnpcData = etc.getData("DeveloperNpc.img");
if(dnpcData != null) {
for (MapleData data : dnpcData.getChildren()) {

View File

@@ -25,9 +25,9 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import tools.StringUtil;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
@@ -37,7 +37,7 @@ import java.util.Map;
*/
public class MobAttackInfoFactory {
private static Map<String, MobAttackInfo> mobAttacks = new HashMap<>();
private static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
private static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(WZFiles.MOB);
public static MobAttackInfo getMobAttackInfo(MapleMonster mob, int attack) {
MobAttackInfo ret = mobAttacks.get(mob.getId() + "" + attack);

View File

@@ -31,9 +31,9 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import java.awt.*;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -46,7 +46,7 @@ import java.util.Map;
public class MobSkillFactory {
private static Map<String, MobSkill> mobSkills = new HashMap<>();
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(WZFiles.SKILL);
private static MapleData skillRoot = dataSource.getData("MobSkill.img");
private final static MonitoredReentrantReadWriteLock dataLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MOBSKILL_FACTORY);
private final static MonitoredReadLock rL = MonitoredReadLockFactory.createLock(dataLock);

View File

@@ -25,6 +25,7 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import scripting.event.EventInstanceManager;
import server.life.*;
import server.partyquest.GuardianSpawnPoint;
@@ -32,7 +33,6 @@ import tools.DatabaseConnection;
import tools.StringUtil;
import java.awt.*;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -47,8 +47,8 @@ public class MapleMapFactory {
private static MapleDataProvider mapSource;
static {
nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")).getData("Map.img");
mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
nameData = MapleDataProviderFactory.getDataProvider(WZFiles.STRING).getData("Map.img");
mapSource = MapleDataProviderFactory.getDataProvider(WZFiles.MAP);
}
private static void loadLifeFromWz(MapleMap map, MapleData mapData) {

View File

@@ -25,18 +25,18 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import server.maps.MapleReactorStats.StateData;
import tools.Pair;
import tools.StringUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MapleReactorFactory {
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Reactor.wz"));
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.REACTOR);
private static Map<Integer, MapleReactorStats> reactorStats = new HashMap<>();

View File

@@ -5,10 +5,10 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import server.life.MobSkill;
import server.life.MobSkillFactory;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -22,7 +22,7 @@ public class MapleCarnivalFactory {
private final static MapleCarnivalFactory instance = new MapleCarnivalFactory();
private final Map<Integer, MCSkill> skills = new HashMap<>();
private final Map<Integer, MCSkill> guardians = new HashMap<>();
private final MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
private final MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(WZFiles.SKILL);
private final List<Integer> singleTargetedSkills = new ArrayList<>();
private final List<Integer> multiTargetedSkills = new ArrayList<>();

View File

@@ -29,12 +29,12 @@ import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.WZFiles;
import server.quest.actions.*;
import server.quest.requirements.*;
import tools.MaplePacketCreator;
import tools.StringUtil;
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
@@ -68,7 +68,7 @@ public class MapleQuest {
private boolean autoPreComplete, autoComplete;
private boolean repeatable = false;
private String name = "", parent = "";
private final static MapleDataProvider questData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Quest.wz"));
private final static MapleDataProvider questData = MapleDataProviderFactory.getDataProvider(WZFiles.QUEST);
private final static MapleData questInfo = questData.getData("QuestInfo.img");
private final static MapleData questAct = questData.getData("Act.img");
private final static MapleData questReq = questData.getData("Check.img");

View File

@@ -63,6 +63,10 @@ public class DatabaseConnection {
* @return true if connection to the database initiated successfully, false if not successful
*/
public static boolean initializeConnectionPool() {
if (dataSource != null) {
return true;
}
log.info("Initializing connection pool...");
final HikariConfig config = getConfig();
Instant initStart = Instant.now();

View File

@@ -17,205 +17,203 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplearrowfetcher;
package tools.mapletools;
import life.MapleLifeFactory;
import life.MapleMonsterStats;
import tools.DatabaseConnection;
import server.life.MapleMonsterStats;
import tools.Pair;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
*
* @author RonanLana
*
* <p>
* This application traces arrow drop data on the underlying DB (that must be
* defined on the DatabaseConnection file of this project) and generates a SQL file
* that proposes updated arrow quantitty on drop entries for the drop_data table.
*
* <p>
* The arrow quantity range is calculated accordingly with the target mob stats, such
* as level and if it's a boss or not.
*
*/
public class MapleArrowFetcher {
public class ArrowFetcher {
private static final String OUTPUT_FILE_NAME = "arrow_drop_data.sql";
private static final int MIN_ARROW_ID = 2060000;
private static final int MAX_ARROW_ID = 2061004;
private static final float CORRECTION_FACTOR = 2.2f;
private static final Map<Integer, Pair<Integer, Integer>> mobRange = new HashMap<>();
private static PrintWriter printWriter;
private static String newFile = "lib/arrow_drop_data.sql";
private static int minArrowId = 2060000;
private static int maxArrowId = 2061004;
private static float correctionFactor = 2.2f;
private static Map<Integer, MapleMonsterStats> mobStats;
private static Map<Integer, Pair<Integer, Integer>> mobRange = new HashMap<>();
private static Pair<Integer, Integer> calcArrowRange(int level, boolean boss) {
int minRange, maxRange;
// MIN range
minRange = (int)Math.ceil(((2.870503597 * level) - 1.870503597) * (boss ? 1.4 : 1.0) / correctionFactor);
minRange = (int) Math.ceil(((2.870503597 * level) - 1.870503597) * (boss ? 1.4 : 1.0) / CORRECTION_FACTOR);
// MAX range
maxRange = (int)Math.ceil(1.25 * minRange);
maxRange = (int) Math.ceil(1.25 * minRange);
return new Pair<>(minRange, maxRange);
}
private static void calcAllMobsArrowRange() {
System.out.print("Calculating range... ");
for(Entry<Integer, MapleMonsterStats> mobStat : mobStats.entrySet()) {
for (Entry<Integer, MapleMonsterStats> mobStat : mobStats.entrySet()) {
MapleMonsterStats mms = mobStat.getValue();
Pair<Integer, Integer> arrowRange;
arrowRange = calcArrowRange(mms.getLevel(), mms.isBoss());
mobRange.put(mobStat.getKey(), arrowRange);
}
System.out.println("done!");
}
private static void printSqlHeader() {
printWriter.println(" # SQL File autogenerated from the MapleArrowFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account mob stats such as level and boss for the raw arrow ranges.");
printWriter.println(" # Only current arrows entries on the DB it was compiled are being updated here.");
printWriter.println();
printWriter.println("UPDATE drop_data");
printWriter.println("SET minimum_quantity = CASE");
}
private static void printSqlMiddle() {
printWriter.println(" ELSE minimum_quantity END,");
printWriter.println(" maximum_quantity = CASE");
}
private static void printSqlFooter() {
printWriter.println(" ELSE maximum_quantity END");
printWriter.println(";");
}
private static void updateSqlMobArrowMinEntry(int[] entry) {
printWriter.println(" WHEN dropperid = " + entry[0] + " AND itemid = " + entry[1] + " THEN " + entry[2]);
}
private static void updateSqlMobArrowMaxEntry(int[] entry) {
printWriter.println(" WHEN dropperid = " + entry[0] + " AND itemid = " + entry[1] + " THEN " + entry[3]);
}
private static List<int[]> getArrowEntryValues(Map<Integer, List<Integer>> existingEntries) {
List<int[]> entryValues = new ArrayList<>(200);
List<Entry<Integer, List<Integer>>> listEntries = new ArrayList<>(existingEntries.entrySet());
Collections.sort(listEntries, (o1, o2) -> {
int val1 = o1.getKey();
int val2 = o2.getKey();
return (val1 < val2 ? -1 : (val1 == val2 ? 0 : 1));
listEntries.sort((o1, o2) -> {
int val1 = o1.getKey();
int val2 = o2.getKey();
return Integer.compare(val1, val2);
});
for(Entry<Integer, List<Integer>> ee : listEntries) {
for (Entry<Integer, List<Integer>> ee : listEntries) {
int mobid = ee.getKey();
Pair<Integer, Integer> mr = mobRange.get(mobid);
for(Integer itemid : ee.getValue()) {
for (Integer itemid : ee.getValue()) {
int itemWeight = (itemid % 10) + 1;
int[] values = new int[4];
values[0] = mobid;
values[1] = itemid;
values[2] = (int) Math.ceil(mr.getLeft() / itemWeight); // weighted min quantity
values[2] = (int) Math.ceil(mr.getLeft() / itemWeight); // weighted min quantity
values[3] = (int) Math.ceil(mr.getRight() / itemWeight); // weighted max quantity
entryValues.add(values);
}
}
return entryValues;
}
private static void updateMobsArrowRange() {
System.out.print("Generating updated ranges... ");
Connection con = DatabaseConnection.getConnection();
final Connection con = SimpleDatabaseConnection.getConnection();
Map<Integer, List<Integer>> existingEntries = new HashMap<>(200);
try {
// select all arrow drop entries on the DB, to update their values
PreparedStatement ps = con.prepareStatement("SELECT dropperid, itemid FROM drop_data WHERE itemid >= " + minArrowId + " AND itemid <= " + maxArrowId + " ORDER BY itemid;");
PreparedStatement ps = con.prepareStatement("SELECT dropperid, itemid FROM drop_data WHERE itemid >= " + MIN_ARROW_ID + " AND itemid <= " + MAX_ARROW_ID + " ORDER BY itemid;");
ResultSet rs = ps.executeQuery();
if (rs.isBeforeFirst()) {
while(rs.next()) {
while (rs.next()) {
int mobid = rs.getInt(1);
int itemid = rs.getInt(2);
if(mobRange.containsKey(mobid)) {
if (mobRange.containsKey(mobid)) {
List<Integer> em = existingEntries.get(mobid);
if(em == null) {
if (em == null) {
em = new ArrayList<>(2);
existingEntries.put(mobid, em);
}
em.add(itemid);
}
}
if(!existingEntries.isEmpty()) {
if (!existingEntries.isEmpty()) {
List<int[]> entryValues = getArrowEntryValues(existingEntries);
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(ToolConstants.getOutputFile(OUTPUT_FILE_NAME), StandardCharsets.UTF_8);
printSqlHeader();
for(int[] arrowEntry : entryValues) {
for (int[] arrowEntry : entryValues) {
updateSqlMobArrowMinEntry(arrowEntry);
}
printSqlMiddle();
for(int[] arrowEntry : entryValues) {
for (int[] arrowEntry : entryValues) {
updateSqlMobArrowMaxEntry(arrowEntry);
}
printSqlFooter();
printWriter.close();
} else {
throw new Exception("NO DATA");
}
} else {
throw new Exception("NO DATA");
}
rs.close();
ps.close();
con.close();
System.out.println("done!");
} catch(Exception e) {
if(e.getMessage() != null && e.getMessage().equals("NO DATA")) {
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().equals("NO DATA")) {
System.out.println("failed! The DB has no arrow entry to be updated.");
} else {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// load mob stats from WZ
mobStats = MapleLifeFactory.getAllMonsterStats();
mobStats = MonsterStatFetcher.getAllMonsterStats();
calcAllMobsArrowRange();
updateMobsArrowRange();
}

View File

@@ -0,0 +1,190 @@
package tools.mapletools;
import provider.wz.WZFiles;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* @author RonanLana
* <p>
* This application parses the Mob.wz file inputted and generates a report showing
* all cases where a mob has a boss HP bar and doesn't have a "boss" label.
* <p>
* Running it should generate a report file under "lib" folder with the search results.
*/
public class BossHpBarFetcher {
private static final String OUTPUT_FILE_NAME = "boss_hp_bar_report.txt";
private static final int INITIAL_STRING_LENGTH = 50;
private static final List<Integer> missingBosses = new ArrayList<>();
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int curBoss;
private static int curHpTag;
private static int curMobId;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("value");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void readMobLabel(String token) {
String name = getName(token);
String value = getValue(token);
switch (name) {
case "boss" -> curBoss = Integer.parseInt(value);
case "hpTagColor" -> curHpTag = Integer.parseInt(value);
}
}
private static void processMobData() {
if (curHpTag != Integer.MAX_VALUE && curBoss == Integer.MAX_VALUE) {
missingBosses.add(curMobId);
}
}
private static void translateToken(String token) {
String d;
if (token.contains("/imgdir")) {
status -= 1;
if (status == 1) {
processMobData();
}
} else if (token.contains("imgdir")) {
if (status == 0) {
String mobText = getName(token);
curMobId = Integer.parseInt(mobText.substring(0, mobText.lastIndexOf('.')));
} else if (status == 1) { //getting info
d = getName(token);
if (!d.contentEquals("info")) {
forwardCursor(status);
} else {
curBoss = Integer.MAX_VALUE;
curHpTag = Integer.MAX_VALUE;
}
} else if (status == 2) {
forwardCursor(status);
}
status += 1;
} else {
if (status == 2) { //info tags
readMobLabel(token);
}
}
}
private static void readBossHpBarData() throws IOException {
String line;
final File mobDirectory = WZFiles.MOB.getFile();
for (File file : mobDirectory.listFiles()) {
if (file.isFile()) {
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
bufferedReader.close();
fileReader.close();
}
}
}
private static void printReportFileHeader(PrintWriter writer) {
writer.println(" # Report File autogenerated from the MapleBossHpBarFetcher feature by Ronan Lana.");
writer.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
writer.println();
}
private static void printReportFileResults(PrintWriter writer) {
for (int mobId : missingBosses) {
writer.println("Missing 'isBoss' on id: " + mobId);
}
}
private static void reportBossHpBarData() {
// This will reference one line at a time
try {
System.out.println("Reading WZs...");
readBossHpBarData();
System.out.println("Reporting results...");
final PrintWriter printWriter = new PrintWriter(ToolConstants.getOutputFile(OUTPUT_FILE_NAME), StandardCharsets.UTF_8);
printReportFileHeader(printWriter);
printReportFileResults(printWriter);
printWriter.close();
System.out.println("Done!");
} catch (FileNotFoundException ex) {
System.out.println("Unable to open mob file.");
} catch (IOException ex) {
System.out.println("Error reading mob file.");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reportBossHpBarData();
}
}

View File

@@ -1,77 +1,49 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplecashcosmeticschecker;
import provider.wz.WZFiles;
import tools.Pair;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;
/**
*
* @author RonanLana
This application parses the cosmetic recipes defined within "lib/care" folder, loads
every present cosmetic itemid from the XML data, then checks the scripts for missed
cosmetics within the stylist/surgeon. Results from the search are reported in a report
file.
Note: to best make use of this feature, set ignoreCurrentScriptCosmetics = true. This
way, every available cosmetic present on the recipes will be listed on the report.
Estimated parse time: 1 minute
* <p>
* This application parses the cosmetic recipes defined within "lib/care" folder, loads
* every present cosmetic itemid from the XML data, then checks the scripts for missed
* cosmetics within the stylist/surgeon. Results from the search are reported in a report
* file.
* <p>
* Note: to best make use of this feature, set IGNORE_CURRENT_SCRIPT_COSMETICS = true. This
* way, every available cosmetic present on the recipes will be listed on the report.
* <p>
* Estimated parse time: 1 minute
*/
public class MapleCashCosmeticsChecker {
static String libPath = "lib";
static String handbookPath = "../../handbook";
static String wzPath = "../../wz";
static String scriptPath = "../../scripts";
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static boolean ignoreCurrentScriptCosmetics = false;
static int initialStringLength = 50;
static byte status = 0;
static Map<Integer, Set<Integer>> scriptCosmetics = new HashMap<>();
static Map<Integer, String> scriptEntries = new HashMap<>(500);
static Set<Integer> allCosmetics = new HashSet<>();
static Set<Integer> unusedCosmetics = new HashSet<>();
static Map<Integer, List<Integer>> usedCosmetics = new HashMap<>();
static Map<Integer, String> couponNames = new HashMap<>();
static Map<Integer, Integer> cosmeticNpcs = new HashMap<>(); // expected only 1 NPC per cosmetic coupon (town care/salon)
static Map<List<String>, Integer> cosmeticNpcids = new HashMap<>();
static Set<String> missingCosmeticNames = new HashSet<>();
static Map<String, Integer> cosmeticNameIds = new HashMap<>();
static Map<Integer, String> cosmeticIdNames = new HashMap<>();
static Map<Pair<Integer, String>, Set<Integer>> missingCosmeticsNpcTypes = new HashMap<>();
public class CashCosmeticsChecker {
private static final String INPUT_DIRECTORY_PATH = ToolConstants.getInputFile("care").getPath();
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("cash_cosmetics_result.txt");
private static final boolean IGNORE_CURRENT_SCRIPT_COSMETICS = false; // Toggle to preference
private static final int INITIAL_STRING_LENGTH = 50;
private static final Map<Integer, Set<Integer>> scriptCosmetics = new HashMap<>();
private static final Map<Integer, String> scriptEntries = new HashMap<>(500);
private static final Set<Integer> allCosmetics = new HashSet<>();
private static final Set<Integer> unusedCosmetics = new HashSet<>();
private static final Map<Integer, List<Integer>> usedCosmetics = new HashMap<>();
private static final Map<Integer, String> couponNames = new HashMap<>();
private static final Map<Integer, Integer> cosmeticNpcs = new HashMap<>(); // expected only 1 NPC per cosmetic coupon (town care/salon)
private static final Map<List<String>, Integer> cosmeticNpcids = new HashMap<>();
private static final Set<String> missingCosmeticNames = new HashSet<>();
private static final Map<String, Integer> cosmeticNameIds = new HashMap<>();
private static final Map<Integer, String> cosmeticIdNames = new HashMap<>();
private static final Map<Pair<Integer, String>, Set<Integer>> missingCosmeticsNpcTypes = new HashMap<>();
private static PrintWriter printWriter = null;
private static InputStreamReader fileReader = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static String getName(String token) {
int i, j;
char[] dest;
@@ -81,13 +53,13 @@ public class MapleCashCosmeticsChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -97,84 +69,81 @@ public class MapleCashCosmeticsChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void translateToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
if (status == 3) {
String d = getName(token);
if (!(d.contentEquals("Face") || d.contentEquals("Hair"))) {
forwardCursor(status);
}
} else if (status == 4) {
String d = getName(token);
int itemid = Integer.valueOf(d);
int itemid = Integer.parseInt(d);
int cosmeticid;
if (itemid >= 30000) {
cosmeticid = (itemid / 10) * 10;
} else {
cosmeticid = itemid - ((itemid / 100) % 10) * 100;
}
allCosmetics.add(cosmeticid);
forwardCursor(status);
}
}
}
private static void readEqpStringData(String eqpStringDirectory) throws IOException {
String line;
fileReader = new InputStreamReader(new FileInputStream(eqpStringDirectory), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(eqpStringDirectory), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
bufferedReader.close();
fileReader.close();
}
private static void loadCosmeticWzData() throws IOException {
System.out.println("Reading String.wz ...");
readEqpStringData(wzPath + "/String.wz/Eqp.img.xml");
readEqpStringData(WZFiles.STRING.getFilePath() + "/Eqp.img.xml");
}
private static void setCosmeticUsage(List<Integer> usedByNpcids, int cosmeticid) {
if (!usedByNpcids.isEmpty()) {
usedCosmetics.put(cosmeticid, usedByNpcids);
@@ -182,7 +151,7 @@ public class MapleCashCosmeticsChecker {
unusedCosmetics.add(cosmeticid);
}
}
private static void listFiles(String directoryName, ArrayList<File> files) {
File directory = new File(directoryName);
@@ -196,60 +165,60 @@ public class MapleCashCosmeticsChecker {
}
}
}
private static int getNpcIdFromFilename(String name) {
try {
return Integer.valueOf(name.substring(0, name.indexOf('.')));
} catch(Exception e) {
return Integer.parseInt(name.substring(0, name.indexOf('.')));
} catch (Exception e) {
return -1;
}
}
private static List<Integer> findCosmeticDataNpcids(int itemid) {
List<Integer> npcids = new LinkedList<>();
for (Entry<Integer, Set<Integer>> sc : scriptCosmetics.entrySet()) {
for (Map.Entry<Integer, Set<Integer>> sc : scriptCosmetics.entrySet()) {
if (sc.getValue().contains(itemid)) {
npcids.add(itemid);
}
}
return npcids;
}
private static void loadScripts() throws IOException {
ArrayList<File> files = new ArrayList<>();
listFiles(scriptPath + "/npc", files);
listFiles(ToolConstants.SCRIPTS_PATH + "/npc", files);
for(File f : files) {
for (File f : files) {
Integer npcid = getNpcIdFromFilename(f.getName());
//System.out.println("Parsing " + f.getAbsolutePath());
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line;
StringBuilder stringBuffer = new StringBuilder();
boolean cosmeticNpc = false;
Set<Integer> cosmeticids = new HashSet<>();
while((line = bufferedReader.readLine())!=null){
while ((line = bufferedReader.readLine()) != null) {
String[] s = line.split("hair_. = Array\\(", 2);
if (s.length > 1) {
cosmeticNpc = true;
s = s[1].split("\\)", 2);
s = s[0].split(", ");
for (String st : s) {
if (!st.isEmpty()) {
int itemid = Integer.valueOf(st);
int itemid = Integer.parseInt(st);
cosmeticids.add(itemid);
}
}
} else {
s = line.split("face_. = Array\\(", 2);
if (s.length > 1) {
cosmeticNpc = true;
s = s[1].split("\\)", 2);
@@ -257,18 +226,18 @@ public class MapleCashCosmeticsChecker {
for (String st : s) {
if (!st.isEmpty()) {
int itemid = Integer.valueOf(st);
int itemid = Integer.parseInt(st);
cosmeticids.add(itemid);
}
}
}
}
stringBuffer.append(line).append("\n");
}
scriptEntries.put(npcid, stringBuffer.toString());
if (cosmeticNpc) {
scriptCosmetics.put(npcid, cosmeticids);
}
@@ -277,72 +246,74 @@ public class MapleCashCosmeticsChecker {
fileReader.close();
}
}
private static void processCosmeticScriptData() throws IOException {
System.out.println("Reading script files ...");
loadScripts();
if (ignoreCurrentScriptCosmetics) {
if (IGNORE_CURRENT_SCRIPT_COSMETICS) {
for (Set<Integer> npcCosmetics : scriptCosmetics.values()) {
npcCosmetics.clear();
}
}
for (Integer itemid : allCosmetics) {
List<Integer> npcids = findCosmeticDataNpcids(itemid);
setCosmeticUsage(npcids, itemid);
}
}
private static List<Integer> loadCosmeticCouponids() throws IOException {
List<Integer> couponItemids = new LinkedList<>();
fileReader = new InputStreamReader(new FileInputStream(handbookPath + "/Cash.txt"), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(getHandbookFileName("/Cash.txt")), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line;
while((line = bufferedReader.readLine())!=null){
if (line.isEmpty()) continue;
while ((line = bufferedReader.readLine()) != null) {
if (line.isEmpty()) {
continue;
}
String[] s = line.split(" - ", 3);
int itemid = Integer.valueOf(s[0]);
int itemid = Integer.parseInt(s[0]);
if (itemid >= 5150000 && itemid < 5160000) {
couponItemids.add(itemid);
couponNames.put(itemid, s[1]);
}
}
bufferedReader.close();
fileReader.close();
return couponItemids;
}
private static List<Integer> findItemidOnScript(int itemid) {
List<Integer> files = new LinkedList<>();
String t = String.valueOf(itemid);
for (Entry<Integer, String> text : scriptEntries.entrySet()) {
for (Map.Entry<Integer, String> text : scriptEntries.entrySet()) {
if (text.getValue().contains(t)) {
files.add(text.getKey());
}
}
return files;
}
private static void loadCosmeticCouponNpcs() throws IOException {
System.out.println("Locating cosmetic NPCs ...");
for (Integer itemid : loadCosmeticCouponids()) {
List<Integer> npcids = findItemidOnScript(itemid);
if (!npcids.isEmpty()) {
cosmeticNpcs.put(itemid, npcids.get(0));
}
}
}
private enum CosmeticType {
HAIRSTYLE,
HAIRCOLOR,
@@ -351,11 +322,11 @@ public class MapleCashCosmeticsChecker {
EYE_COLOR,
SKIN_CARE
}
private static Pair<Integer, CosmeticType> parseCosmeticCoupon(String[] tokens) {
for (int i = 0; i < tokens.length; i++) {
String s = tokens[i];
if (s.startsWith("Hair")) {
if (s.contentEquals("Hairstyle")) {
return new Pair<>(i, CosmeticType.HAIRSTYLE);
@@ -378,10 +349,10 @@ public class MapleCashCosmeticsChecker {
return new Pair<>(i, CosmeticType.SKIN_CARE);
}
}
return null;
}
private static List<String> getCosmeticCouponData(String town, String type, String subtype) {
List<String> ret = new ArrayList<>(3);
ret.add(town);
@@ -389,77 +360,83 @@ public class MapleCashCosmeticsChecker {
ret.add(subtype);
return ret;
}
private static List<String> parseCosmeticCoupon(String couponName) {
String town, type, subtype = "EXP";
String[] s = couponName.split(" Coupon ", 2);
if (s.length > 1) {
subtype = s[1].substring(1, s[1].length() - 1);
}
String[] tokens = s[0].split(" ");
Pair<Integer, CosmeticType> cosmeticData = parseCosmeticCoupon(tokens);
if (cosmeticData == null) return null;
if (cosmeticData == null) {
return null;
}
town = "";
for (int i = 0; i < cosmeticData.left; i++) {
town += (tokens[i] + "_");
}
town = town.substring(0, town.length() - 1).toLowerCase();
switch (cosmeticData.right) {
case HAIRSTYLE:
type = "hair";
break;
case FACE_SURGERY:
type = "face";
break;
default:
return null;
}
return getCosmeticCouponData(town, type, subtype);
}
private static void generateCosmeticPlaceNpcs() {
for (Entry<Integer, String> e : couponNames.entrySet()) {
for (Map.Entry<Integer, String> e : couponNames.entrySet()) {
Integer npcid = cosmeticNpcs.get(e.getKey());
if (npcid == null) continue;
if (npcid == null) {
continue;
}
String couponName = e.getValue();
List<String> couponData = parseCosmeticCoupon(couponName);
if (couponData == null) continue;
if (couponData == null) {
continue;
}
cosmeticNpcids.put(couponData, npcid);
}
}
private static Integer getCosmeticNpcid(String townName, String typeCosmetic, String typeCoupon) {
return cosmeticNpcids.get(getCosmeticCouponData(townName, typeCosmetic, typeCoupon));
}
private static String getCosmeticName(String name, boolean gender) {
String ret = name + " (" + (gender ? "F" : "M") + ")";
return ret;
final String genderString = gender ? "F" : "M";
return String.format("%s (%s)", name, genderString);
}
private static void loadCosmeticNames(String cosmeticPath) throws IOException {
fileReader = new InputStreamReader(new FileInputStream(cosmeticPath), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(cosmeticPath), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line;
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
String[] s = line.split(" - ", 3);
int itemid = Integer.valueOf(s[0]);
int itemid = Integer.parseInt(s[0]);
String name;
if (itemid < 30000) {
itemid = itemid - ((itemid / 100) % 10) * 100;
int idx = s[1].lastIndexOf(" ");
if (idx > -1) {
name = s[1].substring(0, idx);
@@ -468,7 +445,7 @@ public class MapleCashCosmeticsChecker {
}
} else {
itemid = (Integer.valueOf(s[0]) / 10) * 10;
int idx = s[1].indexOf(" ");
if (idx > -1) {
name = s[1].substring(idx + 1);
@@ -476,17 +453,17 @@ public class MapleCashCosmeticsChecker {
name = s[1];
}
}
name = name.trim();
String cname = getCosmeticName(name, (((itemid / 1000) % 10) % 3) != 0);
/*
if (cosmeticNameIds.containsKey(cname) && Math.abs(cosmeticNameIds.get(cname) - itemid) > 50) {
System.out.println("Clashing '" + name + "' " + itemid + "/" + cosmeticNameIds.get(cname));
}
*/
cosmeticNameIds.put(cname, itemid);
cosmeticIdNames.put(itemid, name);
}
@@ -494,17 +471,21 @@ public class MapleCashCosmeticsChecker {
bufferedReader.close();
fileReader.close();
}
private static void loadCosmeticNames() throws IOException {
System.out.println("Reading cosmetics from handbook ...");
loadCosmeticNames(handbookPath + "/Equip/Face.txt");
loadCosmeticNames(handbookPath + "/Equip/Hair.txt");
loadCosmeticNames(getHandbookFileName("/Equip/Face.txt"));
loadCosmeticNames(getHandbookFileName("/Equip/Hair.txt"));
}
private static String getHandbookFileName(String fileName) {
return ToolConstants.HANDBOOK_PATH + fileName;
}
private static List<Integer> fetchExpectedCosmetics(String[] cosmeticList, boolean gender) {
List<Integer> list = new LinkedList<>();
for (String cosmetic : cosmeticList) {
String cname = getCosmeticName(cosmetic, gender);
Integer itemid = cosmeticNameIds.get(cname);
@@ -514,30 +495,30 @@ public class MapleCashCosmeticsChecker {
missingCosmeticNames.add(cosmetic);
}
}
return list;
}
private static void verifyCosmeticExpectedFile(File f) throws IOException {
String townName = f.getParent().substring(f.getParent().lastIndexOf("\\") + 1);
String typeCosmetic = f.getName().substring(0, f.getName().indexOf("."));
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line;
while((line = bufferedReader.readLine())!=null){
while ((line = bufferedReader.readLine()) != null) {
String[] s = line.split(": ", 2);
String[] t = s[0].split("ale ");
String typeCoupon = t[1];
boolean gender = !t[0].contentEquals("M");
Integer npcid = getCosmeticNpcid(townName, typeCosmetic, typeCoupon);
if (npcid != null) {
String[] cosmetics = s[1].split(", ");
List<Integer> cosmeticItemids = fetchExpectedCosmetics(cosmetics, gender);
Set<Integer> npcCosmetics = scriptCosmetics.get(npcid);
Set<Integer> missingCosmetics = new HashSet<>();
for (Integer itemid : cosmeticItemids) {
@@ -545,10 +526,10 @@ public class MapleCashCosmeticsChecker {
missingCosmetics.add(itemid);
}
}
if (!missingCosmetics.isEmpty()) {
Pair<Integer, String> key = new Pair<>(npcid, typeCoupon);
Set<Integer> list = missingCosmeticsNpcTypes.get(key);
if (list == null) {
missingCosmeticsNpcTypes.put(key, missingCosmetics);
@@ -558,34 +539,34 @@ public class MapleCashCosmeticsChecker {
}
}
}
bufferedReader.close();
fileReader.close();
}
private static void verifyCosmeticExpectedData() throws IOException {
System.out.println("Analyzing cosmetic NPC scripts ...");
ArrayList<File> cosmeticRecipes = new ArrayList<>();
listFiles(libPath + "/care", cosmeticRecipes);
listFiles(INPUT_DIRECTORY_PATH, cosmeticRecipes);
for (File f : cosmeticRecipes) {
verifyCosmeticExpectedFile(f);
}
}
private static List<Pair<Pair<Integer, String>, List<Integer>>> getSortedMapEntries(Map<Pair<Integer, String>, Set<Integer>> map) {
List<Pair<Pair<Integer, String>, List<Integer>>> list = new ArrayList<>(map.size());
for(Entry<Pair<Integer, String>, Set<Integer>> e : map.entrySet()) {
for (Map.Entry<Pair<Integer, String>, Set<Integer>> e : map.entrySet()) {
List<Integer> il = new ArrayList<>(2);
il.addAll(e.getValue());
Collections.sort(il, (o1, o2) -> o1 - o2);
il.sort((o1, o2) -> o1 - o2);
list.add(new Pair<>(e.getKey(), il));
}
Collections.sort(list, (o1, o2) -> {
list.sort((o1, o2) -> {
int cmp = o1.getLeft().getLeft() - o2.getLeft().getLeft();
if (cmp == 0) {
return o1.getLeft().getRight().compareTo(o2.getLeft().getRight());
@@ -593,20 +574,20 @@ public class MapleCashCosmeticsChecker {
return cmp;
}
});
return list;
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleCashCosmeticsChecker feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server source files and the server-side WZ.xmls.");
printWriter.println();
}
private static Pair<List<Integer>, List<Integer>> getCosmeticReport(List<Integer> itemids) {
List<Integer> maleItemids = new LinkedList<>();
List<Integer> femaleItemids = new LinkedList<>();
for (Integer i : itemids) {
if ((((i / 1000) % 10) % 3) == 0) {
maleItemids.add(i);
@@ -614,10 +595,10 @@ public class MapleCashCosmeticsChecker {
femaleItemids.add(i);
}
}
return new Pair<>(maleItemids, femaleItemids);
}
private static void reportNpcCosmetics(List<Integer> itemids) {
if (!itemids.isEmpty()) {
String res = " ";
@@ -625,75 +606,75 @@ public class MapleCashCosmeticsChecker {
res += (i + ", ");
unusedCosmetics.remove(i);
}
printWriter.println(res.substring(0, res.length() - 2));
}
}
private static void reportCosmeticResults() throws IOException {
System.out.println("Reporting results ...");
printWriter = new PrintWriter("lib/result.txt", "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
if (!missingCosmeticsNpcTypes.isEmpty()) {
printWriter.println("Found " + missingCosmeticsNpcTypes.size() + " entries with missing cosmetic entries.");
for (Pair<Pair<Integer, String>, List<Integer>> mcn : getSortedMapEntries(missingCosmeticsNpcTypes)) {
printWriter.println(" NPC " + mcn.getLeft());
Pair<List<Integer>, List<Integer>> genderItemids = getCosmeticReport(mcn.getRight());
reportNpcCosmetics(genderItemids.getLeft());
reportNpcCosmetics(genderItemids.getRight());
printWriter.println();
}
}
if (!unusedCosmetics.isEmpty()) {
printWriter.println("Unused cosmetics: " + unusedCosmetics.size());
List<Integer> list = new ArrayList<>(unusedCosmetics);
Collections.sort(list);
for (Integer i : list) {
printWriter.println(i + " " + cosmeticIdNames.get(i));
}
printWriter.println();
}
if (!missingCosmeticNames.isEmpty()) {
printWriter.println("Missing cosmetic itemids: " + missingCosmeticNames.size());
List<String> listString = new ArrayList<>(missingCosmeticNames);
Collections.sort(listString);
for (String c : listString) {
printWriter.println(c);
}
printWriter.println();
}
printWriter.close();
}
public static void main(String[] args) {
try {
loadCosmeticWzData();
processCosmeticScriptData();
loadCosmeticCouponNpcs();
generateCosmeticPlaceNpcs();
loadCosmeticNames();
verifyCosmeticExpectedData();
reportCosmeticResults();
System.out.println("Done!");
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
}

View File

@@ -1,57 +1,28 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplecashcosmeticsfetcher;
import server.MapleItemInformationProvider;
import tools.DatabaseConnection;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ArrayList;
import tools.MapleItemInformationProvider;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
*
* @author RonanLana
This application gathers info from the WZ.XML files, fetching all cosmetic coupons and tickets from there, and then
searches the NPC script files, identifying the stylish NPCs that supposedly uses them. It will reports all NPCs that
uses up a card, as well as report those currently unused.
Estimated parse time: 10 seconds
This application gathers info from the WZ.XML files, fetching all cosmetic coupons and tickets from there, and then
searches the NPC script files, identifying the stylish NPCs that supposedly uses them. It will reports all NPCs that
uses up a card, as well as report those currently unused.
Estimated parse time: 10 seconds
*/
public class MapleCashCosmeticsFetcher {
static MapleItemInformationProvider ii;
static String wzPath = "../../wz";
static String scriptPath = "../../scripts";
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static Map<Integer, String> scriptEntries = new HashMap<>(500);
public class CashCosmeticsFetcher {
private static final Map<Integer, String> scriptEntries = new HashMap<>(500);
private static void listFiles(String directoryName, ArrayList<File> files) {
File directory = new File(directoryName);
@@ -66,25 +37,25 @@ public class MapleCashCosmeticsFetcher {
}
}
}
private static int getNpcIdFromFilename(String name) {
try {
return Integer.valueOf(name.substring(0, name.indexOf('.')));
return Integer.parseInt(name.substring(0, name.indexOf('.')));
} catch(Exception e) {
return -1;
}
}
private static void loadScripts() throws Exception {
ArrayList<File> files = new ArrayList<>();
listFiles(scriptPath + "/npc", files);
listFiles(ToolConstants.SCRIPTS_PATH + "/npc", files);
for(File f : files) {
Integer npcid = getNpcIdFromFilename(f.getName());
//System.out.println("Parsing " + f.getAbsolutePath());
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
bufferedReader = new BufferedReader(fileReader);
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(fileReader);
StringBuilder stringBuffer = new StringBuilder();
String line;
@@ -92,28 +63,29 @@ public class MapleCashCosmeticsFetcher {
while((line = bufferedReader.readLine())!=null){
stringBuffer.append(line).append("\n");
}
scriptEntries.put(npcid, stringBuffer.toString());
bufferedReader.close();
fileReader.close();
}
}
private static List<Integer> findItemidOnScript(int itemid) {
List<Integer> files = new LinkedList<>();
String t = String.valueOf(itemid);
for (Entry<Integer, String> text : scriptEntries.entrySet()) {
for (Map.Entry<Integer, String> text : scriptEntries.entrySet()) {
if (text.getValue().contains(t)) {
files.add(text.getKey());
}
}
return files;
}
private static void reportCosmeticCouponResults() {
final MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
for (int itemid = 5150000; itemid <= 5154000; itemid++) {
String itemName = ii.getName(itemid);
@@ -128,18 +100,16 @@ public class MapleCashCosmeticsFetcher {
}
}
}
public static void main(String[] args) {
System.setProperty("wzpath", wzPath);
ii = MapleItemInformationProvider.getInstance();
DatabaseConnection.initializeConnectionPool(); // MapleItemInformationProvider loads unrelated stuff from the db
try {
loadScripts();
System.out.println("Loaded scripts");
reportCosmeticCouponResults();
} catch(Exception e) {
e.printStackTrace();
}
}
}
}

View File

@@ -1,96 +1,59 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
import tools.Pair;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplecashdropfetcher;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.io.File;
import tools.Pair;
import java.util.*;
/**
*
* @author RonanLana
This application gets info from the WZ.XML files regarding cash itemids then searches the drop data on the DB
after any NX (cash item) drops and reports them.
Estimated parse time: 2 minutes
* <p>
* This application gets info from the WZ.XML files regarding cash itemids then searches the drop data on the DB
* after any NX (cash item) drops and reports them.
* <p>
* Estimated parse time: 2 minutes
*/
public class MapleCashDropFetcher {
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
public class CashDropFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("cash_drop_report.txt");
private static final Connection con = SimpleDatabaseConnection.getConnection();
private static final int INITIAL_STRING_LENGTH = 50;
private static final int ITEM_FILE_NAME_SIZE = 13;
static String wzPath = "../../wz";
static String directoryName = "../..";
static String newFile = "lib/CashDropReport.txt";
private static final Set<Integer> nxItems = new HashSet<>();
private static final Set<Integer> nxDrops = new HashSet<>();
static Connection con = null;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static int initialLength = 200;
static int initialStringLength = 50;
static int itemFileNameSize = 13;
static Set<Integer> nxItems = new HashSet<>();
static Set<Integer> nxDrops = new HashSet<>();
static byte status = 0;
static int currentItemid = 0;
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int currentItemid = 0;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
if(j < i) return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
dest = new char[initialStringLength];
if (j < i) {
return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
}
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -100,115 +63,106 @@ public class MapleCashDropFetcher {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void inspectEquipWzEntry() {
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateEquipToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateEquipToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) {
if(!getName(token).equals("info")) {
} else if (token.contains("imgdir")) {
if (status == 1) {
if (!getName(token).equals("info")) {
forwardCursor(status);
}
}
status += 1;
}
else {
if(status == 2) {
} else {
if (status == 2) {
String d = getName(token);
if(d.equals("cash")) {
if(!getValue(token).equals("0")) {
if (d.equals("cash")) {
if (!getValue(token).equals("0")) {
nxItems.add(currentItemid);
}
forwardCursor(status);
}
}
}
}
private static void inspectItemWzEntry() {
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateItemToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateItemToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) {
currentItemid = Integer.valueOf(getName(token));
}
else if(status == 2) {
if(!getName(token).equals("info")) {
} else if (token.contains("imgdir")) {
if (status == 1) {
currentItemid = Integer.parseInt(getName(token));
} else if (status == 2) {
if (!getName(token).equals("info")) {
forwardCursor(status);
}
}
status += 1;
}
else {
if(status == 3) {
} else {
if (status == 3) {
String d = getName(token);
if(d.equals("cash")) {
if(!getValue(token).equals("0")) {
if (d.equals("cash")) {
if (!getValue(token).equals("0")) {
nxItems.add(currentItemid);
}
forwardCursor(status);
}
}
@@ -220,7 +174,7 @@ public class MapleCashDropFetcher {
printWriter.println(" # Generated data takes into account several data info from the underlying DB and the server-side WZ.xmls.");
printWriter.println();
}
private static void listFiles(String directoryName, ArrayList<File> files) {
File directory = new File(directoryName);
@@ -234,93 +188,92 @@ public class MapleCashDropFetcher {
}
}
}
private static int getItemIdFromFilename(String name) {
try {
return Integer.valueOf(name.substring(0, name.indexOf('.')));
} catch(Exception e) {
return Integer.parseInt(name.substring(0, name.indexOf('.')));
} catch (Exception e) {
return -1;
}
}
private static String getDropTableName(boolean dropdata) {
return (dropdata ? "drop_data" : "reactordrops");
}
private static String getDropElementName(boolean dropdata) {
return (dropdata ? "dropperid" : "reactorid");
}
private static void filterNxDropsOnDB(boolean dropdata) throws SQLException {
nxDrops.clear();
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT itemid FROM " + getDropTableName(dropdata));
ResultSet rs = ps.executeQuery();
while(rs.next()) {
while (rs.next()) {
int itemid = rs.getInt("itemid");
if(nxItems.contains(itemid)) {
if (nxItems.contains(itemid)) {
nxDrops.add(itemid);
}
}
rs.close();
ps.close();
}
private static List<Pair<Integer, Integer>> getNxDropsEntries(boolean dropdata) throws SQLException {
List<Pair<Integer, Integer>> entries = new ArrayList<>();
List<Integer> sortedNxDrops = new ArrayList<>(nxDrops);
Collections.sort(sortedNxDrops);
for(Integer nx : sortedNxDrops) {
for (Integer nx : sortedNxDrops) {
PreparedStatement ps = con.prepareStatement("SELECT " + getDropElementName(dropdata) + " FROM " + getDropTableName(dropdata) + " WHERE itemid = ?");
ps.setInt(1, nx);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
while (rs.next()) {
entries.add(new Pair<>(nx, rs.getInt(getDropElementName(dropdata))));
}
rs.close();
ps.close();
}
return entries;
}
private static void reportNxDropResults(boolean dropdata) throws SQLException {
filterNxDropsOnDB(dropdata);
if(!nxDrops.isEmpty()) {
if (!nxDrops.isEmpty()) {
List<Pair<Integer, Integer>> nxEntries = getNxDropsEntries(dropdata);
printWriter.println("NX DROPS ON " + getDropTableName(dropdata));
for(Pair<Integer, Integer> nx : nxEntries) {
for (Pair<Integer, Integer> nx : nxEntries) {
printWriter.println(nx.left + " : " + nx.right);
}
printWriter.println("\n\n\n");
}
}
private static void ReportNxDropData() {
private static void reportNxDropData() {
try {
Class.forName(driver).newInstance();
System.out.println("Reading Character.wz ...");
ArrayList<File> files = new ArrayList<>();
listFiles(wzPath + "/Character.wz", files);
for(File f : files) {
listFiles(WZFiles.CHARACTER.getFilePath(), files);
InputStreamReader fileReader = null;
for (File f : files) {
//System.out.println("Parsing " + f.getAbsolutePath());
int itemid = getItemIdFromFilename(f.getName());
if(itemid < 0) {
if (itemid < 0) {
continue;
}
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
currentItemid = itemid;
@@ -329,24 +282,24 @@ public class MapleCashDropFetcher {
bufferedReader.close();
fileReader.close();
}
System.out.println("Reading Item.wz ...");
files = new ArrayList<>();
listFiles(wzPath + "/Item.wz", files);
for(File f : files) {
listFiles(WZFiles.ITEM.getFilePath(), files);
for (File f : files) {
//System.out.println("Parsing " + f.getAbsolutePath());
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
if(f.getName().length() <= itemFileNameSize) {
if (f.getName().length() <= ITEM_FILE_NAME_SIZE) {
inspectItemWzEntry();
} else { // pet file structure is similar to equips, maybe there are other item-types following this behaviour?
int itemid = getItemIdFromFilename(f.getName());
if(itemid < 0) {
if (itemid < 0) {
continue;
}
currentItemid = itemid;
inspectEquipWzEntry();
}
@@ -354,53 +307,35 @@ public class MapleCashDropFetcher {
bufferedReader.close();
fileReader.close();
}
System.out.println("Reporting results...");
// filter drop data on DB
con = DriverManager.getConnection(host, username, password);
// report suspects of missing quest drop data, as well as those drop data that may have incorrect questids.
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
reportNxDropResults(true);
reportNxDropResults(false);
/*
printWriter.println("NX LIST"); // list of all cash items found
for(Integer nx : nxItems) {
printWriter.println(nx);
}
*/
con.close();
printWriter.close();
System.out.println("Done!");
}
catch(SQLException e) {
} catch (SQLException e) {
System.out.println("Warning: Could not establish connection to database to report quest data.");
System.out.println(e.getMessage());
}
catch(ClassNotFoundException e) {
System.out.println("Error: could not find class");
System.out.println(e.getMessage());
}
catch(InstantiationException e) {
System.out.println("Error: instantiation failure");
System.out.println(e.getMessage());
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ReportNxDropData();
reportNxDropData();
}
}

View File

@@ -1,57 +1,32 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplecashvegachecker;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author RonanLana
*
This application main objective is to read Vega-related information from
the item's description report back missing nodes for these items.
Estimated parse time: 10 seconds
*
This application main objective is to read Vega-related information from
the item's description report back missing nodes for these items.
Estimated parse time: 10 seconds
*/
public class MapleCashVegaChecker {
private static String wzPath = "../../wz";
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static int initialStringLength = 1000;
static int currentItem;
static byte status = 0;
static Set<Integer> vegaItems = new HashSet<>();
public class CashVegaChecker {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("vega_checker_report.txt");
private static final int INITIAL_STRING_LENGTH = 1000;
private static final Set<Integer> vegaItems = new HashSet<>();
private static PrintWriter printWriter = null;
private static InputStreamReader fileReader = null;
private static BufferedReader bufferedReader = null;
private static int currentItem;
private static byte status = 0;
private static String getName(String token) {
int i, j;
char[] dest;
@@ -61,13 +36,13 @@ public class MapleCashVegaChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -77,13 +52,13 @@ public class MapleCashVegaChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
}
private static void forwardCursor(int st) {
String line = null;
@@ -105,16 +80,16 @@ public class MapleCashVegaChecker {
status += 1;
}
}
private static void translateItemToken(String token) {
if(token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
status += 1;
if (status == 2) {
currentItem = Integer.valueOf(getName(token));
currentItem = Integer.parseInt(getName(token));
}
} else {
if (status == 2) {
@@ -124,7 +99,7 @@ public class MapleCashVegaChecker {
}
}
}
private static void translateVegaToken(String token) {
if(token.contains("/imgdir")) {
status -= 1;
@@ -139,11 +114,11 @@ public class MapleCashVegaChecker {
}
}
}
private static void readItemDescriptionFile(File f) {
System.out.print("Reading String.wz... ");
try {
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line;
@@ -158,11 +133,11 @@ public class MapleCashVegaChecker {
}
System.out.println(vegaItems.size() + " Vega Scroll items found");
}
private static void readVegaDescriptionFile(File f) {
System.out.println("Reading Etc.wz...");
try {
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line;
@@ -176,38 +151,38 @@ public class MapleCashVegaChecker {
ioe.printStackTrace();
}
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleCashVegaChecker feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static void reportMissingVegaItems() {
System.out.println("Reporting results ...");
try {
printWriter = new PrintWriter("lib/result.txt", "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
for (Integer itemid : vegaItems) {
printWriter.println(" " + itemid);
}
printWriter.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public static void main(String[] args) {
readItemDescriptionFile(new File(wzPath + "/String.wz/Consume.img.xml"));
readVegaDescriptionFile(new File(wzPath + "/Etc.wz/VegaSpell.img.xml"));
readItemDescriptionFile(new File(WZFiles.STRING.getFilePath() + "/Consume.img.xml"));
readVegaDescriptionFile(new File(WZFiles.ETC.getFilePath() + "/VegaSpell.img.xml"));
reportMissingVegaItems();
}
}

View File

@@ -1,79 +1,46 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import tools.Pair;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplecodecoupongenerator;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
*
* @author RonanLana
This application parses the coupon descriptor XML file and automatically generates
code entries on the DB reflecting the descriptions found. Parse time relies on the
sum of coupon codes created and amount of current codes on DB.
Estimated parse time: 2 minutes (for 100 code entries)
* <p>
* This application parses the coupon descriptor XML file and automatically generates
* code entries on the DB reflecting the descriptions found. Parse time relies on the
* sum of coupon codes created and amount of current codes on DB.
* <p>
* Estimated parse time: 2 minutes (for 100 code entries)
*/
public class MapleCodeCouponGenerator {
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
static Connection con = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static String fileName = "lib/CouponCodes.img.xml";
static long currentTime;
static int initialStringLength = 250;
static String name;
static boolean active;
static int quantity, duration;
static int maplePoint, nxCredit, nxPrepaid;
static List<Pair<Integer, Integer>> itemList = new ArrayList<>();
static Pair<Integer, Integer> item;
static List<CodeCouponDescriptor> activeCoupons = new ArrayList<>();
static List<Integer> generatedKeys;
static Set<String> usedCodes = new HashSet<>();
static byte status;
public class CodeCouponGenerator {
private static final File INPUT_FILE = ToolConstants.getInputFile("CouponCodes.img.xml");
private static final int INITIAL_STRING_LENGTH = 250;
private static final Connection con = SimpleDatabaseConnection.getConnection();
private static final List<CodeCouponDescriptor> activeCoupons = new ArrayList<>();
private static final Set<String> usedCodes = new HashSet<>();
private static final List<Pair<Integer, Integer>> itemList = new ArrayList<>();
private static BufferedReader bufferedReader = null;
private static long currentTime;
private static String name;
private static boolean active;
private static int quantity;
private static int duration;
private static int maplePoint;
private static int nxCredit;
private static int nxPrepaid;
private static Pair<Integer, Integer> item;
private static List<Integer> generatedKeys;
private static byte status;
private static void resetCouponPackage() {
name = null;
active = false;
@@ -84,17 +51,17 @@ public class MapleCodeCouponGenerator {
nxPrepaid = 0;
itemList.clear();
}
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
try {
token.getChars(i, j, dest, 0);
} catch (StringIndexOutOfBoundsException e) {
@@ -105,14 +72,13 @@ public class MapleCodeCouponGenerator {
e.printStackTrace();
try {
Thread.sleep(100000000);
} catch (Exception ex) {}
} catch (Exception ex) {
}
}
d = new String(dest);
return(d.trim());
return new String(dest).trim();
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -122,68 +88,64 @@ public class MapleCodeCouponGenerator {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void translateToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
if (status == 1) {
if (active) {
activeCoupons.add(new CodeCouponDescriptor(name, quantity, duration, maplePoint, nxCredit, nxPrepaid, itemList));
}
resetCouponPackage();
} else if (status == 3) {
itemList.add(item);
}
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
if (status == 4) {
item = new Pair<>(-1, -1);
} else if (status == 2) {
String d = getName(token);
System.out.println(" Reading coupon '" + d + "'");
name = d;
}
}
else {
} else {
String d = getName(token);
if (status == 2) {
switch (d) {
case "active":
if (Integer.valueOf(getValue(token)) == 0) {
if (Integer.parseInt(getValue(token)) == 0) {
forwardCursor(status);
resetCouponPackage();
} else {
@@ -192,19 +154,19 @@ public class MapleCodeCouponGenerator {
break;
case "quantity":
quantity = Integer.valueOf(getValue(token));
quantity = Integer.parseInt(getValue(token));
break;
case "duration":
duration = Integer.valueOf(getValue(token));
duration = Integer.parseInt(getValue(token));
break;
case "maplePoint":
maplePoint = Integer.valueOf(getValue(token));
maplePoint = Integer.parseInt(getValue(token));
break;
case "nxCredit":
nxCredit = Integer.valueOf(getValue(token));
nxCredit = Integer.parseInt(getValue(token));
break;
case "nxPrepaid":
nxPrepaid = Integer.valueOf(getValue(token));
nxPrepaid = Integer.parseInt(getValue(token));
break;
}
} else if (status == 4) {
@@ -219,13 +181,13 @@ public class MapleCodeCouponGenerator {
}
}
}
private static class CodeCouponDescriptor {
protected String name;
protected int quantity, duration;
protected int nxCredit, maplePoint, nxPrepaid;
protected List<Pair<Integer, Integer>> itemList;
protected CodeCouponDescriptor(String name, int quantity, int duration, int maplePoint, int nxCredit, int nxPrepaid, List<Pair<Integer, Integer>> itemList) {
this.name = name;
this.quantity = quantity;
@@ -233,109 +195,111 @@ public class MapleCodeCouponGenerator {
this.maplePoint = maplePoint;
this.nxCredit = nxCredit;
this.nxPrepaid = nxPrepaid;
this.itemList = new ArrayList<>(itemList);
}
}
private static String randomizeCouponCode() {
return Long.toHexString(Double.doubleToLongBits(Math.random())).substring(0, 15);
}
private static String generateCouponCode() {
String newCode;
do {
newCode = randomizeCouponCode();
} while (usedCodes.contains(newCode));
usedCodes.add(newCode);
return newCode;
}
private static List<Integer> getGeneratedKeys(PreparedStatement ps) throws SQLException {
if (generatedKeys == null) {
generatedKeys = new ArrayList<>();
ResultSet rs = ps.getGeneratedKeys();
while (rs.next()) {
generatedKeys.add(rs.getInt(1));
}
rs.close();
}
return generatedKeys;
}
private static void commitCodeCouponDescription(CodeCouponDescriptor recipe) throws SQLException {
if (recipe.quantity < 1) return;
if (recipe.quantity < 1) {
return;
}
System.out.println(" Generating coupon '" + recipe.name + "'");
generatedKeys = null;
PreparedStatement ps = con.prepareStatement("INSERT IGNORE INTO `nxcode` (`code`, `expiration`) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);
ps.setLong(2, currentTime + ((long) recipe.duration * 60 * 60 * 1000));
for(int i = 0; i < recipe.quantity; i++) {
for (int i = 0; i < recipe.quantity; i++) {
ps.setString(1, generateCouponCode());
ps.addBatch();
}
ps.executeBatch();
PreparedStatement ps2 = con.prepareStatement("INSERT IGNORE INTO `nxcode_items` (`codeid`, `type`, `item`, `quantity`) VALUES (?, ?, ?, ?)");
if (!recipe.itemList.isEmpty()) {
ps2.setInt(2, 5);
List<Integer> keys = getGeneratedKeys(ps);
for (Pair<Integer, Integer> p : recipe.itemList) {
ps2.setInt(3, p.getLeft());
ps2.setInt(4, p.getRight());
for (Integer codeid : keys) {
ps2.setInt(1, codeid);
ps2.addBatch();
}
}
}
ps2.setInt(3, 0);
if (recipe.nxCredit > 0) {
ps2.setInt(2, 0);
ps2.setInt(4, recipe.nxCredit);
List<Integer> keys = getGeneratedKeys(ps);
for(Integer codeid : keys) {
for (Integer codeid : keys) {
ps2.setInt(1, codeid);
ps2.addBatch();
}
}
if (recipe.maplePoint > 0) {
ps2.setInt(2, 1);
ps2.setInt(4, recipe.maplePoint);
List<Integer> keys = getGeneratedKeys(ps);
for(Integer codeid : keys) {
for (Integer codeid : keys) {
ps2.setInt(1, codeid);
ps2.addBatch();
}
}
if (recipe.nxPrepaid > 0) {
ps2.setInt(2, 2);
ps2.setInt(4, recipe.nxPrepaid);
List<Integer> keys = getGeneratedKeys(ps);
for(Integer codeid : keys) {
for (Integer codeid : keys) {
ps2.setInt(1, codeid);
ps2.addBatch();
}
}
ps2.executeBatch();
ps2.close();
ps.close();
}
private static void loadUsedCouponCodes() throws SQLException {
PreparedStatement ps = con.prepareStatement("SELECT code FROM nxcode", Statement.RETURN_GENERATED_KEYS);
ResultSet rs = ps.executeQuery();
@@ -345,51 +309,48 @@ public class MapleCodeCouponGenerator {
rs.close();
ps.close();
}
private static void generateCodeCoupons(String fileName) throws IOException {
fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
private static void generateCodeCoupons(File file) throws IOException {
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
resetCouponPackage();
status = 0;
System.out.println("Reading XML coupon information...");
String line;
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
bufferedReader.close();
fileReader.close();
System.out.println();
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(host, username, password);
System.out.println("Loading DB coupon codes...");
loadUsedCouponCodes();
System.out.println();
System.out.println("Saving generated coupons...");
currentTime = System.currentTimeMillis();
for (CodeCouponDescriptor ccd : activeCoupons) {
commitCodeCouponDescription(ccd);
}
System.out.println();
con.close();
System.out.println("Done.");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException e) {
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
generateCodeCoupons(fileName);
} catch(IOException ex) {
System.out.println("Error reading file '" + fileName + "'");
generateCodeCoupons(INPUT_FILE);
} catch (IOException ex) {
System.out.println("Error reading file '" + INPUT_FILE.getAbsolutePath() + "'");
}
}
}

View File

@@ -0,0 +1,255 @@
package tools.mapletools;
import provider.wz.WZFiles;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author RonanLana
* <p>
* This application gathers information about the Cash Shop's EXP & DROP coupons,
* such as applied rates, active times of day and days of week and dumps them in
* a SQL table that the server will make use.
*/
public class CouponInstaller {
private static final File COUPON_INPUT_FILE_1 = new File(WZFiles.ITEM.getFilePath(), "/Cash/0521.img.xml");
private static final File COUPON_INPUT_FILE_2 = new File(WZFiles.ITEM.getFilePath(), "/Cash/0536.img.xml");
private static final Connection con = SimpleDatabaseConnection.getConnection();
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int itemId = -1;
private static int itemMultiplier = 1;
private static int startHour = -1;
private static int endHour = -1;
private static int activeDay = 0;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
if (i < 0) {
return "";
}
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[8];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d);
}
private static String getNodeValue(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("value=");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
if (j - i < 1) {
return "";
}
dest = new char[j - i];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d);
}
private static void forwardCursor(int st) {
String line = null;
try {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static int getDayOfWeek(String day) {
return switch (day) {
case "SUN" -> 1;
case "MON" -> 2;
case "TUE" -> 3;
case "WED" -> 4;
case "THU" -> 5;
case "FRI" -> 6;
case "SAT" -> 7;
default -> 0;
};
}
private static void processHourTimeString(String time) {
startHour = Integer.parseInt(time.substring(4, 6));
endHour = Integer.parseInt(time.substring(7, 9));
}
private static void processDayTimeString(String time) {
String day = time.substring(0, 3);
int d = getDayOfWeek(day);
activeDay |= (1 << d);
}
private static void loadTimeFromCoupon(int st) {
System.out.println("Loading coupon id " + itemId + ". Rate: " + itemMultiplier + "x.");
String line = null;
try {
startHour = -1;
endHour = -1;
activeDay = 0;
String time = null;
while ((line = bufferedReader.readLine()) != null) {
simpleToken(line);
if (status < st) {
break;
}
time = getNodeValue(line);
processDayTimeString(time);
simpleToken(line);
}
if (time != null) {
processHourTimeString(time);
PreparedStatement ps = con.prepareStatement("INSERT INTO nxcoupons (couponid, rate, activeday, starthour, endhour) VALUES (?, ?, ?, ?, ?)");
ps.setInt(1, itemId);
ps.setInt(2, itemMultiplier);
ps.setInt(3, activeDay);
ps.setInt(4, startHour);
ps.setInt(5, endHour);
ps.execute();
ps.close();
}
} catch (SQLException | IOException e) {
e.printStackTrace();
}
}
private static void translateToken(String token) {
String d;
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
if (status == 1) { //getting ItemId
d = getName(token);
itemId = Integer.parseInt(d);
} else if (status == 2) {
d = getName(token);
if (!d.contains("info")) {
forwardCursor(status);
}
} else if (status == 3) {
d = getName(token);
if (!d.contains("time")) {
forwardCursor(status);
} else {
loadTimeFromCoupon(status);
}
}
status += 1;
} else {
if (status == 3) {
d = getName(token);
if (d.contains("rate")) {
String r = getNodeValue(token);
double db = Double.parseDouble(r);
itemMultiplier = (int) db;
}
}
}
}
private static void installRateCoupons(File file) {
// This will reference one line at a time
String line = null;
try {
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
bufferedReader.close();
fileReader.close();
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + file + "'");
} catch (IOException ex) {
System.out.println("Error reading file '" + file + "'");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void installCouponsTable() {
try {
PreparedStatement ps = con.prepareStatement("DROP TABLE IF EXISTS `nxcoupons`;");
ps.execute();
ps.close();
ps = con.prepareStatement(
"""
CREATE TABLE IF NOT EXISTS `nxcoupons` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`couponid` int(11) NOT NULL DEFAULT '0',
`rate` int(11) NOT NULL DEFAULT '0',
`activeday` int(11) NOT NULL DEFAULT '0',
`starthour` int(11) NOT NULL DEFAULT '0',
`endhour` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;"""
);
ps.execute();
ps.close();
installRateCoupons(COUPON_INPUT_FILE_1);
installRateCoupons(COUPON_INPUT_FILE_2);
con.close();
} catch (SQLException e) {
System.out.println("Warning: Could not establish connection to database to change card chance rate.");
System.out.println(e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
installCouponsTable();
}
}

View File

@@ -1,70 +1,42 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapledojoupdate;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
*
* @author RonanLana
This application parses the Character.wz folder inputted and adds/updates the "info/level"
node on every known equipment id. This addition enables client-side view of the equipment
level attribute on every equipment in the game, given proper item visibility, be it from
own equipments or from other players.
Estimated parse time: 10 seconds
* <p>
* This application parses the Character.wz folder inputted and adds/updates the "info/level"
* node on every known equipment id. This addition enables client-side view of the equipment
* level attribute on every equipment in the game, given proper item visibility, be it from
* own equipments or from other players.
* <p>
* Estimated parse time: 10 seconds
*/
public class MapleDojoUpdate {
public class DojoUpdate {
private static final File INPUT_DIRECTORY = new File(WZFiles.MAP.getFile(), "/Map/Map9");
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("dojo-maps");
private static final int DOJO_MIN_MAP_ID = 925_020_100;
private static final int DOJO_MAX_MAP_ID = 925_033_804;
private static final int INITIAL_STRING_LENGTH = 250;
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static boolean isDojoMapid;
private static byte status;
static String dojoDirectory = "lib/original/";
static String outputDirectory = "lib/updated/";
static Connection con = null;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static int initialStringLength = 250;
static boolean isDojoMapid;
static byte status;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
try {
token.getChars(i, j, dest, 0);
} catch (StringIndexOutOfBoundsException e) {
@@ -75,59 +47,56 @@ public class MapleDojoUpdate {
e.printStackTrace();
try {
Thread.sleep(100000000);
} catch (Exception ex) {}
} catch (Exception ex) {
}
}
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
printWriter.println(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void translateToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
printWriter.println(token);
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
printWriter.println(token);
status += 1;
if (status == 2) {
String d = getName(token);
if(!d.contentEquals("info")) {
if (!d.contentEquals("info")) {
forwardCursor(status);
}
} else if (status > 2) {
forwardCursor(status);
}
}
else {
} else {
if (status == 2 && isDojoMapid) {
String item = getName(token);
if (item.contentEquals("onFirstUserEnter")) {
printWriter.println(" <string name=\"onFirstUserEnter\" value=\"dojang_1st\"/>");
} else if (item.contentEquals("onUserEnter")) {
@@ -140,24 +109,27 @@ public class MapleDojoUpdate {
}
}
}
private static int getMapId(String fileName) {
return Integer.parseInt(fileName.substring(0, 9));
}
private static void parseDojoData(File file, String curPath) throws IOException {
printWriter = new PrintWriter(outputDirectory + curPath + file.getName(), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
int mapId = getMapId(file.getName());
isDojoMapid = isDojoMapId(mapId);
if (!isDojoMapid) {
return;
}
printWriter = new PrintWriter(OUTPUT_DIRECTORY.getPath() + "/" + curPath + file.getName(), StandardCharsets.UTF_8);
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
status = 0;
int mapid = getMapId(file.getName());
isDojoMapid = mapid >= 925020100 && mapid < 925040000;
String line;
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
@@ -167,35 +139,35 @@ public class MapleDojoUpdate {
printFileFooter();
printWriter.close();
}
private static boolean isDojoMapId(int mapId) {
return mapId >= DOJO_MIN_MAP_ID && mapId <= DOJO_MAX_MAP_ID;
}
private static void printFileFooter() {
printWriter.println("<!--");
printWriter.println(" # WZ XML File updated by the MapleDojoUpdate feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account info from the server-side WZ.xmls.");
printWriter.println("-->");
}
private static void parseDirectoryDojoData(String curPath) {
File folder = new File(outputDirectory + curPath);
File folder = new File(OUTPUT_DIRECTORY, curPath);
if (!folder.exists()) {
folder.mkdir();
}
System.out.println("Parsing directory '" + curPath + "'");
folder = new File(dojoDirectory + curPath);
folder = new File(INPUT_DIRECTORY, curPath);
for (File file : folder.listFiles()) {
if (file.isFile()) {
try {
parseDojoData(file, curPath);
}
catch(FileNotFoundException ex) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open dojo file " + file.getAbsolutePath() + ".");
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println("Error reading dojo file " + file.getAbsolutePath() + ".");
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
} else {
@@ -203,9 +175,8 @@ public class MapleDojoUpdate {
}
}
}
public static void main(String[] args) {
parseDirectoryDojoData("");
}
}

View File

@@ -1,72 +1,38 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleemptyitemwzchecker;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
*
* @author RonanLana
*
This application has two objectives: it reports in a detailed file all itemids which is
currently missing either a name entry in the String.wz or an item entry in the Item.wz;
And it removes from the String.wz XMLs all entries which misses properties on Item.wz.
* <p>
* This application has two objectives: it reports in a detailed file all itemids which is
* currently missing either a name entry in the String.wz or an item entry in the Item.wz;
* And it removes from the String.wz XMLs all entries which misses properties on Item.wz.
*/
public class MapleEmptyItemWzChecker {
static String newFile = "lib/Report.txt";
static String outputWzPath = "lib";
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static String wzPath = "../../wz";
static String handbookPath = "../../handbook";
static int initialStringLength = 50;
static int itemFileNameSize = 13;
static byte status = 0;
static int currentItemid = 0;
static int currentDepth = 0;
static Stack<String> currentPath = new Stack<>();
static String currentFile;
static Map<Integer, String> stringWzItems = new HashMap<>();
static Map<Integer, String> contentWzItems = new HashMap<>();
static Set<Integer> handbookItems = new HashSet<>();
static Set<Integer> nonPropItems;
public class EmptyItemWzChecker {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("empty_item_wz_report.txt");
private static final String OUTPUT_PATH = ToolConstants.OUTPUT_DIRECTORY.getPath();
private static final int INITIAL_STRING_LENGTH = 50;
private static final int ITEM_FILE_NAME_SIZE = 13;
private static final Stack<String> currentPath = new Stack<>();
private static final Map<Integer, String> stringWzItems = new HashMap<>();
private static final Map<Integer, String> contentWzItems = new HashMap<>();
private static final Set<Integer> handbookItems = new HashSet<>();
private static PrintWriter printWriter = null;
private static InputStreamReader fileReader = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int currentItemid = 0;
private static int currentDepth = 0;
private static String currentFile;
private static Set<Integer> nonPropItems;
private static String getName(String token) {
int i, j;
char[] dest;
@@ -76,35 +42,33 @@ public class MapleEmptyItemWzChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void listFiles(String directoryName, ArrayList<File> files) {
File directory = new File(directoryName);
@@ -118,137 +82,135 @@ public class MapleEmptyItemWzChecker {
}
}
}
private static int getItemIdFromFilename(String name) {
try {
return Integer.valueOf(name.substring(0, name.indexOf('.')));
} catch(Exception e) {
return Integer.parseInt(name.substring(0, name.indexOf('.')));
} catch (Exception e) {
return -1;
}
}
private static void inspectItemWzEntry() {
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateItemToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static String currentItemPath() {
String s = currentFile + " -> ";
for (String p : currentPath) {
s += (p + "\\");
}
return s;
}
private static void translateItemToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
currentPath.pop();
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
String d = getName(token);
if(status == 2) {
currentItemid = Integer.valueOf(d);
if (status == 2) {
currentItemid = Integer.parseInt(d);
contentWzItems.put(currentItemid, currentItemPath());
forwardCursor(status);
} else {
currentPath.push(d);
}
}
}
private static void inspectStringWzEntry() {
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateStringToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateStringToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
currentPath.pop();
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
String d = getName(token);
if(status == currentDepth) {
currentItemid = Integer.valueOf(d);
if (status == currentDepth) {
currentItemid = Integer.parseInt(d);
stringWzItems.put(currentItemid, currentItemPath());
//if (currentItemid >= 4000000) System.out.println(" " + currentItemid);
forwardCursor(status);
} else {
currentPath.push(d);
}
}
}
private static void loadStringWzFile(String filePath, int depth) throws IOException {
fileReader = new InputStreamReader(new FileInputStream(filePath), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
currentFile = filePath;
currentDepth = 2 + depth;
//System.out.println(filePath + " depth " + depth);
inspectStringWzEntry();
bufferedReader.close();
fileReader.close();
}
private static void loadStringWz() throws IOException {
System.out.println("Reading String.wz ...");
String stringWzFiles[][] = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}};
String stringWzPath = wzPath + "/String.wz/";
String[][] stringWzFiles = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}};
for (int i = 0; i < stringWzFiles.length; i++) {
for (String dirFile : stringWzFiles[i]) {
loadStringWzFile(stringWzPath + dirFile + ".img.xml", i);
final String fileName = "/" + dirFile + ".img.xml";
loadStringWzFile(WZFiles.STRING.getFilePath() + fileName, i);
}
}
}
private static void loadItemWz() throws IOException {
System.out.println("Reading Item.wz ...");
ArrayList<File> files = new ArrayList<>();
listFiles(wzPath + "/Item.wz", files);
listFiles(WZFiles.ITEM.getFilePath(), files);
for (File f : files) {
if (f.getParentFile().getName().contentEquals("Special")) {
continue;
}
for(File f : files) {
if (f.getParentFile().getName().contentEquals("Special")) continue;
//System.out.println("Parsing " + f.getAbsolutePath());
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
currentFile = f.getCanonicalPath();
if(f.getName().length() <= itemFileNameSize) {
if (f.getName().length() <= ITEM_FILE_NAME_SIZE) {
inspectItemWzEntry();
} else { // pet file structure is similar to equips, maybe there are other item-types following this behaviour?
int itemid = getItemIdFromFilename(f.getName());
if(itemid < 0) {
if (itemid < 0) {
continue;
}
@@ -260,17 +222,19 @@ public class MapleEmptyItemWzChecker {
fileReader.close();
}
}
private static void loadCharacterWz() throws IOException {
System.out.println("Reading Character.wz ...");
ArrayList<File> files = new ArrayList<>();
listFiles(wzPath + "/Character.wz", files);
listFiles(WZFiles.CHARACTER.getFilePath(), files);
for (File f : files) {
if (f.getParentFile().getName().contentEquals("Character.wz")) {
continue;
}
for(File f : files) {
if (f.getParentFile().getName().contentEquals("Character.wz")) continue;
int itemid = getItemIdFromFilename(f.getName());
if(itemid < 0) {
if (itemid < 0) {
continue;
}
@@ -279,57 +243,56 @@ public class MapleEmptyItemWzChecker {
contentWzItems.put(currentItemid, currentItemPath());
}
}
private static void calculateItemNameDiff(Set<Integer> emptyItemNames, Set<Integer> emptyNameItems) {
for (Integer i : contentWzItems.keySet()) {
if (!stringWzItems.containsKey(i)) {
emptyNameItems.add(i);
}
}
for (Integer i : stringWzItems.keySet()) {
if (!contentWzItems.containsKey(i)) {
emptyItemNames.add(i);
}
}
}
private static void readHandbookItems() throws IOException {
System.out.println("Reading handbook ...");
String[] handbookPaths = {"Equip", "Cash.txt", "Etc.txt", "Pet.txt", "Setup.txt", "Use.txt"};
for (String path : handbookPaths) {
readHandbookPath(handbookPath + "/" + path);
readHandbookPath(ToolConstants.HANDBOOK_PATH + "/" + path);
}
}
private static void readHandbookPath(String filePath) throws IOException {
ArrayList<File> files = new ArrayList<>();
File testFile = new File(filePath);
if (testFile.isDirectory()) {
listFiles(filePath, files);
} else {
files.add(testFile);
}
for (File f : files) {
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
String[] tokens = line.split(" - ");
if (tokens[0].length() > 0) {
int itemid = Integer.valueOf(tokens[0]);
int itemid = Integer.parseInt(tokens[0]);
handbookItems.add(itemid);
}
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
@@ -337,60 +300,60 @@ public class MapleEmptyItemWzChecker {
fileReader.close();
}
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleEmptyItemWzChecker feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static List<Integer> getSortedItems(Set<Integer> items) {
List<Integer> sortedItems = new ArrayList<>(items);
Collections.sort(sortedItems);
return sortedItems;
}
private static void printReportFileResults(Set<Integer> emptyItemNames, Set<Integer> emptyNameItems) {
if (!emptyItemNames.isEmpty()) {
printWriter.println("String.wz NAMES with no Item.wz node, " + emptyItemNames.size() + " entries:");
for(Integer itemid : getSortedItems(emptyItemNames)) {
for (Integer itemid : getSortedItems(emptyItemNames)) {
printWriter.println(" " + itemid + " " + stringWzItems.get(itemid) + (handbookItems.contains(itemid) ? "" : " NOT FOUND"));
}
printWriter.println();
}
if (!emptyNameItems.isEmpty()) {
printWriter.println("Item.wz ITEMS with no String.wz node, " + emptyNameItems.size() + " entries:");
for(Integer itemid : getSortedItems(emptyNameItems)) {
for (Integer itemid : getSortedItems(emptyNameItems)) {
printWriter.println(" " + itemid + " " + contentWzItems.get(itemid) + (handbookItems.contains(itemid) ? "" : " NOT FOUND"));
}
printWriter.println();
}
}
private static void reportItemNameDiff(Set<Integer> emptyItemNames, Set<Integer> emptyNameItems) throws IOException {
System.out.println("Reporting results...");
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
printReportFileResults(emptyItemNames, emptyNameItems);
printWriter.close();
}
private static void locateItemStringWzDiff() throws IOException {
Set<Integer> emptyItemNames = new HashSet<>(), emptyNameItems = new HashSet<>();
calculateItemNameDiff(emptyItemNames, emptyNameItems);
reportItemNameDiff(emptyItemNames, emptyNameItems);
nonPropItems = emptyItemNames;
}
private static void runEmptyItemWzChecker() throws IOException {
readHandbookItems();
@@ -400,77 +363,75 @@ public class MapleEmptyItemWzChecker {
locateItemStringWzDiff();
}
private static void generateStringWzEntry() {
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
updateStringToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void updateStringToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
if (status == currentDepth && nonPropItems.contains(Integer.valueOf(getName(token)))) {
forwardCursor(status);
return;
}
}
printWriter.println(token);
}
private static void generateStringWzFile(String filePath, int depth) throws IOException {
fileReader = new InputStreamReader(new FileInputStream(wzPath + filePath), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(WZFiles.DIRECTORY + filePath), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
printWriter = new PrintWriter(outputWzPath + filePath, "UTF-8");
printWriter = new PrintWriter(OUTPUT_PATH + filePath, StandardCharsets.UTF_8);
currentDepth = 2 + depth;
//System.out.println(filePath + " depth " + depth);
generateStringWzEntry();
printWriter.close();
bufferedReader.close();
fileReader.close();
}
private static void generateStringWz() throws IOException {
System.out.println("Generating clean String.wz ...");
String stringWzFiles[][] = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}};
String[][] stringWzFiles = {{"Cash", "Consume", "Ins", "Pet"}, {"Etc"}, {"Eqp"}};
String stringWzPath = "/String.wz/";
File folder = new File(outputWzPath + "/String.wz/");
File folder = new File(OUTPUT_PATH + "/String.wz/");
if (!folder.exists()) {
folder.mkdirs();
}
for (int i = 0; i < stringWzFiles.length; i++) {
for (String dirFile : stringWzFiles[i]) {
generateStringWzFile(stringWzPath + dirFile + ".img.xml", i);
}
}
}
public static void main(String[] args) {
try {
runEmptyItemWzChecker();
generateStringWz();
System.out.println("Done!");
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}

View File

@@ -1,82 +1,51 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleequipmentomnileveler;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
*
* @author RonanLana
This application parses the Character.wz folder inputted and adds/updates the "info/level"
node on every known equipment id. This addition enables client-side view of the equipment
level attribute on every equipment in the game, given proper item visibility, be it from
own equipments or from other players.
Estimated parse time: 7 minutes
* <p>
* This application parses the Character.wz folder inputted and adds/updates the "info/level"
* node on every known equipment id. This addition enables client-side view of the equipment
* level attribute on every equipment in the game, given proper item visibility, be it from
* own equipments or from other players.
* <p>
* Estimated parse time: 7 minutes
*/
public class MapleEquipmentOmnileveler {
public class EquipmentOmniLeveller {
private static final File INPUT_DIRECTORY = WZFiles.CHARACTER.getFile();
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("equips-with-levels");
private static final int INITIAL_STRING_LENGTH = 250;
private static final int FIXED_EXP = 10000;
private static final int MAX_EQP_LEVEL = 30;
private static PrintWriter printWriter = null;
private static InputStreamReader fileReader = null;
private static BufferedReader bufferedReader = null;
private static int infoTagState = -1;
private static int infoTagExpState = -1;
private static boolean infoTagLevel;
private static boolean infoTagLevelExp;
private static boolean infoTagLevelInfo;
private static int parsedLevels = 0;
private static byte status;
private static boolean upgradeable;
private static boolean cash;
static String equipDirectory = "lib/original/";
static String outputDirectory = "lib/updated/";
static Connection con = null;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static int initialStringLength = 250;
static int fixedExp = 10000;
static int maxEqpLevel = 30;
static int infoTagState = -1, infoTagExpState = -1;
static boolean infoTagLevel;
static boolean infoTagLevelExp;
static boolean infoTagLevelInfo;
static int parsedLevels = 0;
static byte status;
static boolean upgradeable;
static boolean cash;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
try {
token.getChars(i, j, dest, 0);
} catch (StringIndexOutOfBoundsException e) {
@@ -87,14 +56,15 @@ public class MapleEquipmentOmnileveler {
e.printStackTrace();
try {
Thread.sleep(100000000);
} catch (Exception ex) {}
} catch (Exception ex) {
}
}
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -104,153 +74,147 @@ public class MapleEquipmentOmnileveler {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
printWriter.println(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateLevelCursor(int st) {
String line = null;
try {
infoTagLevelInfo = false;
while (status >= st && (line = bufferedReader.readLine()) != null) {
translateLevelToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateInfoTag(int st) {
infoTagLevel = false;
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) { // skipping directory & canvas definition
while (status >= st && (line = bufferedReader.readLine()) != null) { // skipping directory & canvas definition
translateInfoToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
if (!upgradeable || cash) {
throw new RuntimeException();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void printUpdatedLevelExp() {
printWriter.println(" <int name=\"exp\" value=\"" + fixedExp + "\"/>");
printWriter.println(" <int name=\"exp\" value=\"" + FIXED_EXP + "\"/>");
}
private static void printDefaultLevel(int level) {
printWriter.println(" <imgdir name=\"" + level + "\">");
printUpdatedLevelExp();
printWriter.println(" </imgdir>");
}
private static void printDefaultLevelInfoTag() {
printWriter.println(" <imgdir name=\"info\">");
for (int i = 1; i <= maxEqpLevel; i++) printDefaultLevel(i);
for (int i = 1; i <= MAX_EQP_LEVEL; i++) {
printDefaultLevel(i);
}
printWriter.println(" </imgdir>");
}
private static void printDefaultLevelTag() {
printWriter.println(" <imgdir name=\"level\">");
printDefaultLevelInfoTag();
printWriter.println(" </imgdir>");
}
private static void processLevelInfoTag(int st) {
String line;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
translateLevelExpToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void processLevelInfoSet(int st) {
parsedLevels = (1 << maxEqpLevel) - 1;
parsedLevels = (1 << MAX_EQP_LEVEL) - 1;
String line;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
translateLevelInfoSetToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateLevelToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
if (status == 3) {
if (!infoTagLevelInfo) {
printDefaultLevelInfoTag();
}
}
printWriter.println(token);
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
printWriter.println(token);
status += 1;
if (status == 4) {
String d = getName(token);
if(d.contentEquals("info")) {
if (d.contentEquals("info")) {
infoTagLevelInfo = true;
processLevelInfoSet(status);
} else {
forwardCursor(status);
}
}
}
else {
} else {
printWriter.println(token);
}
}
private static void translateLevelInfoSetToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
if (status == 3) {
if (parsedLevels != 0) {
for (int i = 0; i < maxEqpLevel; i++) {
for (int i = 0; i < MAX_EQP_LEVEL; i++) {
if ((parsedLevels >> i) % 2 != 0) {
int level = i + 1;
printDefaultLevel(level);
@@ -258,47 +222,43 @@ public class MapleEquipmentOmnileveler {
}
}
}
printWriter.println(token);
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
printWriter.println(token);
status += 1;
if (status == 5) {
int level = Integer.parseInt(getName(token)) - 1;
parsedLevels ^= (1 << level);
infoTagLevelExp = false;
infoTagExpState = status; // status: 5
processLevelInfoTag(status);
processLevelInfoTag(status);
infoTagExpState = -1;
}
}
else {
} else {
printWriter.println(token);
}
}
private static void translateLevelExpToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
if (status < infoTagExpState) {
if (!infoTagLevelExp) {
printUpdatedLevelExp();
}
}
printWriter.println(token);
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
printWriter.println(token);
status += 1;
forwardCursor(status);
}
else {
} else {
String name = getName(token);
if (name.contentEquals("exp")) {
infoTagLevelExp = true;
@@ -308,23 +268,22 @@ public class MapleEquipmentOmnileveler {
}
}
}
private static void translateInfoToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
if (status < infoTagState) {
if (!infoTagLevel) {
printDefaultLevelTag();
}
}
printWriter.println(token);
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
printWriter.println(token);
String d = getName(token);
if (d.contentEquals("level")) {
infoTagLevel = true;
@@ -332,17 +291,16 @@ public class MapleEquipmentOmnileveler {
} else {
forwardCursor(status);
}
}
else {
} else {
String name = getName(token);
switch(name) {
switch (name) {
case "cash":
if (!getValue(token).contentEquals("0")) {
cash = true;
}
break;
case "tuc":
case "incPAD":
case "incMAD":
@@ -363,25 +321,24 @@ public class MapleEquipmentOmnileveler {
}
break;
}
printWriter.println(token);
}
}
private static boolean translateToken(String token) {
boolean accessInfoTag = false;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
printWriter.println(token);
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
printWriter.println(token);
status += 1;
if (status == 2) {
String d = getName(token);
if(!d.contentEquals("info")) {
if (!d.contentEquals("info")) {
forwardCursor(status);
} else {
accessInfoTag = true;
@@ -389,22 +346,21 @@ public class MapleEquipmentOmnileveler {
} else if (status > 2) {
forwardCursor(status);
}
}
else {
} else {
printWriter.println(token);
}
return accessInfoTag;
}
private static void copyCashItemData(File file, String curPath) throws IOException {
printWriter = new PrintWriter(outputDirectory + curPath + file.getName(), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
printWriter = new PrintWriter(new File(OUTPUT_DIRECTORY, curPath + file.getName()), StandardCharsets.UTF_8);
fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
String line;
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
printWriter.println(line);
}
@@ -413,70 +369,66 @@ public class MapleEquipmentOmnileveler {
printWriter.close();
}
private static void parseEquipData(File file, String curPath) throws IOException {
printWriter = new PrintWriter(outputDirectory + curPath + file.getName(), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
printWriter = new PrintWriter(new File(OUTPUT_DIRECTORY, curPath + file.getName()), StandardCharsets.UTF_8);
fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
try {
status = 0;
upgradeable = false;
cash = false;
String line;
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
if (translateToken(line)) {
infoTagState = status; // status: 2
translateInfoTag(status);
infoTagState = -1;
}
}
bufferedReader.close();
fileReader.close();
printFileFooter();
printWriter.close();
} catch (RuntimeException e) {
bufferedReader.close();
fileReader.close();
printWriter.close();
copyCashItemData(file, curPath);
}
}
private static void printFileFooter() {
printWriter.println("<!--");
printWriter.println(" # WZ XML File parsed by the MapleEquipmentOmnilever feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account info from the server-side WZ.xmls.");
printWriter.println("-->");
}
private static void parseDirectoryEquipData(String curPath) {
File folder = new File(outputDirectory + curPath);
File folder = new File(OUTPUT_DIRECTORY, curPath);
if (!folder.exists()) {
folder.mkdir();
}
System.out.println("Parsing directory '" + curPath + "'");
folder = new File(equipDirectory + curPath);
folder = new File(INPUT_DIRECTORY, curPath);
for (File file : folder.listFiles()) {
if (file.isFile()) {
try {
parseEquipData(file, curPath);
}
catch(FileNotFoundException ex) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open equip file " + file.getAbsolutePath() + ".");
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println("Error reading equip file " + file.getAbsolutePath() + ".");
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
} else {
@@ -484,9 +436,8 @@ public class MapleEquipmentOmnileveler {
}
}
}
public static void main(String[] args) {
parseDirectoryEquipData("");
}
}

View File

@@ -0,0 +1,102 @@
package tools.mapletools;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author RonanLana
* <p>
* This application objective is to read all scripts from the event folder
* and fill empty functions for every function name not yet present in the
* script.
* <p>
* Estimated parse time: 10 seconds
*/
public class EventMethodFiller {
private static boolean foundMatchingDataOnFile(String fileContent, Pattern pattern) {
Matcher matcher = pattern.matcher(fileContent);
return matcher.find();
}
private static void fileSearchMatchingData(File file, Map<Pattern, String> functions) {
try {
String fileContent = FileUtils.readFileToString(file, "UTF-8");
List<String> fillFunctions = new LinkedList<>();
for (Map.Entry<Pattern, String> f : functions.entrySet()) {
if (!foundMatchingDataOnFile(fileContent, f.getKey())) {
fillFunctions.add(f.getValue());
}
}
if (!fillFunctions.isEmpty()) {
System.out.println("Filling out " + file.getName() + "...");
FileWriter fileWriter = new FileWriter(file, true);
PrintWriter printWriter = new PrintWriter(fileWriter);
printWriter.println();
printWriter.println();
printWriter.println("// ---------- FILLER FUNCTIONS ----------");
printWriter.println();
for (String s : fillFunctions) {
printWriter.println(s);
printWriter.println();
}
printWriter.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void filterDirectorySearchMatchingData(String directoryPath, Map<Pattern, String> functions) {
Iterator iter = FileUtils.iterateFiles(new File(directoryPath), new String[]{"sql", "js", "txt", "java"}, true);
while (iter.hasNext()) {
File file = (File) iter.next();
fileSearchMatchingData(file, functions);
}
}
private static Pattern compileJsFunctionPattern(String function) {
String jsFunction = "function(\\s)+";
return Pattern.compile(jsFunction + function);
}
private static Map<Pattern, String> getFunctions() {
Map<Pattern, String> functions = new HashMap<>();
functions.put(compileJsFunctionPattern("playerEntry"), "function playerEntry(eim, player) {}");
functions.put(compileJsFunctionPattern("playerExit"), "function playerExit(eim, player) {}");
functions.put(compileJsFunctionPattern("scheduledTimeout"), "function scheduledTimeout(eim) {}");
functions.put(compileJsFunctionPattern("playerUnregistered"), "function playerUnregistered(eim, player) {}");
functions.put(compileJsFunctionPattern("changedLeader"), "function changedLeader(eim, leader) {}");
functions.put(compileJsFunctionPattern("monsterKilled"), "function monsterKilled(mob, eim) {}");
functions.put(compileJsFunctionPattern("allMonstersDead"), "function allMonstersDead(eim) {}");
functions.put(compileJsFunctionPattern("playerDisconnected"), "function playerDisconnected(eim, player) {}");
functions.put(compileJsFunctionPattern("monsterValue"), "function monsterValue(eim, mobid) {return 0;}");
functions.put(compileJsFunctionPattern("dispose"), "function dispose() {}");
functions.put(compileJsFunctionPattern("leftParty"), "function leftParty(eim, player) {}");
functions.put(compileJsFunctionPattern("disbandParty"), "function disbandParty(eim, player) {}");
functions.put(compileJsFunctionPattern("clearPQ"), "function clearPQ(eim) {}");
functions.put(compileJsFunctionPattern("afterSetup"), "function afterSetup(eim) {}");
functions.put(compileJsFunctionPattern("cancelSchedule"), "function cancelSchedule() {}");
functions.put(compileJsFunctionPattern("setup"), "function setup(eim, leaderid) {}");
//put(compileJsFunctionPattern("getEligibleParty"), "function getEligibleParty(party) {}"); not really needed
return functions;
}
public static void main(String[] args) {
filterDirectorySearchMatchingData(ToolConstants.SCRIPTS_PATH + "/event", getFunctions());
}
}

View File

@@ -1,68 +1,36 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplegachaponitemidretriever;
package tools.mapletools;
import java.io.*;
import java.sql.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author RonanLana
*
* <p>
* This application reads metadata for the gachapons found on the "gachapon_items.txt"
* recipe file, then checks up the Handbook DB (installed through MapleIdRetriever)
* and translates the item names from the recipe file into their respective itemids.
* The translated itemids are then stored in specific gachapon files inside the
* "lib/gachapons" folder.
*
* <p>
* Estimated parse time: 1 minute
*/
public class MapleGachaponItemidRetriever {
public class GachaponItemIdRetriever {
private static final File INPUT_FILE = ToolConstants.getInputFile("gachapon_items.txt");
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("gachapons");
private static final Connection con = SimpleDatabaseConnection.getConnection();
private static final Pattern pattern = Pattern.compile("(\\d*)%");
private static final int[] scrollsChances = new int[]{10, 15, 30, 60, 65, 70, 100};
private static final Map<GachaponScroll, List<Integer>> scrollItemids = new HashMap<>();
private static PrintWriter printWriter = null;
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
static Connection con = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static PrintWriter printWriter = null;
// ------- SET-UP section arguments --------
static String directoryName = "./handbook/";
// ------- SEARCH section arguments --------
static String inputName = "lib/gachapon_items.txt";
static String outputPath = "lib/gachapons/";
static Pattern p = Pattern.compile("(\\d*)%");
static int[] scrollsChances = new int[]{10, 15, 30, 60, 65, 70, 100};
static Map<GachaponScroll, List<Integer>> scrollItemids = new HashMap<>();
private static void insertGachaponScrollItemid(Integer id, String name, String description, boolean both) {
GachaponScroll gachaScroll = getGachaponScroll(name, description, both);
@@ -74,25 +42,25 @@ public class MapleGachaponItemidRetriever {
list.add(id);
}
private static void loadHandbookUseNames() throws SQLException {
PreparedStatement ps = con.prepareStatement("SELECT * FROM `handbook` WHERE `id` >= 2040000 AND `id` < 2050000 ORDER BY `id` ASC;");
ResultSet rs = ps.executeQuery();
while(rs.next()) {
while (rs.next()) {
Integer id = rs.getInt("id");
String name = rs.getString("name");
if (isUpgradeScroll(name)) {
String description = rs.getString("description");
insertGachaponScrollItemid(id, name, description, false);
insertGachaponScrollItemid(id, name, description, true);
}
}
rs.close();
ps.close();
/*
for (Entry<GachaponScroll, List<Integer>> e : scrollItemids.entrySet()) {
System.out.println(e);
@@ -100,41 +68,41 @@ public class MapleGachaponItemidRetriever {
System.out.println("------------");
*/
}
private static class GachaponScroll {
private String header;
private String target;
private String buff;
private int prop;
private GachaponScroll(GachaponScroll from, int prop) {
this.header = from.header;
this.target = from.target;
this.buff = from.buff;
this.prop = prop;
}
private GachaponScroll(String name, String description, boolean both) {
String[] params = name.split(" for ");
if (params.length < 3) {
return;
}
String header = both ? "scroll" : " " + params[0];
String target = params[1];
int prop = 0;
String buff = params[2];
Matcher m = p.matcher(buff);
Matcher m = pattern.matcher(buff);
if (m.find()) {
prop = Integer.valueOf(m.group(1));
prop = Integer.parseInt(m.group(1));
buff = buff.substring(0, m.start() - 1).trim();
} else {
m = p.matcher(description);
m = pattern.matcher(description);
if (m.find()) {
prop = Integer.valueOf(m.group(1));
prop = Integer.parseInt(m.group(1));
}
}
@@ -143,45 +111,54 @@ public class MapleGachaponItemidRetriever {
buff = buff.substring(0, idx);
}
buff = buff.replace(".", "");
this.header = header;
this.target = target;
this.buff = buff;
this.prop = prop;
}
@Override
@Override
public int hashCode() {
int result = prop ^ (prop >>> 32);
result = 31 * result + (header != null ? header.hashCode() : 0);
result = 31 * result + (header != null ? header.hashCode() : 0);
result = 31 * result + (target != null ? target.hashCode() : 0);
result = 31 * result + (buff != null ? buff.hashCode() : 0);
return result;
return result;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
GachaponScroll sc = (GachaponScroll) o;
if (header != null ? !header.equals(sc.header) : sc.header != null) return false;
if (target != null ? !target.equals(sc.target) : sc.target != null) return false;
if (buff != null ? !buff.equals(sc.buff) : sc.buff != null) return false;
if (prop != sc.prop) return false;
return true;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
GachaponScroll sc = (GachaponScroll) o;
if (header != null ? !header.equals(sc.header) : sc.header != null) {
return false;
}
if (target != null ? !target.equals(sc.target) : sc.target != null) {
return false;
}
if (buff != null ? !buff.equals(sc.buff) : sc.buff != null) {
return false;
}
return prop == sc.prop;
}
@Override
public String toString() {
return header + " for " + target + " for " + buff + " - " + prop + "%";
}
}
private static String getGachaponScrollResults(String line, boolean both) {
String str = "";
List<GachaponScroll> gachaScrollList;
GachaponScroll gachaScroll = getGachaponScroll(line, "", both);
if (gachaScroll.prop != 0) {
gachaScrollList = Collections.singletonList(gachaScroll);
@@ -192,7 +169,7 @@ public class MapleGachaponItemidRetriever {
gachaScrollList.add(new GachaponScroll(gachaScroll, prop));
}
}
for (GachaponScroll gs : gachaScrollList) {
List<Integer> gachaItemids = scrollItemids.get(gs);
if (gachaItemids != null) {
@@ -209,10 +186,10 @@ public class MapleGachaponItemidRetriever {
}
}
}
return str;
}
private static GachaponScroll getGachaponScroll(String name, String description, boolean both) {
name = name.toLowerCase();
name = name.replace("for acc ", "for accuracy ");
@@ -222,22 +199,22 @@ public class MapleGachaponItemidRetriever {
name = name.replace("for attack", "for att");
name = name.replace("1-handed", "one-handed");
name = name.replace("2-handed", "two-handed");
return new GachaponScroll(name, description, both);
}
private static boolean isUpgradeScroll(String name) {
return name.matches("^(([D|d]ark )?[S|s]croll for).*");
}
private static void fetchLineOnMapleHandbook(String line, String rarity) throws SQLException {
String str = "";
if (!isUpgradeScroll(line)) {
PreparedStatement ps = con.prepareStatement("SELECT `id` FROM `handbook` WHERE `name` LIKE ? COLLATE latin1_general_ci ORDER BY `id` ASC;");
PreparedStatement ps = con.prepareStatement("SELECT `id` FROM `handbook` WHERE `name` LIKE ? ORDER BY `id` ASC;");
ps.setString(1, line);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
while (rs.next()) {
int id = rs.getInt("id");
str += Integer.toString(id);
@@ -250,37 +227,37 @@ public class MapleGachaponItemidRetriever {
str += getGachaponScrollResults(line, false);
if (str.isEmpty()) {
str += getGachaponScrollResults(line, true);
if (str.isEmpty()) {
System.out.println("NONE for '" + line + "' : " + getGachaponScroll(line, "", false));
}
}
}
if (str.isEmpty()) {
str += line;
}
if (rarity != null) {
str += ("- " + rarity);
}
printWriter.println(str);
}
private static void fetchDataOnMapleHandbook() throws SQLException {
String line;
try {
fileReader = new InputStreamReader(new FileInputStream(inputName), "UTF-8");
bufferedReader = new BufferedReader(fileReader);
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(fileReader);
int skip = 0;
boolean lineHeader = false;
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
if (skip > 0) {
skip--;
if (lineHeader) {
if (!line.isEmpty()) {
lineHeader = false;
@@ -291,14 +268,19 @@ public class MapleGachaponItemidRetriever {
} else if (line.isEmpty()) {
printWriter.println("");
} else if (line.startsWith("Gachapon ")) {
String s[] = line.split("<EFBFBD> ");
String[] s = line.split("<EFBFBD> ");
String gachaponName = s[s.length - 1];
gachaponName = gachaponName.replace(" ", "_");
gachaponName = gachaponName.toLowerCase();
if (printWriter != null) printWriter.close();
printWriter = new PrintWriter(outputPath + gachaponName + ".txt", "UTF-8");
if (printWriter != null) {
printWriter.close();
}
File outputFile = new File(OUTPUT_DIRECTORY, gachaponName + ".txt");
setupDirectories(outputFile);
printWriter = new PrintWriter(outputFile, StandardCharsets.UTF_8);
skip = 2;
lineHeader = true;
} else if (line.startsWith(".")) {
@@ -316,44 +298,33 @@ public class MapleGachaponItemidRetriever {
}
}
if (printWriter != null) printWriter.close();
if (printWriter != null) {
printWriter.close();
}
bufferedReader.close();
fileReader.close();
}
catch(FileNotFoundException ex) {
System.out.println(ex.getMessage());
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
private static void setupDirectories(File file) {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
}
public static void main(String[] args) {
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(host, username, password);
loadHandbookUseNames();
fetchDataOnMapleHandbook();
con.close();
}
catch(SQLException e) {
} catch (SQLException e) {
System.out.println("Error: invalid SQL syntax");
System.out.println(e.getMessage());
}
catch(ClassNotFoundException e) {
System.out.println("Error: could not find class");
System.out.println(e.getMessage());
}
catch(InstantiationException | IllegalAccessException e) {
System.out.println("Error: instantiation failure");
System.out.println(e.getMessage());
}
}
}

View File

@@ -1,86 +1,56 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleidretriever;
package tools.mapletools;
import java.io.*;
import java.sql.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
/**
*
* @author RonanLana
*
* <p>
* This application acts two-way: first section sets up a table on the SQL Server with all the names used within MapleStory,
* and the second queries all the names placed inside "fetch.txt", returning in the same line order the ids of the elements.
* In case of multiple entries with the same name, multiple ids will be returned in the same line split by a simple space
* in ascending order. An empty line means that no entry with the given name in a line has been found.
*
* <p>
* IMPORTANT: this will fail for fetching MAP ID (you shouldn't be using this program for these, just checking them up in the
* handbook is enough anyway).
*
* Set whether you are first installing the handbook on the SQL Server (TRUE) or just fetching whatever is on your "fetch.txt"
* <p>
* Set whether you are first installing the handbook on the SQL Server (TRUE) or just fetching whatever is on your "fetch_ids.txt"
* file (FALSE) on the INSTALL_SQLTABLE property and build the project. With all done, run the Java executable.
*
* <p>
* Expected installing time: 30 minutes
*
*/
public class MapleIdRetriever {
private final static boolean INSTALL_SQLTABLE = true;
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
static Connection con = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static PrintWriter printWriter = null;
// ------- SET-UP section arguments --------
static String directoryName = "./handbook/";
// ------- SEARCH section arguments --------
static String inputName = "lib/fetch.txt";
static String outputName = "lib/result.txt";
private static void listFiles(String directoryName, ArrayList<File> files) {
File directory = new File(directoryName);
public class IdRetriever {
private static final boolean INSTALL_SQLTABLE = true;
private static final File INPUT_FILE = ToolConstants.getInputFile("fetch_ids.txt");
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("fetched_ids.txt");
private static final Connection con = SimpleDatabaseConnection.getConnection();
// get all the files from a directory
File[] fList = directory.listFiles();
for (File file : fList) {
if (file.isFile()) {
files.add(file);
} else if (file.isDirectory()) {
listFiles(file.getAbsolutePath(), files);
}
private static InputStreamReader fileReader = null;
private static BufferedReader bufferedReader = null;
private static void listFiles(String directoryName, ArrayList<File> files) {
File directory = new File(directoryName);
// get all the files from a directory
File[] fList = directory.listFiles();
for (File file : fList) {
if (file.isFile()) {
files.add(file);
} else if (file.isDirectory()) {
listFiles(file.getAbsolutePath(), files);
}
}
}
private static void parseMapleHandbookLine(String line) throws SQLException {
String[] tokens = line.split(" - ", 3);
if(tokens.length > 1) {
if (tokens.length > 1) {
PreparedStatement ps = con.prepareStatement("INSERT INTO `handbook` (`id`, `name`, `description`) VALUES (?, ?, ?)");
try {
ps.setInt(1, Integer.parseInt(tokens[0]));
@@ -91,135 +61,132 @@ public class MapleIdRetriever {
ps.setString(2, tokens[1]);
ps.setString(3, tokens.length > 2 ? tokens[2] : "");
ps.execute();
ps.close();
}
}
private static void parseMapleHandbookFile(File fileObj) throws SQLException {
if (shouldSkipParsingFile(fileObj.getName())) {
return;
}
String line;
try {
fileReader = new InputStreamReader(new FileInputStream(fileObj), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(fileObj), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
System.out.println("Parsing file '" + fileObj.getCanonicalPath() + "'.");
while((line = bufferedReader.readLine()) != null) {
parseMapleHandbookLine(line);
while ((line = bufferedReader.readLine()) != null) {
try {
parseMapleHandbookLine(line);
} catch (SQLException e) {
System.err.println("Failed to parse line: " + line);
throw e;
}
}
bufferedReader.close();
fileReader.close();
}
catch(FileNotFoundException ex) {
System.out.println(ex.getMessage());
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
// Quest.txt has different formatting: id is last token on the line, instead of the first
private static boolean shouldSkipParsingFile(String fileName) {
return "Quest.txt".equals(fileName);
}
private static void setupSqlTable() throws SQLException {
PreparedStatement ps = con.prepareStatement("DROP TABLE IF EXISTS `handbook`;");
ps.execute();
ps.close();
ps = con.prepareStatement("CREATE TABLE `handbook` ("
+ "`key` int(10) unsigned NOT NULL AUTO_INCREMENT,"
+ "`id` int(10) DEFAULT NULL,"
+ "`name` varchar(200) DEFAULT NULL,"
+ "`description` varchar(1000) DEFAULT '',"
+ "PRIMARY KEY (`key`)"
+ ") ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;");
+ ");");
ps.execute();
ps.close();
}
private static void parseMapleHandbook() throws SQLException {
ArrayList<File> files = new ArrayList<>();
listFiles(directoryName, files);
if(files.isEmpty()) return;
listFiles(ToolConstants.HANDBOOK_PATH, files);
if (files.isEmpty()) {
return;
}
setupSqlTable();
for(File f: files) {
for (File f : files) {
parseMapleHandbookFile(f);
}
}
private static void fetchDataOnMapleHandbook() throws SQLException {
String line;
try {
fileReader = new InputStreamReader(new FileInputStream(inputName), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
printWriter = new PrintWriter(outputName, "UTF-8");
while((line = bufferedReader.readLine()) != null) {
if(line.isEmpty()) {
PrintWriter printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
while ((line = bufferedReader.readLine()) != null) {
if (line.isEmpty()) {
printWriter.println("");
continue;
}
PreparedStatement ps = con.prepareStatement("SELECT `id` FROM `handbook` WHERE `name` LIKE ? COLLATE latin1_general_ci ORDER BY `id` ASC;");
PreparedStatement ps = con.prepareStatement("SELECT `id` FROM `handbook` WHERE `name` LIKE ? ORDER BY `id` ASC;");
ps.setString(1, line);
ResultSet rs = ps.executeQuery();
String str = "";
while(rs.next()) {
while (rs.next()) {
int id = rs.getInt("id");
str += Integer.toString(id);
str += " ";
}
rs.close();
ps.close();
printWriter.println(str);
}
printWriter.close();
bufferedReader.close();
fileReader.close();
}
catch(FileNotFoundException ex) {
System.out.println(ex.getMessage());
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
public static void main(String[] args) {
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(host, username, password);
if(INSTALL_SQLTABLE) parseMapleHandbook();
else fetchDataOnMapleHandbook();
public static void main(String[] args) {
try {
if (INSTALL_SQLTABLE) {
parseMapleHandbook();
} else {
fetchDataOnMapleHandbook();
}
con.close();
}
catch(SQLException e) {
} catch (SQLException e) {
System.out.println("Error: invalid SQL syntax");
System.out.println(e.getMessage());
}
catch(ClassNotFoundException e) {
System.out.println("Error: could not find class");
System.out.println(e.getMessage());
}
catch(InstantiationException | IllegalAccessException e) {
System.out.println("Error: instantiation failure");
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}

View File

@@ -17,7 +17,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleskillmakerfetcher;
package tools.mapletools;
import java.util.List;
@@ -25,7 +25,7 @@ import java.util.List;
*
* @author RonanLana
*/
public class MapleMakerItemEntry {
public class MakerItemEntry {
public int id = -1;
public int itemid = -1;
public int reqLevel = -1;
@@ -43,7 +43,7 @@ public class MapleMakerItemEntry {
public List<int[]> recipeList = null;
public List<int[]> randomList = null;
public MapleMakerItemEntry(int id, int itemid, int reqLevel, int reqMakerLevel, int reqItem, int reqMeso, int reqEquip, int catalyst, int quantity, int tuc, int recipeCount, int recipeItem, List<int[]> recipeList, List<int[]> randomList) {
MakerItemEntry(int id, int itemid, int reqLevel, int reqMakerLevel, int reqItem, int reqMeso, int reqEquip, int catalyst, int quantity, int tuc, int recipeCount, int recipeItem, List<int[]> recipeList, List<int[]> randomList) {
this.id = id;
this.itemid = itemid;
this.reqLevel = reqLevel;

View File

@@ -1,56 +1,25 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplemapfieldlimitchecker;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
/**
*
* @author RonanLana
*
This application seeks from the XMLs all mapid entries that holds the specified
fieldLimit.
* <p>
* This application seeks from the XMLs all mapid entries that holds the specified
* fieldLimit.
*/
public class MapleMapFieldLimitChecker {
static String newFile = "lib/Report.txt";
static String outputWzPath = "lib";
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static String wzPath = "../../wz";
static int initialStringLength = 50;
static int itemFileNameSize = 13;
static int fieldLimit = 0x400000;
static byte status = 0;
static int mapid = 0;
public class MapFieldLimitChecker {
private static final int INITIAL_STRING_LENGTH = 50;
private static final int FIELD_LIMIT = 0x400000;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int mapid = 0;
private static String getName(String token) {
int i, j;
char[] dest;
@@ -60,13 +29,13 @@ public class MapleMapFieldLimitChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -76,35 +45,33 @@ public class MapleMapFieldLimitChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void listFiles(String directoryName, ArrayList<File> files) {
File directory = new File(directoryName);
@@ -118,65 +85,62 @@ public class MapleMapFieldLimitChecker {
}
}
}
private static int getMapIdFromFilename(String name) {
try {
return Integer.valueOf(name.substring(0, name.indexOf('.')));
} catch(Exception e) {
return Integer.parseInt(name.substring(0, name.indexOf('.')));
} catch (Exception e) {
return -1;
}
}
private static void translateToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
if (status == 2) {
String d = getName(token);
if (!d.contentEquals("info")) {
forwardCursor(status);
}
}
}
else {
} else {
if (status == 2) {
String d = getName(token);
if (d.contentEquals("fieldLimit")) {
int value = Integer.valueOf(getValue(token));
if ((value & fieldLimit) == fieldLimit) {
int value = Integer.parseInt(getValue(token));
if ((value & FIELD_LIMIT) == FIELD_LIMIT) {
System.out.println(mapid + " " + value);
}
}
}
}
}
private static void inspectMapEntry() {
String line = null;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void loadMapWz() throws IOException {
System.out.println("Reading Map.wz ...");
ArrayList<File> files = new ArrayList<>();
listFiles(wzPath + "/Map.wz/Map", files);
listFiles(WZFiles.MAP.getFilePath() + "/Map", files);
for(File f : files) {
fileReader = new InputStreamReader(new FileInputStream(f), "UTF-8");
for (File f : files) {
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
mapid = getMapIdFromFilename(f.getName());
inspectMapEntry();
@@ -184,7 +148,7 @@ public class MapleMapFieldLimitChecker {
fileReader.close();
}
}
public static void main(String[] args) {
try {
loadMapWz();
@@ -193,5 +157,4 @@ public class MapleMapFieldLimitChecker {
ioe.printStackTrace();
}
}
}

View File

@@ -0,0 +1,157 @@
package tools.mapletools;
import org.apache.commons.io.FileUtils;
import provider.wz.WZFiles;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author RonanLana
* <p>
* The main objective of this tool is to locate all mapids that doesn't have
* the "info" node in their WZ node tree.
*/
public class MapInfoRetriever {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("map_info_report.txt");
private static final List<Integer> missingInfo = new ArrayList<>();
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static boolean hasInfo;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[50];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static boolean translateToken(String token) {
String d;
int temp;
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
if (status == 1) {
d = getName(token);
if (d.contains("info")) {
hasInfo = true;
return true;
}
temp = status;
forwardCursor(temp);
}
status += 1;
}
return false;
}
private static void searchMapDirectory(int mapArea) {
final File mapDirectory = new File(WZFiles.MAP.getFilePath() + "/Map/Map" + mapArea);
try {
Iterator<File> iter = FileUtils.iterateFiles(mapDirectory, new String[]{"xml"}, true);
System.out.println("Parsing map area " + mapArea);
while (iter.hasNext()) {
File file = iter.next();
searchMapFile(file);
}
} catch (UncheckedIOException e) {
System.err.println("Directory " + mapDirectory.getPath() + " does not exist");
}
}
private static void searchMapFile(File file) {
// This will reference one line at a time
String line = null;
try {
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
hasInfo = false;
status = 0;
while ((line = bufferedReader.readLine()) != null) {
if (translateToken(line)) {
break;
}
}
if (!hasInfo) {
missingInfo.add(Integer.valueOf(file.getName().split(".img.xml")[0]));
}
bufferedReader.close();
fileReader.close();
} catch (IOException ex) {
System.out.println("Error reading file '" + file.getName() + "'");
} catch (Exception e) {
e.printStackTrace();
}
}
private static void writeReport() {
try {
PrintWriter printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
if (!missingInfo.isEmpty()) {
for (Integer i : missingInfo) {
printWriter.println(i);
}
} else {
printWriter.println("All map files contain 'info' node.");
}
printWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int i = 0; i <= 9; i++) {
searchMapDirectory(i);
}
writeReport();
}
}

View File

@@ -1,210 +1,184 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplemesofetcher;
import life.MapleLifeFactory;
import life.MapleMonsterStats;
import server.life.MapleMonsterStats;
import tools.Pair;
import java.io.File;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import tools.DatabaseConnection;
import tools.Pair;
/**
*
* @author RonanLana
*
* <p>
* This application traces missing meso drop data on the underlying DB (that must be
* defined on the DatabaseConnection file of this project) and generates a
* SQL file that proposes missing drop entries for the drop_data table.
*
* <p>
* The meso range is calculated accordingly with the target mob stats, such as level
* and if it's a boss or not, similarly as how it has been done for the actual meso
* drops.
*
*/
public class MapleMesoFetcher {
public class MesoFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("meso_drop_data.sql");
private static final boolean PERMIT_MESOS_ON_DOJO_BOSSES = false;
private static final int MESO_ID = 0;
private static final int MIN_ITEMS = 4;
private static final int CHANCE = 400000;
private static final Map<Integer, Pair<Integer, Integer>> mobRange = new HashMap<>();
private static PrintWriter printWriter;
private static String newFile = "lib/meso_drop_data.sql";
private static boolean permitMesosOnDojoBosses = false;
private static int minItems = 4;
private static int mesoid = 0;
private static int chance = 400000;
private static Map<Integer, MapleMonsterStats> mobStats;
private static Map<Integer, Pair<Integer, Integer>> mobRange = new HashMap<>();
private static Pair<Integer, Integer> calcMesoRange90(int level, boolean boss) {
int minRange, maxRange;
// MIN range
minRange = (int)(72.70814714 * Math.exp(0.02284640619 * level));
minRange = (int) (72.70814714 * Math.exp(0.02284640619 * level));
// MAX range
maxRange = (int)(133.8194881 * Math.exp(0.02059225059 * level));
maxRange = (int) (133.8194881 * Math.exp(0.02059225059 * level));
// boss perks
if(boss) {
if (boss) {
minRange *= 3;
maxRange *= 10;
}
return new Pair<>(minRange, maxRange);
}
private static Pair<Integer, Integer> calcMesoRange(int level, boolean boss) {
int minRange, maxRange;
// MIN range
minRange = (int)(30.32032228 * Math.exp(0.03281144930 * level));
minRange = (int) (30.32032228 * Math.exp(0.03281144930 * level));
// MAX range
maxRange = (int)(44.45878459 * Math.exp(0.03289611686 * level));
maxRange = (int) (44.45878459 * Math.exp(0.03289611686 * level));
// boss perks
if(boss) {
if (boss) {
minRange *= 3;
maxRange *= 10;
}
return new Pair<>(minRange, maxRange);
}
private static void calcAllMobsMesoRange() {
System.out.print("Calculating range... ");
for(Entry<Integer, MapleMonsterStats> mobStat : mobStats.entrySet()) {
for (Map.Entry<Integer, MapleMonsterStats> mobStat : mobStats.entrySet()) {
MapleMonsterStats mms = mobStat.getValue();
Pair<Integer, Integer> mesoRange;
if(mms.getLevel() < 90) {
if (mms.getLevel() < 90) {
mesoRange = calcMesoRange(mms.getLevel(), mms.isBoss());
} else {
mesoRange = calcMesoRange90(mms.getLevel(), mms.isBoss());
}
mobRange.put(mobStat.getKey(), mesoRange);
}
System.out.println("done!");
}
private static void printSqlHeader() {
printWriter.println(" # SQL File autogenerated from the MapleMesoFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account mob stats such as level and boss for the meso ranges.");
printWriter.println(" # Only mobs with " + minItems + " or more items with no meso entry on the DB it was compiled are presented here.");
printWriter.println(" # Only mobs with " + MIN_ITEMS + " or more items with no meso entry on the DB it was compiled are presented here.");
printWriter.println();
printWriter.println(" INSERT IGNORE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES");
}
private static void printSqlExceptions() {
if(!permitMesosOnDojoBosses) {
printWriter.println("\r\n DELETE FROM drop_data WHERE dropperid >= 9300184 AND dropperid <= 9300215 AND itemid = " + mesoid + ";");
if (!PERMIT_MESOS_ON_DOJO_BOSSES) {
printWriter.println("\r\n DELETE FROM drop_data WHERE dropperid >= 9300184 AND dropperid <= 9300215 AND itemid = " + MESO_ID + ";");
}
}
private static void printSqlMobMesoRange(int mobid) {
Pair<Integer, Integer> mobmeso = mobRange.get(mobid);
printWriter.println("(" + mobid + ", " + mesoid + ", " + mobmeso.left + ", " + mobmeso.right + ", 0, " + chance + "),");
printWriter.println("(" + mobid + ", " + MESO_ID + ", " + mobmeso.left + ", " + mobmeso.right + ", 0, " + CHANCE + "),");
}
private static void printSqlMobMesoRangeFinal(int mobid) {
Pair<Integer, Integer> mobmeso = mobRange.get(mobid);
printWriter.println("(" + mobid + ", " + mesoid + ", " + mobmeso.left + ", " + mobmeso.right + ", 0, " + chance + ");");
printWriter.println("(" + mobid + ", " + MESO_ID + ", " + mobmeso.left + ", " + mobmeso.right + ", 0, " + CHANCE + ");");
}
private static void generateMissingMobsMesoRange() {
System.out.print("Generating missing ranges... ");
Connection con = DatabaseConnection.getConnection();
Connection con = SimpleDatabaseConnection.getConnection();
List<Integer> existingMobs = new ArrayList<>(200);
try {
// select all mobs which doesn't drop mesos and have a fair amount of items dropping (meaning they are not an event mob)
PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE dropperid NOT IN (SELECT DISTINCT dropperid FROM drop_data WHERE itemid = 0) GROUP BY dropperid HAVING count(*) >= " + minItems + ";");
PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE dropperid NOT IN (SELECT DISTINCT dropperid FROM drop_data WHERE itemid = 0) GROUP BY dropperid HAVING count(*) >= " + MIN_ITEMS + ";");
ResultSet rs = ps.executeQuery();
if (rs.isBeforeFirst()) {
while(rs.next()) {
while (rs.next()) {
int mobid = rs.getInt(1);
if(mobRange.containsKey(mobid)) {
if (mobRange.containsKey(mobid)) {
existingMobs.add(mobid);
}
}
if(!existingMobs.isEmpty()) {
printWriter = new PrintWriter(newFile, "UTF-8");
if (!existingMobs.isEmpty()) {
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printSqlHeader();
for(int i = 0; i < existingMobs.size() - 1; i++)
for (int i = 0; i < existingMobs.size() - 1; i++) {
printSqlMobMesoRange(existingMobs.get(i));
}
printSqlMobMesoRangeFinal(existingMobs.get(existingMobs.size() - 1));
printSqlExceptions();
printWriter.close();
} else {
throw new Exception("ALREADY UPDATED");
}
} else {
throw new Exception("ALREADY UPDATED");
}
rs.close();
ps.close();
con.close();
System.out.println("done!");
} catch(Exception e) {
if(e.getMessage() != null && e.getMessage().equals("ALREADY UPDATED")) {
} catch (Exception e) {
if (e.getMessage() != null && e.getMessage().equals("ALREADY UPDATED")) {
System.out.println("done! The DB is already up-to-date, no file generated.");
} else {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// load mob stats from WZ
mobStats = MapleLifeFactory.getAllMonsterStats();
mobStats = MonsterStatFetcher.getAllMonsterStats();
calcAllMobsMesoRange();
generateMissingMobsMesoRange();
}
}

View File

@@ -0,0 +1,169 @@
package tools.mapletools;
import provider.wz.WZFiles;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author RonanLana
* <p>
* This application simply gets from the MonsterBook.img.xml all mobid's and
* puts them on a SQL table with the correspondent mob cardid.
*/
public class MobBookIndexer {
private static final File INPUT_FILE = new File(WZFiles.STRING.getFile(), "MonsterBook.img.xml");
private static final Connection con = SimpleDatabaseConnection.getConnection();
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int mobId = -1;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
if (j - i < 7) {
dest = new char[6];
} else {
dest = new char[7];
}
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d);
}
private static void forwardCursor(int st) {
String line = null;
try {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static boolean isCard(int itemId) {
return itemId / 10000 == 238;
}
private static void loadPairFromMob() {
System.out.println("Loading mob id " + mobId);
try {
PreparedStatement ps, ps2;
ResultSet rs;
ps = con.prepareStatement("SELECT itemid FROM drop_data WHERE (dropperid = ? AND itemid > 0) GROUP BY itemid;");
ps.setInt(1, mobId);
rs = ps.executeQuery();
while (rs.next()) {
int itemId = rs.getInt("itemid");
if (isCard(itemId)) {
ps2 = con.prepareStatement("INSERT INTO `monstercardwz` (`cardid`, `mobid`) VALUES (?, ?)");
ps2.setInt(1, itemId);
ps2.setInt(2, mobId);
ps2.executeUpdate();
}
}
rs.close();
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
private static void translateToken(String token) {
String d;
int temp;
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
if (status == 1) { //getting MobId
d = getName(token);
mobId = Integer.parseInt(d);
} else if (status == 2) {
d = getName(token);
if (d.contains("reward")) {
temp = status;
loadPairFromMob();
forwardCursor(temp);
}
}
status += 1;
}
}
private static void indexFromDropData() {
// This will reference one line at a time
String line = null;
try {
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
PreparedStatement ps = con.prepareStatement("DROP TABLE IF EXISTS monstercardwz;");
ps.execute();
ps = con.prepareStatement("CREATE TABLE `monstercardwz` ("
+ "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
+ "`cardid` int(10) NOT NULL DEFAULT '-1',"
+ "`mobid` int(10) NOT NULL DEFAULT '-1',"
+ "PRIMARY KEY (`id`)"
+ ");");
ps.execute();
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
bufferedReader.close();
fileReader.close();
con.close();
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + INPUT_FILE + "'");
} catch (IOException ex) {
System.out.println("Error reading file '" + INPUT_FILE + "'");
} catch (SQLException e) {
System.out.println("Warning: Could not establish connection to database to change card chance rate.");
System.out.println(e.getMessage());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
indexFromDropData();
}
}

View File

@@ -1,65 +1,39 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplemobbookupdate;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import provider.wz.WZFiles;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author RonanLana
*
* <p>
* This application updates the Monster Book drop data with the actual underlying drop data from
* the Maplestory database specified in the URL below.
*
* <p>
* In other words all items drops from monsters listed inside the Mob Book feature will be patched to match exactly like the item
* drop list specified in the URL's Maplestory database.
*
* <p>
* The original file "MonsterBook.img.xml" from String.wz must be copied to the directory of this application and only then
* executed. This program will generate another file that must replace the original server file to make the effects take place
* to on your server.
*
* <p>
* After replacing on server, this XML must be updated on the client via WZ Editor (HaRepack for instance). Once inside the repack,
* remove the property 'MonsterBook.img' inside 'string.wz' and choose to import the xml generated with this software.
*
*/
public class MapleMobBookUpdate {
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
public class MobBookUpdate {
private static final File INPUT_FILE = new File(WZFiles.STRING.getFile(), "MonsterBook.img.xml");
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("MonsterBook_updated.img.xml");
private static final Connection con = SimpleDatabaseConnection.getConnection();
static String fileName = "lib/MonsterBook.img.xml";
static String newFile = "lib/MonsterBook_updated.img.xml";
static Connection con = null;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static byte status = 0;
static int mobId = -1;
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int mobId = -1;
private static String getName(String token) {
int i, j;
@@ -70,33 +44,36 @@ public class MapleMobBookUpdate {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
if(j - i < 7) dest = new char[6];
else dest = new char[7];
if (j - i < 7) {
dest = new char[6];
} else {
dest = new char[7];
}
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d);
return (d);
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
if(line != null) printWriter.println(line);
}
catch(Exception e) {
if (line != null) {
printWriter.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
@@ -112,9 +89,11 @@ public class MapleMobBookUpdate {
ps.setInt(1, mobId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
while (rs.next()) {
toPrint = "";
for(int k = 0; k <= status; k++) toPrint += " ";
for (int k = 0; k <= status; k++) {
toPrint += " ";
}
toPrint += "<int name=\"";
toPrint += cont;
@@ -127,11 +106,10 @@ public class MapleMobBookUpdate {
printWriter.println(toPrint);
cont++;
}
rs.close();
ps.close();
}
catch(SQLException e) {
} catch (SQLException e) {
e.printStackTrace();
}
}
@@ -142,18 +120,16 @@ public class MapleMobBookUpdate {
printWriter.println(token);
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting MobId
} else if (token.contains("imgdir")) {
if (status == 1) { //getting MobId
d = getName(token);
mobId = Integer.parseInt(d);
}
else if(status == 2) {
} else if (status == 2) {
d = getName(token);
if(d.contains("reward")) {
if (d.contains("reward")) {
temp = status;
loadDropsFromMob();
@@ -166,19 +142,16 @@ public class MapleMobBookUpdate {
}
private static void UpdateFromDropData() {
private static void updateFromDropData() {
// This will reference one line at a time
String line = null;
try {
Class.forName(driver).newInstance();
con = DriverManager.getConnection(host, username, password);
printWriter = new PrintWriter(newFile, "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
@@ -187,37 +160,20 @@ public class MapleMobBookUpdate {
fileReader.close();
con.close();
}
catch(FileNotFoundException ex) {
System.out.println("Unable to open file '" + fileName + "'");
}
catch(IOException ex) {
System.out.println("Error reading file '" + fileName + "'");
}
catch(SQLException e) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + INPUT_FILE + "'");
} catch (IOException ex) {
System.out.println("Error reading file '" + INPUT_FILE + "'");
} catch (SQLException e) {
System.out.println("Warning: Could not establish connection to database to change card chance rate.");
System.out.println(e.getMessage());
}
catch(ClassNotFoundException e) {
System.out.println("Error: could not find class");
System.out.println(e.getMessage());
}
catch(InstantiationException e) {
System.out.println("Error: instantiation failure");
System.out.println(e.getMessage());
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
UpdateFromDropData();
updateFromDropData();
}
}

View File

@@ -1,69 +1,40 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package life;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import provider.MapleData;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataFileEntry;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.*;
import provider.wz.MapleDataType;
import provider.wz.WZFiles;
import server.life.Element;
import server.life.ElementalEffectiveness;
import server.life.MapleLifeFactory.BanishInfo;
import server.life.MapleLifeFactory.loseItem;
import server.life.MapleLifeFactory.selfDestruction;
import server.life.MapleMonsterStats;
import tools.Pair;
public class MapleLifeFactory {
private static String wzPath = "../../wz";
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(wzPath + "/Mob.wz"));
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(new File(wzPath + "/String.wz"));
private static MapleData mobStringData = stringDataWZ.getData("Mob.img");
private static MapleData npcStringData = stringDataWZ.getData("Npc.img");
private static Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
import java.util.*;
private static int getMonsterId(String fileName) {
return Integer.parseInt(fileName.substring(0, 7));
}
public static Map<Integer, MapleMonsterStats> getAllMonsterStats() {
public class MonsterStatFetcher {
private static final MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.MOB);
private static final MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
private static final MapleData mobStringData = stringDataWZ.getData("Mob.img");
private static final Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
static Map<Integer, MapleMonsterStats> getAllMonsterStats() {
MapleDataDirectoryEntry root = data.getRoot();
System.out.print("Parsing mob stats... ");
for (MapleDataFileEntry mFile : root.getFiles()) {
try {
String fileName = mFile.getName();
//System.out.println("Parsing '" + fileName + "'");
MapleData monsterData = data.getData(fileName);
if (monsterData == null) {
continue;
}
Integer mid = getMonsterId(fileName);
MapleData monsterInfoData = monsterData.getChildByPath("info");
MapleMonsterStats stats = new MapleMonsterStats();
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
@@ -71,7 +42,7 @@ public class MapleLifeFactory {
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0));
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0));
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
@@ -139,7 +110,7 @@ public class MapleLifeFactory {
int i = 0;
List<Pair<Integer, Integer>> skills = new ArrayList<>();
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0))));
skills.add(new Pair<>(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0), MapleDataTool.getInt(i + "/level", monsterSkillData, 0)));
i++;
}
stats.setSkills(skills);
@@ -148,93 +119,25 @@ public class MapleLifeFactory {
if (banishData != null) {
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
}
monsterStats.put(mid, stats);
} catch(NullPointerException npe) {
//System.out.println("[SEVERE] " + mFile.getName() + " failed to load. Issue: " + npe.getMessage() + "\n\n");
}
}
System.out.println("done!");
System.out.println("Done parsing mob stats!");
return monsterStats;
}
private static int getMonsterId(String fileName) {
return Integer.parseInt(fileName.substring(0, 7));
}
private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) {
for (int i = 0; i < elemAttr.length(); i += 2) {
stats.setEffectiveness(Element.getFromChar(elemAttr.charAt(i)), ElementalEffectiveness.getByNumber(Integer.valueOf(String.valueOf(elemAttr.charAt(i + 1)))));
}
}
public static class BanishInfo {
private int map;
private String portal, msg;
public BanishInfo(String msg, int map, String portal) {
this.msg = msg;
this.map = map;
this.portal = portal;
}
public int getMap() {
return map;
}
public String getPortal() {
return portal;
}
public String getMsg() {
return msg;
}
}
public static class loseItem {
private int id;
private byte chance, x;
private loseItem(int id, byte chance, byte x) {
this.id = id;
this.chance = chance;
this.x = x;
}
public int getId() {
return id;
}
public byte getChance() {
return chance;
}
public byte getX() {
return x;
}
}
public static class selfDestruction {
private byte action;
private int removeAfter;
private int hp;
private selfDestruction(byte action, int removeAfter, int hp) {
this.action = action;
this.removeAfter = removeAfter;
this.hp = hp;
}
public int getHp() {
return hp;
}
public byte getAction() {
return action;
}
public int removeAfter() {
return removeAfter;
}
}
}

View File

@@ -1,70 +1,35 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplenoitemidfetcher;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
*
* @author RonanLana
*
* <p>
* This application finds inexistent itemids within the drop data from
* the Maplestory database specified in the URL below. This program
* assumes all itemids uses 7 digits.
*
* <p>
* A file is generated listing all the inexistent ids.
*/
public class MapleNoItemIdFetcher {
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
public class NoItemIdFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("no_item_id_report.txt");
private static final Connection con = SimpleDatabaseConnection.getConnection();
static String wzPath = "../../wz";
static String newFile = "lib/result.txt";
private static final Set<Integer> existingIds = new HashSet<>();
private static final Set<Integer> nonExistingIds = new HashSet<>();
static Connection con = null;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static byte status = 0;
static int itemId = -1;
static Set<Integer> existingIds = new HashSet<>();
static Set<Integer> nonExistingIds = new HashSet<>();
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int itemId = -1;
private static String getName(String token) {
int i, j;
@@ -79,42 +44,39 @@ public class MapleNoItemIdFetcher {
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d);
return (d);
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void translateToken(String token) {
String d;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting ItemId
} else if (token.contains("imgdir")) {
if (status == 1) { //getting ItemId
d = getName(token);
itemId = Integer.parseInt(d.substring(1, 8));
existingIds.add(itemId);
forwardCursor(status);
}
@@ -127,36 +89,34 @@ public class MapleNoItemIdFetcher {
// This will reference one line at a time
String line = null;
try {
fileReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
try {
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
status = 0;
try {
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
} catch(NumberFormatException npe) {
} catch (NumberFormatException npe) {
// second criteria, itemid is on the name of the file
try {
itemId = Integer.parseInt(file.getName().substring(0, 7));
existingIds.add(itemId);
} catch(NumberFormatException npe2) {}
} catch (NumberFormatException npe2) {
}
}
bufferedReader.close();
fileReader.close();
}
catch(FileNotFoundException ex) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + file.getName() + "'");
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println("Error reading file '" + file.getName() + "'");
}
}
private static void readEquipDataDirectory(String dirPath) {
File[] folders = new File(dirPath).listFiles();
//If this pathname does not denote a directory, then listFiles() returns null.
@@ -164,21 +124,22 @@ public class MapleNoItemIdFetcher {
for (File folder : folders) { // enter all subfolders
if (folder.isDirectory()) {
System.out.println("Reading '" + dirPath + "/" + folder.getName() + "'...");
try {
File[] files = folder.listFiles();
for (File file : files) { // enter all XML files under subfolders
if (file.isFile()) {
itemId = Integer.parseInt(file.getName().substring(0, 8));
existingIds.add(itemId);
}
}
} catch (NumberFormatException nfe) {}
} catch (NumberFormatException nfe) {
}
}
}
}
private static void readItemDataDirectory(String dirPath) {
File[] folders = new File(dirPath).listFiles();
//If this pathname does not denote a directory, then listFiles() returns null.
@@ -186,9 +147,9 @@ public class MapleNoItemIdFetcher {
for (File folder : folders) { // enter all subfolders
if (folder.isDirectory()) {
System.out.println("Reading '" + dirPath + "/" + folder.getName() + "'...");
File[] files = folder.listFiles();
for (File file : files) { // enter all XML files under subfolders
if (file.isFile()) {
readItemDataFile(file);
@@ -197,13 +158,13 @@ public class MapleNoItemIdFetcher {
}
}
}
private static void evaluateDropsFromTable(String table) throws SQLException {
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT itemid FROM " + table + ";");
ResultSet rs = ps.executeQuery();
while(rs.next()) {
if(!existingIds.contains(rs.getInt(1))) {
while (rs.next()) {
if (!existingIds.contains(rs.getInt(1))) {
nonExistingIds.add(rs.getInt(1));
}
}
@@ -211,64 +172,45 @@ public class MapleNoItemIdFetcher {
rs.close();
ps.close();
}
private static void evaluateDropsFromDb() {
try {
System.out.println("Evaluating item data on DB...");
Class.forName(driver).newInstance();
con = DriverManager.getConnection(host, username, password);
evaluateDropsFromTable("drop_data");
evaluateDropsFromTable("reactordrops");
if(!nonExistingIds.isEmpty()) {
if (!nonExistingIds.isEmpty()) {
List<Integer> list = new ArrayList<>(nonExistingIds);
Collections.sort(list);
for(Integer i : list) {
for (Integer i : list) {
printWriter.println(i);
}
}
System.out.println("Inexistent itemid count: " + nonExistingIds.size());
System.out.println("Total itemid count: " + existingIds.size());
con.close();
}
catch(ClassNotFoundException e) {
System.out.println("Error: could not find class");
System.out.println(e.getMessage());
}
catch(InstantiationException e) {
System.out.println("Error: instantiation failure");
System.out.println(e.getMessage());
}
catch(SQLException e) {
e.printStackTrace();
}
catch(Exception e) {
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
existingIds.add(0); // meso itemid
readEquipDataDirectory(wzPath + "/Character.wz");
readItemDataDirectory(wzPath + "/Item.wz");
readEquipDataDirectory(WZFiles.CHARACTER.getFilePath());
readItemDataDirectory(WZFiles.ITEM.getFilePath());
evaluateDropsFromDb();
printWriter.close();
} catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

View File

@@ -1,89 +1,51 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.*;
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplenoitemnamefetcher;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import provider.MapleData;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataFileEntry;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
*
* @author RonanLana
*
* <p>
* This application finds itemids with inexistent name and description from
* within the server-side XMLs, then identify them on a report file along
* with a XML excerpt to be appended on the String.wz xml nodes. This program
* assumes all equipids are depicted using 8 digits and item using 7 digits.
*
* <p>
* Estimated parse time: 2 minutes
*/
public class MapleNoItemNameFetcher {
static String wzPath = "../../wz";
static String newFile = "lib/result.txt";
static String xmlFile = "lib/output.txt";
public class NoItemNameFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("no_item_name_result.txt");
private static final File OUTPUT_XML_FILE = ToolConstants.getOutputFile("no_item_name_xml.txt");
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static Map<Integer, String> itemsWzPath = new HashMap<>();
static Map<Integer, ItemType> itemTypes = new HashMap<>();
static Map<Integer, EquipType> equipTypes = new HashMap<>();
static Map<Integer, ItemType> itemsWithNoNameProperty = new HashMap<>();
static Set<Integer> equipsWithNoCashProperty = new HashSet<>();
private static final Map<Integer, String> itemsWzPath = new HashMap<>();
private static final Map<Integer, EquipType> equipTypes = new HashMap<>();
private static final Map<Integer, ItemType> itemsWithNoNameProperty = new HashMap<>();
private static final Set<Integer> equipsWithNoCashProperty = new HashSet<>();
private static final Map<Integer, String> nameContentCache = new HashMap<>();
private static final Map<Integer, String> descContentCache = new HashMap<>();
private static PrintWriter printWriter = null;
private static ItemType curType = ItemType.UNDEF;
static Map<Integer, String> nameContentCache = new HashMap<>();
static Map<Integer, String> descContentCache = new HashMap<>();
static ItemType curType = ItemType.UNDEF;
private enum ItemType {
UNDEF, CASH, CONSUME, EQP, ETC, INS, PET
}
private enum EquipType {
UNDEF, ACCESSORY, CAP, CAPE, COAT, FACE, GLOVE, HAIR, LONGCOAT, PANTS, PETEQUIP, RING, SHIELD, SHOES, TAMING, WEAPON
}
private static void processStringSubdirectoryData(MapleData subdirData, String subdirPath) {
for(MapleData md : subdirData.getChildren()) {
for (MapleData md : subdirData.getChildren()) {
try {
MapleData nameData = md.getChildByPath("name");
MapleData descData = md.getChildByPath("desc");
int itemId = Integer.parseInt(md.getName());
if (nameData != null && descData != null) {
itemsWithNoNameProperty.remove(itemId);
@@ -93,7 +55,7 @@ public class MapleNoItemNameFetcher {
} else if (descData != null) {
descContentCache.put(itemId, MapleDataTool.getString("desc", md));
}
System.out.println("Found itemid on String.wz with no full property: " + subdirPath + subdirData.getName() + "/" + md.getName());
}
} catch (NumberFormatException nfe) {
@@ -101,9 +63,9 @@ public class MapleNoItemNameFetcher {
}
}
}
private static void readStringSubdirectoryData(MapleData subdirData, int depth, String subdirPath) {
if(depth > 0) {
if (depth > 0) {
for (MapleData mDir : subdirData.getChildren()) {
readStringSubdirectoryData(mDir, depth - 1, subdirPath + mDir.getName() + "/");
}
@@ -111,230 +73,162 @@ public class MapleNoItemNameFetcher {
processStringSubdirectoryData(subdirData, subdirPath);
}
}
private static void readStringSubdirectoryData(MapleData subdirData, int depth) {
readStringSubdirectoryData(subdirData, depth, "");
}
private static void readStringWZData() {
System.out.println("Parsing String.wz...");
MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
MapleDataProvider stringData = MapleDataProviderFactory.getDataProvider(WZFiles.STRING);
MapleData cashStringData = stringData.getData("Cash.img");
readStringSubdirectoryData(cashStringData, 0);
MapleData consumeStringData = stringData.getData("Consume.img");
readStringSubdirectoryData(consumeStringData, 0);
MapleData eqpStringData = stringData.getData("Eqp.img");
readStringSubdirectoryData(eqpStringData, 2);
MapleData etcStringData = stringData.getData("Etc.img");
readStringSubdirectoryData(etcStringData, 1);
MapleData insStringData = stringData.getData("Ins.img");
readStringSubdirectoryData(insStringData, 0);
MapleData petStringData = stringData.getData("Pet.img");
readStringSubdirectoryData(petStringData, 0);
}
private static boolean isTamingMob(int itemId) {
int itemType = itemId / 1000;
return itemType == 1902 || itemType == 1912;
}
private static boolean isAccessory(int itemId) {
return itemId >= 1110000 && itemId < 1140000;
}
private static ItemType getItemTypeFromDirectoryName(String dirName) {
switch(dirName) {
case "Cash":
return ItemType.CASH;
case "Consume":
return ItemType.CONSUME;
case "Etc":
return ItemType.ETC;
case "Install":
return ItemType.INS;
case "Pet":
return ItemType.PET;
default:
return ItemType.UNDEF;
}
return switch (dirName) {
case "Cash" -> ItemType.CASH;
case "Consume" -> ItemType.CONSUME;
case "Etc" -> ItemType.ETC;
case "Install" -> ItemType.INS;
case "Pet" -> ItemType.PET;
default -> ItemType.UNDEF;
};
}
private static EquipType getEquipTypeFromDirectoryName(String dirName) {
switch(dirName) {
case "Accessory":
return EquipType.ACCESSORY;
case "Cap":
return EquipType.CAP;
case "Cape":
return EquipType.CAPE;
case "Coat":
return EquipType.COAT;
case "Face":
return EquipType.FACE;
case "Glove":
return EquipType.GLOVE;
case "Hair":
return EquipType.HAIR;
case "Longcoat":
return EquipType.LONGCOAT;
case "Pants":
return EquipType.PANTS;
case "PetEquip":
return EquipType.PETEQUIP;
case "Ring":
return EquipType.RING;
case "Shield":
return EquipType.SHIELD;
case "Shoes":
return EquipType.SHOES;
case "TamingMob":
return EquipType.TAMING;
case "Weapon":
return EquipType.WEAPON;
default:
return EquipType.UNDEF;
}
return switch (dirName) {
case "Accessory" -> EquipType.ACCESSORY;
case "Cap" -> EquipType.CAP;
case "Cape" -> EquipType.CAPE;
case "Coat" -> EquipType.COAT;
case "Face" -> EquipType.FACE;
case "Glove" -> EquipType.GLOVE;
case "Hair" -> EquipType.HAIR;
case "Longcoat" -> EquipType.LONGCOAT;
case "Pants" -> EquipType.PANTS;
case "PetEquip" -> EquipType.PETEQUIP;
case "Ring" -> EquipType.RING;
case "Shield" -> EquipType.SHIELD;
case "Shoes" -> EquipType.SHOES;
case "TamingMob" -> EquipType.TAMING;
case "Weapon" -> EquipType.WEAPON;
default -> EquipType.UNDEF;
};
}
private static String getStringDirectoryNameFromEquipType(EquipType eType) {
switch(eType) {
case ACCESSORY:
return "Accessory";
case CAP:
return "Cap";
case CAPE:
return "Cape";
case COAT:
return "Coat";
case FACE:
return "Face";
case GLOVE:
return "Glove";
case HAIR:
return "Hair";
case LONGCOAT:
return "Longcoat";
case PANTS:
return "Pants";
case PETEQUIP:
return "PetEquip";
case RING:
return "Ring";
case SHIELD:
return "Shield";
case SHOES:
return "Shoes";
case TAMING:
return "Taming";
case WEAPON:
return "Weapon";
default:
return "Undefined";
}
return switch (eType) {
case ACCESSORY -> "Accessory";
case CAP -> "Cap";
case CAPE -> "Cape";
case COAT -> "Coat";
case FACE -> "Face";
case GLOVE -> "Glove";
case HAIR -> "Hair";
case LONGCOAT -> "Longcoat";
case PANTS -> "Pants";
case PETEQUIP -> "PetEquip";
case RING -> "Ring";
case SHIELD -> "Shield";
case SHOES -> "Shoes";
case TAMING -> "Taming";
case WEAPON -> "Weapon";
default -> "Undefined";
};
}
private static void readEquipNodeData(MapleDataProvider data, MapleDataDirectoryEntry mDir, String wzFileName, String dirName) {
EquipType eqType = getEquipTypeFromDirectoryName(dirName);
for(MapleDataFileEntry mFile : mDir.getFiles()) {
for (MapleDataFileEntry mFile : mDir.getFiles()) {
String fileName = mFile.getName();
try {
int itemId = Integer.parseInt(fileName.substring(0, 8));
int itemId = Integer.parseInt(fileName.substring(0, 8));
itemsWithNoNameProperty.put(itemId, curType);
equipTypes.put(itemId, eqType);
itemsWzPath.put(itemId, wzFileName + "/" + dirName + "/" + fileName);
if(!isAccessory(itemId) && !isTamingMob(itemId)) {
if (!isAccessory(itemId) && !isTamingMob(itemId)) {
try {
MapleData fileData = data.getData(dirName + "/" + fileName);
MapleData mdinfo = fileData.getChildByPath("info");
if( mdinfo.getChildByPath("cash") == null) {
if (mdinfo.getChildByPath("cash") == null) {
equipsWithNoCashProperty.add(itemId);
}
} catch(NullPointerException npe) {
} catch (NullPointerException npe) {
System.out.println("[SEVERE] " + mFile.getName() + " failed to load. Issue: " + npe.getMessage() + "\n\n");
}
}
} catch (Exception e) {}
} catch (Exception e) {
}
}
}
private static void readEquipWZData() {
String wzFileName = "Character.wz";
MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Character.wz"));
MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.CHARACTER);
MapleDataDirectoryEntry root = data.getRoot();
System.out.println("Parsing " + wzFileName + "...");
for (MapleDataDirectoryEntry mDir : root.getSubdirectories()) {
String dirName = mDir.getName();
if(dirName.contentEquals("Dragon")) continue;
if (dirName.contentEquals("Dragon")) {
continue;
}
readEquipNodeData(data, mDir, wzFileName, dirName);
}
}
private static void readItemWZData() {
String wzFileName = "Item.wz";
MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
MapleDataProvider data = MapleDataProviderFactory.getDataProvider(WZFiles.ITEM);
MapleDataDirectoryEntry root = data.getRoot();
System.out.println("Parsing " + wzFileName + "...");
for (MapleDataDirectoryEntry mDir : root.getSubdirectories()) {
String dirName = mDir.getName();
if(dirName.contentEquals("Special")) continue;
if (dirName.contentEquals("Special")) {
continue;
}
curType = getItemTypeFromDirectoryName(dirName);
if(!dirName.contentEquals("Pet")) {
for(MapleDataFileEntry mFile : mDir.getFiles()) {
if (!dirName.contentEquals("Pet")) {
for (MapleDataFileEntry mFile : mDir.getFiles()) {
String fileName = mFile.getName();
MapleData fileData = data.getData(dirName + "/" + fileName);
for(MapleData mData : fileData.getChildren()) {
for (MapleData mData : fileData.getChildren()) {
try {
int itemId = Integer.parseInt(mData.getName());
itemsWithNoNameProperty.put(itemId, curType);
@@ -349,38 +243,38 @@ public class MapleNoItemNameFetcher {
}
}
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleInvalidItemWithNoNameFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static void printReportFileResults() {
if(!itemsWithNoNameProperty.isEmpty()) {
if (!itemsWithNoNameProperty.isEmpty()) {
printWriter.println("Itemids with missing 'name' property: ");
List<Integer> itemids = new ArrayList<>(itemsWithNoNameProperty.keySet());
Collections.sort(itemids);
for(Integer itemid : itemids) {
for (Integer itemid : itemids) {
printWriter.println(" " + itemid + " " + itemsWzPath.get(itemid));
}
printWriter.println();
}
if(!equipsWithNoCashProperty.isEmpty()) {
if (!equipsWithNoCashProperty.isEmpty()) {
printWriter.println("Equipids with missing 'cash' property: ");
List<Integer> itemids = new ArrayList<>(equipsWithNoCashProperty);
Collections.sort(itemids);
for(Integer itemid : itemids) {
for (Integer itemid : itemids) {
printWriter.println(" " + itemid + " " + itemsWzPath.get(itemid));
}
}
}
private static Map<String, List<Integer>> filterMissingItemNames() {
List<Integer> cashList = new ArrayList<>(20);
List<Integer> consList = new ArrayList<>(20);
@@ -388,35 +282,18 @@ public class MapleNoItemNameFetcher {
List<Integer> etcList = new ArrayList<>(20);
List<Integer> insList = new ArrayList<>(20);
List<Integer> petList = new ArrayList<>(20);
for(Entry<Integer, ItemType> ids : itemsWithNoNameProperty.entrySet()) {
switch(ids.getValue()) {
case CASH:
cashList.add(ids.getKey());
break;
case CONSUME:
consList.add(ids.getKey());
break;
case EQP:
eqpList.add(ids.getKey());
break;
case ETC:
etcList.add(ids.getKey());
break;
case INS:
insList.add(ids.getKey());
break;
case PET:
petList.add(ids.getKey());
break;
for (Map.Entry<Integer, ItemType> ids : itemsWithNoNameProperty.entrySet()) {
switch (ids.getValue()) {
case CASH -> cashList.add(ids.getKey());
case CONSUME -> consList.add(ids.getKey());
case EQP -> eqpList.add(ids.getKey());
case ETC -> etcList.add(ids.getKey());
case INS -> insList.add(ids.getKey());
case PET -> petList.add(ids.getKey());
}
}
Map<String, List<Integer>> nameTags = new HashMap<>();
nameTags.put("Cash.img", cashList);
nameTags.put("Consume.img", consList);
@@ -424,94 +301,94 @@ public class MapleNoItemNameFetcher {
nameTags.put("Etc.img", etcList);
nameTags.put("Ins.img", insList);
nameTags.put("Pet.img", petList);
return nameTags;
}
private static void printOutputFileHeader() {
printWriter.println(" # XML File autogenerated from the MapleInvalidItemWithNoNameFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static String getMissingEquipName(int itemid) {
String s = nameContentCache.get(itemid);
if (s == null) {
s = "MISSING NAME " + itemid;
}
return s;
}
private static String getMissingEquipDesc(int itemid) {
String s = descContentCache.get(itemid);
if (s == null && itemid >= 2000000) { // thanks Halcyon for noticing "missing info" on equips
s = "MISSING INFO " + itemid;
}
return s;
}
private static void writeMissingEquipInfo(Integer itemid) {
printWriter.println(" <imgdir name=\"" + itemid + "\">");
String s;
s = getMissingEquipName(itemid);
printWriter.println(" <string name=\"name\" value=\"" + s + "\"/>");
s = getMissingEquipDesc(itemid);
printWriter.println(" <string name=\"desc\" value=\"" + s + "\"/>");
printWriter.println(" </imgdir>");
}
private static void writeEquipSubdirectoryHeader(EquipType eType) {
printWriter.println(" <imgdir name=\"" + getStringDirectoryNameFromEquipType(eType) + "\">");
}
private static void writeEquipSubdirectoryFooter() {
printWriter.println(" </imgdir>");
}
private static void writeEquipXMLHeader() {
printWriter.println(" <imgdir name=\"Eqp\">");
}
private static void writeEquipXMLFooter() {
printWriter.println(" </imgdir>");
}
private static void writeMissingItemInfo(Integer itemid) {
printWriter.println(" <imgdir name=\"" + itemid + "\">");
printWriter.println(" <string name=\"name\" value=\"MISSING NAME\"/>");
printWriter.println(" <string name=\"desc\" value=\"MISSING INFO\"/>");
printWriter.println(" </imgdir>");
}
private static void writeXMLHeader(String fileName) {
printWriter.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
printWriter.println("<imgdir name=\"" + fileName + "\">");
}
private static void writeXMLFooter() {
printWriter.println("</imgdir>");
}
private static void writeMissingEquipWZNode(EquipType eType, List<Integer> missingNames) {
if(!missingNames.isEmpty()) {
if (!missingNames.isEmpty()) {
Collections.sort(missingNames);
writeEquipSubdirectoryHeader(eType);
for(Integer equipid : missingNames) {
for (Integer equipid : missingNames) {
writeMissingEquipInfo(equipid);
}
writeEquipSubdirectoryFooter();
}
}
private static void writeMissingStringWZNode(String nodePath, List<Integer> missingNames, boolean isEquip) {
if(!missingNames.isEmpty()) {
if(!isEquip) {
if (!missingNames.isEmpty()) {
if (!isEquip) {
Collections.sort(missingNames);
printWriter.println(nodePath + ":");
@@ -519,7 +396,7 @@ public class MapleNoItemNameFetcher {
writeXMLHeader(nodePath);
for(Integer i : missingNames) {
for (Integer i : missingNames) {
writeMissingItemInfo(i);
}
@@ -528,23 +405,23 @@ public class MapleNoItemNameFetcher {
printWriter.println();
} else {
int arraySize = EquipType.values().length;
List<Integer> equips[] = new List[arraySize];
for(int i = 0; i < arraySize; i++) {
List<Integer>[] equips = new List[arraySize];
for (int i = 0; i < arraySize; i++) {
equips[i] = new ArrayList<>(42);
}
for(Integer itemid : missingNames) {
for (Integer itemid : missingNames) {
equips[equipTypes.get(itemid).ordinal()].add(itemid);
}
printWriter.println(nodePath + ":");
printWriter.println();
writeXMLHeader(nodePath);
writeEquipXMLHeader();
for(EquipType eType : EquipType.values()) {
for (EquipType eType : EquipType.values()) {
writeMissingEquipWZNode(eType, equips[eType.ordinal()]);
}
@@ -555,43 +432,41 @@ public class MapleNoItemNameFetcher {
}
}
}
private static void writeMissingStringWZNames(Map<String, List<Integer>> missingNames) throws Exception {
System.out.println("Writing remaining 'String.wz' names...");
printWriter = new PrintWriter(xmlFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_XML_FILE, StandardCharsets.UTF_8);
printOutputFileHeader();
String nodePaths[] = {"Cash.img", "Consume.img", "Eqp.img", "Etc.img", "Ins.img", "Pet.img"};
for(int i = 0; i < nodePaths.length; i++) {
String[] nodePaths = {"Cash.img", "Consume.img", "Eqp.img", "Etc.img", "Ins.img", "Pet.img"};
for (int i = 0; i < nodePaths.length; i++) {
writeMissingStringWZNode(nodePaths[i], missingNames.get(nodePaths[i]), i == 2);
}
printWriter.close();
}
public static void main(String[] args) {
try {
System.setProperty("wzpath", wzPath);
curType = ItemType.EQP;
readEquipWZData();
curType = ItemType.UNDEF;
readItemWZData();
readStringWZData(); // calculates the diff and effectively holds all items with no name property on the WZ
System.out.println("Reporting results...");
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
printReportFileResults();
printWriter.close();
Map<String, List<Integer>> missingNames = filterMissingItemNames();
writeMissingStringWZNames(missingNames);
System.out.println("Done!");
} catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}

View File

@@ -0,0 +1,268 @@
package tools.mapletools;
import provider.wz.WZFiles;
import tools.Pair;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @author RonanLana
* <p>
* This application parses the Quest.wz file inputted and generates a report showing
* all cases where a quest requires an item, but doesn't take them, which may happen
* because the node representing the item doesn't have a "count" clause.
* <p>
* Running it should generate a report file under "output" folder with the search results.
*/
public class QuestItemCountFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("quest_item_count_report.txt");
private static final String ACT_NAME = WZFiles.QUEST.getFilePath() + "/Act.img.xml";
private static final String CHECK_NAME = WZFiles.QUEST.getFilePath() + "/Check.img.xml";
private static final int INITIAL_STRING_LENGTH = 50;
private static final Map<Integer, Map<Integer, Integer>> checkItems = new HashMap<>();
private static final Map<Integer, Map<Integer, Integer>> actItems = new HashMap<>();
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int questId = -1;
private static int isCompleteState = 0;
private static int curItemId;
private static int curItemCount;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("value");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void readItemLabel(String token) {
String name = getName(token);
String value = getValue(token);
switch (name) {
case "id" -> curItemId = Integer.parseInt(value);
case "count" -> curItemCount = Integer.parseInt(value);
}
}
private static void commitQuestItemPair(Map<Integer, Map<Integer, Integer>> map) {
Map<Integer, Integer> list = map.get(questId);
if (list == null) {
list = new LinkedHashMap<>();
map.put(questId, list);
}
list.put(curItemId, curItemCount);
}
private static void translateTokenAct(String token) {
String d;
if (token.contains("/imgdir")) {
status -= 1;
if (status == 4) {
if (curItemCount == Integer.MAX_VALUE && isCompleteState == 1) {
commitQuestItemPair(actItems);
}
}
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
} else if (status == 3) {
if (!token.contains("item")) {
forwardCursor(status);
}
} else if (status == 4) {
curItemId = Integer.MAX_VALUE;
curItemCount = Integer.MAX_VALUE;
}
status += 1;
} else {
if (status == 5) {
readItemLabel(token);
}
}
}
private static void translateTokenCheck(String token) {
String d;
if (token.contains("/imgdir")) {
status -= 1;
if (status == 4) {
Map<Integer, Integer> missedItems = actItems.get(questId);
if (missedItems != null && missedItems.containsKey(curItemId) && isCompleteState == 1) {
commitQuestItemPair(checkItems);
}
}
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
} else if (status == 3) {
if (!token.contains("item")) {
forwardCursor(status);
}
} else if (status == 4) {
curItemId = Integer.MAX_VALUE;
curItemCount = Integer.MAX_VALUE;
}
status += 1;
} else {
if (status == 5) {
readItemLabel(token);
}
}
}
private static void readQuestItemCountData() throws IOException {
String line;
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(ACT_NAME), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
translateTokenAct(line);
}
bufferedReader.close();
fileReader.close();
fileReader = new InputStreamReader(new FileInputStream(CHECK_NAME), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
translateTokenCheck(line);
}
bufferedReader.close();
fileReader.close();
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleQuestItemCountFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static void printReportFileResults() {
List<Pair<Integer, Pair<Integer, Integer>>> reports = new ArrayList<>();
List<Pair<Integer, Integer>> notChecked = new ArrayList<>();
for (Map.Entry<Integer, Map<Integer, Integer>> actItem : actItems.entrySet()) {
int questid = actItem.getKey();
for (Map.Entry<Integer, Integer> actData : actItem.getValue().entrySet()) {
int itemid = actData.getKey();
Map<Integer, Integer> checkData = checkItems.get(questid);
if (checkData != null) {
Integer count = checkData.get(itemid);
if (count != null) {
reports.add(new Pair<>(questid, new Pair<>(itemid, -count)));
}
} else {
notChecked.add(new Pair<>(questid, itemid));
}
}
}
for (Pair<Integer, Pair<Integer, Integer>> r : reports) {
printWriter.println("Questid " + r.left + " : Itemid " + r.right.left + " should have qty " + r.right.right);
}
for (Pair<Integer, Integer> r : notChecked) {
printWriter.println("Questid " + r.left + " : Itemid " + r.right + " is unchecked");
}
}
private static void reportQuestItemCountData() {
// This will reference one line at a time
try {
System.out.println("Reading WZs...");
readQuestItemCountData();
System.out.println("Reporting results...");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
printReportFileResults();
printWriter.close();
System.out.println("Done!");
} catch (FileNotFoundException ex) {
System.out.println("Unable to open quest file.");
} catch (IOException ex) {
System.out.println("Error reading quest file.");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reportQuestItemCountData();
}
}

View File

@@ -1,102 +1,72 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplequestitemfetcher;
package tools.mapletools;
import org.apache.commons.io.FileUtils;
import tools.MapleItemInformationProvider;
import provider.wz.WZFiles;
import server.MapleItemInformationProvider;
import tools.DatabaseConnection;
import tools.Pair;
import java.io.*;
import java.sql.*;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.Map.Entry;
/**
*
* @author RonanLana
*
* <p>
* This application haves 2 objectives: fetch missing drop data relevant to quests,
* and update the questid from items that are labeled as "Quest Item" on the DB.
*
* To test a server instance with this feature, MapleQuestItemFetcher must be set
* just like it is displayed on the HeavenMS source: 2 folders ahead
* of the root of the main source.
*
* Running it should generate a report file under "lib" folder with the search results.
*
* <p>
* Running it should generate a report file under "output" folder with the search results.
* <p>
* Estimated parse time: 1 minute
*/
public class MapleQuestItemFetcher {
static MapleItemInformationProvider ii;
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
public class QuestItemFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("quest_report.txt");
private static final int INITIAL_STRING_LENGTH = 50;
private static final int INITIAL_LENGTH = 200;
private static final boolean DISPLAY_EXTRA_INFO = true; // display items with zero quantity over the quest act WZ
static String wzPath = "../../wz";
static String directoryName = "../..";
static String newFile = "lib/QuestReport.txt";
private static final Connection con = SimpleDatabaseConnection.getConnection();
private static final Map<Integer, Set<Integer>> startQuestItems = new HashMap<>(INITIAL_LENGTH);
private static final Map<Integer, Set<Integer>> completeQuestItems = new HashMap<>(INITIAL_LENGTH);
private static final Map<Integer, Set<Integer>> zeroedStartQuestItems = new HashMap<>();
private static final Map<Integer, Set<Integer>> zeroedCompleteQuestItems = new HashMap<>();
private static final Map<Integer, int[]> mixedQuestidItems = new HashMap<>();
private static final Set<Integer> limitedQuestids = new HashSet<>();
static Connection con = null;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static int initialLength = 200;
static int initialStringLength = 50;
static boolean displayExtraInfo = true; // display items with zero quantity over the quest act WZ
static Map<Integer, Set<Integer>> startQuestItems = new HashMap<>(initialLength);
static Map<Integer, Set<Integer>> completeQuestItems = new HashMap<>(initialLength);
static Map<Integer, Set<Integer>> zeroedStartQuestItems = new HashMap<>();
static Map<Integer, Set<Integer>> zeroedCompleteQuestItems = new HashMap<>();
static Map<Integer, int[]> mixedQuestidItems = new HashMap<>();
static Set<Integer> limitedQuestids = new HashSet<>();
static byte status = 0;
static int questId = -1;
static int isCompleteState = 0;
static int currentItemid = 0;
static int currentCount = 0;
private static MapleItemInformationProvider ii;
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int questId = -1;
private static int isCompleteState = 0;
private static int currentItemid = 0;
private static int currentCount = 0;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
if(j < i) return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
dest = new char[initialStringLength];
if (j < i) {
return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
}
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -106,56 +76,53 @@ public class MapleQuestItemFetcher {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void inspectQuestItemList(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
readItemToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void processCurrentItem() {
try {
if(ii.isQuestItem(currentItemid)) {
if(currentCount != 0) {
if(isCompleteState == 1) {
if(currentCount < 0) {
if (ii.isQuestItem(currentItemid)) {
if (currentCount != 0) {
if (isCompleteState == 1) {
if (currentCount < 0) {
Set<Integer> qi = completeQuestItems.get(questId);
if(qi == null) {
if (qi == null) {
Set<Integer> newSet = new HashSet<>();
newSet.add(currentItemid);
@@ -165,9 +132,9 @@ public class MapleQuestItemFetcher {
}
}
} else {
if(currentCount > 0) {
if (currentCount > 0) {
Set<Integer> qi = startQuestItems.get(questId);
if(qi == null) {
if (qi == null) {
Set<Integer> newSet = new HashSet<>();
newSet.add(currentItemid);
@@ -178,9 +145,9 @@ public class MapleQuestItemFetcher {
}
}
} else {
if(isCompleteState == 1) {
if (isCompleteState == 1) {
Set<Integer> qi = zeroedCompleteQuestItems.get(questId);
if(qi == null) {
if (qi == null) {
Set<Integer> newSet = new HashSet<>();
newSet.add(currentItemid);
@@ -190,7 +157,7 @@ public class MapleQuestItemFetcher {
}
} else {
Set<Integer> qi = zeroedStartQuestItems.get(questId);
if(qi == null) {
if (qi == null) {
Set<Integer> newSet = new HashSet<>();
newSet.add(currentItemid);
@@ -201,27 +168,26 @@ public class MapleQuestItemFetcher {
}
}
}
} catch(Exception e) {}
} catch (Exception e) {
}
}
private static void readItemToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
processCurrentItem();
currentItemid = 0;
currentCount = 0;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
else {
} else {
String d = getName(token);
if(d.equals("id")) {
if (d.equals("id")) {
currentItemid = Integer.parseInt(getValue(token));
} else if(d.equals("count")) {
} else if (d.equals("count")) {
currentCount = Integer.parseInt(getValue(token));
}
}
@@ -231,22 +197,19 @@ public class MapleQuestItemFetcher {
String d;
int temp;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting QuestId
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
}
else if(status == 2) { //start/complete
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
}
else if(status == 3) {
} else if (status == 3) {
d = getName(token);
if(d.contains("item")) {
if (d.contains("item")) {
temp = status;
inspectQuestItemList(temp);
} else {
@@ -256,41 +219,38 @@ public class MapleQuestItemFetcher {
status += 1;
} else {
if(status == 3) {
if (status == 3) {
d = getName(token);
if(d.equals("end")) {
if (d.equals("end")) {
limitedQuestids.add(questId);
}
}
}
}
private static void translateCheckToken(String token) {
String d;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting QuestId
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
}
else if(status == 2) { //start/complete
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
}
else if(status == 3) {
} else if (status == 3) {
forwardCursor(status);
}
status += 1;
} else {
if(status == 3) {
if (status == 3) {
d = getName(token);
if(d.equals("end")) {
if (d.equals("end")) {
limitedQuestids.add(questId);
}
}
@@ -299,14 +259,14 @@ public class MapleQuestItemFetcher {
private static void calculateQuestItemDiff() {
// This will remove started quest items from the "to complete" item set.
for(Entry<Integer, Set<Integer>> qd : startQuestItems.entrySet()) {
for(Integer qi : qd.getValue()) {
for (Map.Entry<Integer, Set<Integer>> qd : startQuestItems.entrySet()) {
for (Integer qi : qd.getValue()) {
Set<Integer> questSet = completeQuestItems.get(qd.getKey());
if(questSet != null) {
if(questSet.remove(qi)) {
if(completeQuestItems.isEmpty()) {
if (questSet != null) {
if (questSet.remove(qi)) {
if (completeQuestItems.isEmpty()) {
completeQuestItems.remove(qd.getKey());
}
}
@@ -314,37 +274,37 @@ public class MapleQuestItemFetcher {
}
}
}
private static List<Pair<Integer, Integer>> getPairsQuestItem() { // quest items not gained at WZ's quest start
List<Pair<Integer, Integer>> list = new ArrayList<>(initialLength);
for(Entry<Integer, Set<Integer>> qd : completeQuestItems.entrySet()) {
for(Integer qi : qd.getValue()) {
List<Pair<Integer, Integer>> list = new ArrayList<>(INITIAL_LENGTH);
for (Map.Entry<Integer, Set<Integer>> qd : completeQuestItems.entrySet()) {
for (Integer qi : qd.getValue()) {
list.add(new Pair<>(qi, qd.getKey()));
}
}
return list;
}
private static String getTableName(boolean dropdata) {
return dropdata ? "drop_data" : "reactordrops";
}
private static void filterQuestDropsOnTable(Pair<Integer, Integer> iq, List<Pair<Integer, Integer>> itemsWithQuest, boolean dropdata) throws SQLException {
PreparedStatement ps = con.prepareStatement("SELECT questid FROM " + getTableName(dropdata) + " WHERE itemid = ?;");
ps.setInt(1, iq.getLeft());
ResultSet rs = ps.executeQuery();
if (rs.isBeforeFirst()) {
while(rs.next()) {
while (rs.next()) {
int curQuest = rs.getInt(1);
if(curQuest != iq.getRight()) {
if (curQuest != iq.getRight()) {
Set<Integer> sqSet = startQuestItems.get(curQuest);
if(sqSet != null && sqSet.contains(iq.getLeft())) {
if (sqSet != null && sqSet.contains(iq.getLeft())) {
continue;
}
int[] mixed = new int[3];
mixed[0] = iq.getLeft();
mixed[1] = curQuest;
@@ -360,176 +320,171 @@ public class MapleQuestItemFetcher {
rs.close();
ps.close();
}
private static void filterQuestDropsOnDB(List<Pair<Integer, Integer>> itemsWithQuest) throws SQLException {
List<Pair<Integer, Integer>> copyItemsWithQuest = new ArrayList<>(itemsWithQuest);
try {
for(Pair<Integer, Integer> iq : copyItemsWithQuest) {
for (Pair<Integer, Integer> iq : copyItemsWithQuest) {
filterQuestDropsOnTable(iq, itemsWithQuest, true);
filterQuestDropsOnTable(iq, itemsWithQuest, false);
}
}
catch(SQLException e) {
} catch (SQLException e) {
e.printStackTrace();
}
}
private static void filterDirectorySearchMatchingData(String path, List<Pair<Integer, Integer>> itemsWithQuest) {
Iterator iter = FileUtils.iterateFiles(new File(directoryName + "/" + path), new String[]{"sql", "js", "txt","java"}, true);
while(iter.hasNext()) {
File file = (File) iter.next();
private static void filterDirectorySearchMatchingData(String path, List<Pair<Integer, Integer>> itemsWithQuest) {
Iterator<File> iter = FileUtils.iterateFiles(new File(path), new String[]{"sql", "js", "txt", "java"}, true);
while (iter.hasNext()) {
File file = iter.next();
fileSearchMatchingData(file, itemsWithQuest);
}
}
private static boolean foundMatchingDataOnFile(String fileContent, String searchStr) {
return fileContent.contains(searchStr);
}
private static void fileSearchMatchingData(File file, List<Pair<Integer, Integer>> itemsWithQuest) {
try {
String fileContent = FileUtils.readFileToString(file, "UTF-8");
String fileContent = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
List<Pair<Integer, Integer>> copyItemsWithQuest = new ArrayList<>(itemsWithQuest);
for(Pair<Integer, Integer> iq : copyItemsWithQuest) {
if(foundMatchingDataOnFile(fileContent, String.valueOf(iq.getLeft()))) {
for (Pair<Integer, Integer> iq : copyItemsWithQuest) {
if (foundMatchingDataOnFile(fileContent, String.valueOf(iq.getLeft()))) {
itemsWithQuest.remove(iq);
}
}
} catch(IOException ioe) {
} catch (IOException ioe) {
System.out.println("Failed to read file: " + file.getAbsolutePath());
ioe.printStackTrace();
}
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleQuestItemFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the underlying DB, server source files and the server-side WZ.xmls.");
printWriter.println();
}
private static List<Entry<Integer, Integer>> getSortedMapEntries0(Map<Integer, Integer> map) {
List<Entry<Integer, Integer>> list = new ArrayList<>(map.size());
private static List<Map.Entry<Integer, Integer>> getSortedMapEntries0(Map<Integer, Integer> map) {
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.size());
list.addAll(map.entrySet());
Collections.sort(list, (o1, o2) -> o1.getKey() - o2.getKey());
list.sort((o1, o2) -> o1.getKey() - o2.getKey());
return list;
}
private static List<Entry<Integer, int[]>> getSortedMapEntries1(Map<Integer, int[]> map) {
List<Entry<Integer, int[]>> list = new ArrayList<>(map.size());
private static List<Map.Entry<Integer, int[]>> getSortedMapEntries1(Map<Integer, int[]> map) {
List<Map.Entry<Integer, int[]>> list = new ArrayList<>(map.size());
list.addAll(map.entrySet());
Collections.sort(list, (o1, o2) -> o1.getKey() - o2.getKey());
list.sort((o1, o2) -> o1.getKey() - o2.getKey());
return list;
}
private static List<Pair<Integer, List<Integer>>> getSortedMapEntries2(Map<Integer, Set<Integer>> map) {
List<Pair<Integer, List<Integer>>> list = new ArrayList<>(map.size());
for(Entry<Integer, Set<Integer>> e : map.entrySet()) {
for (Map.Entry<Integer, Set<Integer>> e : map.entrySet()) {
List<Integer> il = new ArrayList<>(2);
il.addAll(e.getValue());
Collections.sort(il, (o1, o2) -> o1 - o2);
il.sort((o1, o2) -> o1 - o2);
list.add(new Pair<>(e.getKey(), il));
}
Collections.sort(list, (o1, o2) -> o1.getLeft() - o2.getLeft());
list.sort((o1, o2) -> o1.getLeft() - o2.getLeft());
return list;
}
private static String getExpiredStringLabel(int questid) {
return (!limitedQuestids.contains(questid) ? "" : " EXPIRED");
}
private static void ReportQuestItemData() {
private static void reportQuestItemData() {
// This will reference one line at a time
String line = null;
String fileName = null;
try {
Class.forName(driver).newInstance();
System.out.println("Reading WZs...");
fileName = wzPath + "/Quest.wz/Check.img.xml";
fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
fileName = WZFiles.QUEST.getFilePath() + "/Check.img.xml";
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateCheckToken(line); // fetch expired quests through here as well
}
bufferedReader.close();
fileReader.close();
fileName = wzPath + "/Quest.wz/Act.img.xml";
fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
fileName = WZFiles.QUEST.getFilePath() + "/Act.img.xml";
fileReader = new InputStreamReader(new FileInputStream(fileName), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateActToken(line);
}
bufferedReader.close();
fileReader.close();
System.out.println("Calculating table diffs...");
calculateQuestItemDiff();
System.out.println("Filtering drops on DB...");
List<Pair<Integer, Integer>> itemsWithQuest = getPairsQuestItem();
// filter drop data on DB
con = DriverManager.getConnection(host, username, password);
filterQuestDropsOnDB(itemsWithQuest);
con.close();
System.out.println("Filtering drops on project files...");
// finally, filter whether this item is mentioned on the source code or not.
filterDirectorySearchMatchingData("scripts", itemsWithQuest);
filterDirectorySearchMatchingData("sql", itemsWithQuest);
filterDirectorySearchMatchingData("src", itemsWithQuest);
System.out.println("Reporting results...");
// report suspects of missing quest drop data, as well as those drop data that may have incorrect questids.
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
if(!mixedQuestidItems.isEmpty()) {
if (!mixedQuestidItems.isEmpty()) {
printWriter.println("INCORRECT QUESTIDS ON DB");
for(Entry<Integer, int[]> emqi : getSortedMapEntries1(mixedQuestidItems)) {
for (Map.Entry<Integer, int[]> emqi : getSortedMapEntries1(mixedQuestidItems)) {
int[] mqi = emqi.getValue();
printWriter.println(mqi[0] + " : " + mqi[1] + " -> " + mqi[2] + getExpiredStringLabel(mqi[2]));
}
printWriter.println("\n\n\n\n\n");
}
if(!itemsWithQuest.isEmpty()) {
if (!itemsWithQuest.isEmpty()) {
Map<Integer, Integer> mapIwq = new HashMap<>(itemsWithQuest.size());
for(Pair<Integer, Integer> iwq : itemsWithQuest) {
for (Pair<Integer, Integer> iwq : itemsWithQuest) {
mapIwq.put(iwq.getLeft(), iwq.getRight());
}
printWriter.println("ITEMS WITH NO QUEST DROP DATA ON DB");
for(Entry<Integer, Integer> iwq : getSortedMapEntries0(mapIwq)) {
for (Map.Entry<Integer, Integer> iwq : getSortedMapEntries0(mapIwq)) {
printWriter.println(iwq.getKey() + " - " + iwq.getValue() + getExpiredStringLabel(iwq.getValue()));
}
printWriter.println("\n\n\n\n\n");
}
if(displayExtraInfo) {
if(!zeroedStartQuestItems.isEmpty()) {
if (DISPLAY_EXTRA_INFO) {
if (!zeroedStartQuestItems.isEmpty()) {
printWriter.println("START QUEST ITEMS WITH ZERO QUANTITY");
for(Pair<Integer, List<Integer>> iwq : getSortedMapEntries2(zeroedStartQuestItems)) {
for (Pair<Integer, List<Integer>> iwq : getSortedMapEntries2(zeroedStartQuestItems)) {
printWriter.println(iwq.getLeft() + getExpiredStringLabel(iwq.getLeft()) + ":");
for(Integer i : iwq.getRight()) {
for (Integer i : iwq.getRight()) {
printWriter.println(" " + i);
}
printWriter.println();
@@ -537,11 +492,11 @@ public class MapleQuestItemFetcher {
printWriter.println("\n\n\n\n\n");
}
if(!zeroedCompleteQuestItems.isEmpty()) {
if (!zeroedCompleteQuestItems.isEmpty()) {
printWriter.println("COMPLETE QUEST ITEMS WITH ZERO QUANTITY");
for(Pair<Integer, List<Integer>> iwq : getSortedMapEntries2(zeroedCompleteQuestItems)) {
for (Pair<Integer, List<Integer>> iwq : getSortedMapEntries2(zeroedCompleteQuestItems)) {
printWriter.println(iwq.getLeft() + getExpiredStringLabel(iwq.getLeft()) + ":");
for(Integer i : iwq.getRight()) {
for (Integer i : iwq.getRight()) {
printWriter.println(" " + i);
}
printWriter.println();
@@ -552,40 +507,23 @@ public class MapleQuestItemFetcher {
printWriter.close();
System.out.println("Done!");
}
catch(FileNotFoundException ex) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + fileName + "'");
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println("Error reading file '" + fileName + "'");
}
catch(SQLException e) {
} catch (SQLException e) {
System.out.println("Warning: Could not establish connection to database to report quest data.");
System.out.println(e.getMessage());
}
catch(ClassNotFoundException e) {
System.out.println("Error: could not find class");
System.out.println(e.getMessage());
}
catch(InstantiationException e) {
System.out.println("Error: instantiation failure");
System.out.println(e.getMessage());
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.setProperty("wzpath", wzPath);
DatabaseConnection.initializeConnectionPool(); // MapleItemInformationProvider loads some unrelated db data
ii = MapleItemInformationProvider.getInstance();
ReportQuestItemData();
reportQuestItemData();
}
}

View File

@@ -0,0 +1,263 @@
package tools.mapletools;
import provider.wz.WZFiles;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @author RonanLana
* <p>
* This application parses the Quest.wz file inputted and generates a report showing
* all cases where a quest takes a meso fee to complete a quest, but it doesn't
* properly checks the player for the needed amount before completing it.
* <p>
* Running it should generate a report file under "output" folder with the search results.
*/
public class QuestMesoFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("quest_meso_report.txt");
private static final boolean PRINT_FEES = true; // print missing values as additional info report
private static final int INITIAL_STRING_LENGTH = 50;
private static final Map<Integer, Integer> checkedMesoQuests = new HashMap<>();
private static final Map<Integer, Integer> appliedMesoQuests = new HashMap<>();
private static final Set<Integer> checkedEndscriptQuests = new HashSet<>();
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int questId = -1;
private static int isCompleteState = 0;
private static int currentMeso = 0;
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("value");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void translateTokenAct(String token) {
String d;
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
} else if (status == 3) {
forwardCursor(status);
}
status += 1;
} else {
if (token.contains("money")) {
if (isCompleteState != 0) {
d = getValue(token);
currentMeso = -1 * Integer.parseInt(d);
if (currentMeso > 0) {
appliedMesoQuests.put(questId, currentMeso);
}
}
}
}
}
private static void translateTokenCheck(String token) {
String d;
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
} else if (status == 3) {
forwardCursor(status);
}
status += 1;
} else {
if (token.contains("endmeso")) {
d = getValue(token);
currentMeso = Integer.parseInt(d);
checkedMesoQuests.put(questId, currentMeso);
} else if (token.contains("endscript")) {
checkedEndscriptQuests.add(questId);
}
}
}
private static void readQuestMesoData() throws IOException {
String line;
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(WZFiles.QUEST.getFilePath() + "/Act.img.xml"), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
translateTokenAct(line);
}
bufferedReader.close();
fileReader.close();
fileReader = new InputStreamReader(new FileInputStream(WZFiles.QUEST.getFilePath() + "/Check.img.xml"), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
translateTokenCheck(line);
}
bufferedReader.close();
fileReader.close();
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleQuestMesoFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static void printReportFileResults(Map<Integer, Integer> target, Map<Integer, Integer> base, boolean testingCheck) {
List<Integer> result = new ArrayList<>();
List<Integer> error = new ArrayList<>();
Map<Integer, Integer> questFee = new HashMap<>();
for (Map.Entry<Integer, Integer> e : base.entrySet()) {
Integer v = target.get(e.getKey());
if (v == null) {
if (testingCheck || !checkedEndscriptQuests.contains(e.getKey())) {
result.add(e.getKey());
questFee.put(e.getKey(), e.getValue());
}
} else if (v.intValue() != e.getValue().intValue()) {
error.add(e.getKey());
}
}
if (!result.isEmpty() || !error.isEmpty()) {
printWriter.println("MISMATCH INFORMATION ON '" + (testingCheck ? "check" : "act") + "':");
if (!result.isEmpty()) {
result.sort((o1, o2) -> o1 - o2);
printWriter.println("# MISSING");
if (!PRINT_FEES) {
for (Integer i : result) {
printWriter.println(i);
}
} else {
for (Integer i : result) {
printWriter.println(i + " " + questFee.get(i));
}
}
printWriter.println();
}
if (!error.isEmpty() && testingCheck) {
error.sort((o1, o2) -> o1 - o2);
printWriter.println("# WRONG VALUE");
for (Integer i : error) {
printWriter.println(i);
}
printWriter.println();
}
printWriter.println("\r\n");
}
}
private static void reportQuestMesoData() {
// This will reference one line at a time
try {
System.out.println("Reading WZs...");
readQuestMesoData();
System.out.println("Reporting results...");
// report missing meso checks on quest completes
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
printReportFileResults(checkedMesoQuests, appliedMesoQuests, true);
printReportFileResults(appliedMesoQuests, checkedMesoQuests, false);
printWriter.close();
System.out.println("Done!");
} catch (FileNotFoundException ex) {
System.out.println("Unable to open quest file.");
} catch (IOException ex) {
System.out.println("Error reading quest file.");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reportQuestMesoData();
}
}

View File

@@ -1,89 +1,47 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
import provider.wz.WZFiles;
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package maplequestlinefetcher;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
import java.util.Map;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
*
* @author RonanLana
This application parses the Quest.wz file inputted and generates a report showing
all cases where quest script files have not been found for quests that requires a
script file.
As an extension, it highlights missing script files for questlines that hand over
skills as rewards.
Running it should generate a report file under "lib" folder with the search results.
* <p>
* This application parses the Quest.wz file inputted and generates a report showing
* all cases where quest script files have not been found for quests that requires a
* script file.
* As an extension, it highlights missing script files for questlines that hand over
* skills as rewards.
* <p>
* Running it should generate a report file under "output" folder with the search results.
*/
public class MapleQuestlineFetcher {
static String actName = "../../wz/Quest.wz/Act.img.xml";
static String checkName = "../../wz/Quest.wz/Check.img.xml";
static String directoryName = "../..";
static String newFile = "lib/QuestReport.txt";
public class QuestlineFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("questline_report.txt");
private static final String ACT_NAME = WZFiles.QUEST.getFilePath() + "/Act.img.xml";
private static final String CHECK_NAME = WZFiles.QUEST.getFilePath() + "/Check.img.xml";
private static final int INITIAL_STRING_LENGTH = 50;
private static final Stack<Integer> skillObtainableQuests = new Stack<>();
private static final Set<Integer> scriptedQuestFiles = new HashSet<>();
private static final Set<Integer> expiredQuests = new HashSet<>();
private static final Map<Integer, List<Integer>> questDependencies = new HashMap<>();
private static final Set<Integer> nonScriptedQuests = new HashSet<>();
private static final Set<Integer> skillObtainableNonScriptedQuests = new HashSet<>();
private static PrintWriter printWriter = null;
private static InputStreamReader fileReader = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int questId = -1;
private static int isCompleteState = 0;
private static boolean isScriptedQuest;
private static boolean isExpiredQuest;
private static List<Integer> questDependencyList;
private static int curQuestId;
private static int curQuestState;
static Connection con = null;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static int initialLength = 200;
static int initialStringLength = 50;
static byte status = 0;
static int questId = -1;
static int isCompleteState = 0;
static boolean isScriptedQuest;
static boolean isExpiredQuest;
static List<Integer> questDependencyList;
static int curQuestId;
static int curQuestState;
static Stack<Integer> skillObtainableQuests = new Stack<>();
static Set<Integer> scriptedQuestFiles = new HashSet<>();
static Set<Integer> expiredQuests = new HashSet<>();
static Map<Integer, List<Integer>> questDependencies = new HashMap<>();
static Set<Integer> nonScriptedQuests = new HashSet<>();
static Set<Integer> skillObtainableNonScriptedQuests = new HashSet<>();
private static String getName(String token) {
int i, j;
char[] dest;
@@ -93,13 +51,13 @@ public class MapleQuestlineFetcher {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -109,108 +67,97 @@ public class MapleQuestlineFetcher {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void translateTokenCheck(String token) {
String d;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
if(status == 1) {
if (status == 1) {
evaluateCurrentQuest();
}
else if(status == 4) {
} else if (status == 4) {
evaluateCurrentQuestDependency();
}
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting QuestId
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
isScriptedQuest = false;
isExpiredQuest = false;
questDependencyList = new LinkedList<>();
}
else if(status == 2) { //start/complete
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
}
else if(status == 3) {
if(isCompleteState == 1 || !token.contains("quest")) {
} else if (status == 3) {
if (isCompleteState == 1 || !token.contains("quest")) {
forwardCursor(status);
}
}
status += 1;
}
else {
if(status == 3) {
} else {
if (status == 3) {
d = getName(token);
if(d.contains("script")) {
if (d.contains("script")) {
isScriptedQuest = true;
} else if(d.contains("end")) {
} else if (d.contains("end")) {
isExpiredQuest = true;
}
}
else if(status == 5) {
} else if (status == 5) {
readQuestLabel(token);
}
}
}
private static void translateTokenAct(String token) {
String d;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting QuestId
} else if (token.contains("imgdir")) {
if (status == 1) { //getting QuestId
d = getName(token);
questId = Integer.parseInt(d);
}
else if(status == 2) { //start/complete
} else if (status == 2) { //start/complete
d = getName(token);
isCompleteState = Integer.parseInt(d);
}
else if(status == 3) {
if(isCompleteState == 1 && token.contains("skill")) {
} else if (status == 3) {
if (isCompleteState == 1 && token.contains("skill")) {
skillObtainableQuests.add(questId);
}
forwardCursor(status);
}
status += 1;
}
}
@@ -219,34 +166,29 @@ public class MapleQuestlineFetcher {
String name = getName(token);
String value = getValue(token);
switch(name) {
case "id":
curQuestId = Integer.parseInt(value);
break;
case "state":
curQuestState = Integer.parseInt(value);
break;
switch (name) {
case "id" -> curQuestId = Integer.parseInt(value);
case "state" -> curQuestState = Integer.parseInt(value);
}
}
private static void evaluateCurrentQuestDependency() {
if(curQuestState == 2) {
if (curQuestState == 2) {
questDependencyList.add(curQuestId);
}
}
private static void evaluateCurrentQuest() {
if(isScriptedQuest && !scriptedQuestFiles.contains(questId)) {
if (isScriptedQuest && !scriptedQuestFiles.contains(questId)) {
nonScriptedQuests.add(questId);
}
if(isExpiredQuest) {
if (isExpiredQuest) {
expiredQuests.add(questId);
}
questDependencies.put(questId, questDependencyList);
}
private static void instantiateQuestScriptFiles(String directoryName) {
File directory = new File(directoryName);
@@ -255,130 +197,126 @@ public class MapleQuestlineFetcher {
for (File file : fList) {
if (file.isFile()) {
String fname = file.getName();
try {
Integer questid = Integer.parseInt(fname.substring(0, fname.indexOf('.')));
scriptedQuestFiles.add(questid);
} catch(NumberFormatException nfe) {}
} catch (NumberFormatException nfe) {
}
}
}
}
private static void readQuestsWithMissingScripts() throws IOException {
String line;
fileReader = new InputStreamReader(new FileInputStream(checkName), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(CHECK_NAME), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateTokenCheck(line);
}
bufferedReader.close();
fileReader.close();
}
private static void readQuestsWithSkillReward() throws IOException {
String line;
fileReader = new InputStreamReader(new FileInputStream(actName), "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(ACT_NAME), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateTokenAct(line);
}
bufferedReader.close();
fileReader.close();
}
private static void calculateSkillRelatedMissingQuestScripts() {
Stack<Integer> frontierQuests = skillObtainableQuests;
Set<Integer> solvedQuests = new HashSet<>();
while(!frontierQuests.isEmpty()) {
while (!frontierQuests.isEmpty()) {
Integer questid = frontierQuests.pop();
solvedQuests.add(questid);
if(nonScriptedQuests.contains(questid)) {
if (nonScriptedQuests.contains(questid)) {
skillObtainableNonScriptedQuests.add(questid);
nonScriptedQuests.remove(questid);
}
List<Integer> questDependency = questDependencies.get(questid);
for(Integer i : questDependency) {
if(!solvedQuests.contains(i)) {
for (Integer i : questDependency) {
if (!solvedQuests.contains(i)) {
frontierQuests.add(i);
}
}
}
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleQuestlineFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static List<Integer> getSortedListEntries(Set<Integer> set) {
List<Integer> list = new ArrayList<>(set);
Collections.sort(list);
return list;
}
private static void printReportFileResults() {
if(!skillObtainableNonScriptedQuests.isEmpty()) {
if (!skillObtainableNonScriptedQuests.isEmpty()) {
printWriter.println("SKILL-RELATED NON-SCRIPTED QUESTS");
for(Integer nsq : getSortedListEntries(skillObtainableNonScriptedQuests)) {
for (Integer nsq : getSortedListEntries(skillObtainableNonScriptedQuests)) {
printWriter.println(" " + nsq + (expiredQuests.contains(nsq) ? " EXPIRED" : ""));
}
printWriter.println();
}
printWriter.println("\nCOMMON NON-SCRIPTED QUESTS");
for(Integer nsq : getSortedListEntries(nonScriptedQuests)) {
for (Integer nsq : getSortedListEntries(nonScriptedQuests)) {
printWriter.println(" " + nsq + (expiredQuests.contains(nsq) ? " EXPIRED" : ""));
}
}
private static void ReportQuestlineData() {
private static void reportQuestlineData() {
// This will reference one line at a time
try {
System.out.println("Reading quest scripts...");
instantiateQuestScriptFiles(directoryName + "/scripts/quest");
instantiateQuestScriptFiles(ToolConstants.SCRIPTS_PATH + "/quest");
System.out.println("Reading WZs...");
readQuestsWithSkillReward();
readQuestsWithMissingScripts();
System.out.println("Calculating skill related quests...");
calculateSkillRelatedMissingQuestScripts();
System.out.println("Reporting results...");
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
printReportFileResults();
printWriter.close();
System.out.println("Done!");
}
catch(FileNotFoundException ex) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open quest file.");
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println("Error reading quest file.");
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
/*
private static List<Pair<Integer, List<Integer>>> getSortedMapEntries(Map<Integer, List<Integer>> map) {
List<Pair<Integer, List<Integer>>> list = new ArrayList<>(map.size());
@@ -387,27 +325,27 @@ public class MapleQuestlineFetcher {
for(Integer i : e.getValue()) {
il.add(i);
}
Collections.sort(il, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
list.add(new Pair<>(e.getKey(), il));
}
Collections.sort(list, new Comparator<Pair<Integer, List<Integer>>>() {
@Override
public int compare(Pair<Integer, List<Integer>> o1, Pair<Integer, List<Integer>> o2) {
return o1.getLeft() - o2.getLeft();
}
});
return list;
}
private static void DumpQuestlineData() {
for(Pair<Integer, List<Integer>> questDependency : getSortedMapEntries(questDependencies)) {
if(!questDependency.right.isEmpty()) {
@@ -416,9 +354,9 @@ public class MapleQuestlineFetcher {
}
}
*/
public static void main(String[] args) {
ReportQuestlineData();
reportQuestlineData();
}
}

View File

@@ -0,0 +1,112 @@
package tools.mapletools;
import java.io.File;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
/**
* @author RonanLana
* <p>
* This application reports in reactor ids that have drops on the SQL table but are
* not yet coded.
*/
public class ReactorDropFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("reactor_drop_report.txt");
private static final String REACTOR_SCRIPT_PATH = ToolConstants.SCRIPTS_PATH + "/reactor";
private static final Connection con = SimpleDatabaseConnection.getConnection();
private static PrintWriter printWriter = null;
private static final Set<Integer> reactors = new HashSet<>();
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleReactorDropFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the underlying DB and the server-side files.");
printWriter.println();
}
private static int getReactorIdFromFilename(String name) {
try {
return Integer.parseInt(name.substring(0, name.indexOf('.')));
} catch (Exception e) {
return -1;
}
}
private static void removeScriptedReactorids(String directoryName) {
File directory = new File(directoryName);
// get all the files from a directory
File[] fList = directory.listFiles();
for (File file : fList) {
if (file.isFile()) {
reactors.remove(getReactorIdFromFilename(file.getName()));
}
}
}
private static void loadReactoridsOnDB() throws SQLException {
PreparedStatement ps = con.prepareStatement("SELECT DISTINCT reactorid FROM reactordrops;");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
reactors.add(rs.getInt("reactorid"));
}
rs.close();
ps.close();
}
private static List<Integer> getSortedReactorids() {
List<Integer> sortedReactors = new ArrayList<>(reactors);
Collections.sort(sortedReactors);
return sortedReactors;
}
private static void fetchMissingReactorDrops() throws SQLException {
loadReactoridsOnDB();
removeScriptedReactorids(REACTOR_SCRIPT_PATH);
}
private static void reportMissingReactorDrops() throws SQLException {
if (!reactors.isEmpty()) {
printWriter.println("MISSING REACTOR DROP SCRIPTS");
for (Integer reactorid : getSortedReactorids()) {
printWriter.println(" " + reactorid);
}
printWriter.println("\n");
}
}
private static void reportMissingReactors() {
try {
System.out.println("Fetching reactors from DB...");
fetchMissingReactorDrops();
con.close();
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
// report suspects of missing quest drop data, as well as those drop data that may have incorrect questids.
System.out.println("Reporting results...");
printReportFileHeader();
reportMissingReactorDrops();
printWriter.close();
System.out.println("Done!");
} catch (SQLException e) {
System.out.println("Warning: Could not establish connection to database to report quest data.");
System.out.println(e.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reportMissingReactors();
}
}

View File

@@ -0,0 +1,30 @@
package tools.mapletools;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.config.Configurator;
import tools.DatabaseConnection;
import java.sql.Connection;
import java.sql.SQLException;
final class SimpleDatabaseConnection {
private SimpleDatabaseConnection() {}
static Connection getConnection() {
muffleLogging();
DatabaseConnection.initializeConnectionPool();
try {
return DatabaseConnection.getConnection();
} catch (SQLException e) {
throw new IllegalStateException("Failed to get database connection", e);
}
}
private static void muffleLogging() {
final Level minimumVisibleLevel = Level.WARN;
Configurator.setLevel(LogManager.getLogger(com.zaxxer.hikari.HikariDataSource.class).getName(), minimumVisibleLevel);
Configurator.setRootLevel(minimumVisibleLevel);
}
}

View File

@@ -1,78 +1,55 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleskillmakerfetcher;
import provider.wz.WZFiles;
import server.MapleItemInformationProvider;
import tools.DatabaseConnection;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.io.*;
/**
* @author RonanLana
*
* <p>
* The objective of this program is to uncover all maker data from the
* ItemMaker.wz.xml files and generate a SQL file with every data info
* for the Maker DB tables.
*
*/
public class MapleSkillMakerFetcher {
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
public class SkillMakerFetcher {
private static final File INPUT_FILE = new File(WZFiles.ETC.getFile(), "ItemMake.img.xml");
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("maker-data.sql");
private static final int INITIAL_STRING_LENGTH = 50;
static String fileName = "../../wz/Etc.wz/ItemMake.img.xml";
static String newFile = "lib/MakerData.sql";
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static byte state = 0;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static byte status = 0;
static byte state = 0;
static int initialStringLength = 50;
// maker data fields
static int id = -1;
static int itemid = -1;
static int reqLevel = -1;
static int reqMakerLevel = -1;
static int reqItem = -1;
static int reqMeso = -1;
static int reqEquip = -1;
static int catalyst = -1;
static int quantity = -1;
static int tuc = -1;
static int recipePos = -1;
static int recipeProb = -1;
static int recipeCount = -1;
static int recipeItem = -1;
private static int id = -1;
private static int itemid = -1;
private static int reqLevel = -1;
private static int reqMakerLevel = -1;
private static int reqItem = -1;
private static int reqMeso = -1;
private static int reqEquip = -1;
private static int catalyst = -1;
private static int quantity = -1;
private static int tuc = -1;
private static int recipePos = -1;
private static int recipeProb = -1;
private static int recipeCount = -1;
private static int recipeItem = -1;
static List<int[]> recipeList = null;
static List<int[]> randomList = null;
static List<MapleMakerItemEntry> makerList = new ArrayList<>(100);
static List<MakerItemEntry> makerList = new ArrayList<>(100);
private static void resetMakerDataFields() {
reqLevel = 0;
reqMakerLevel = 0;
@@ -82,16 +59,16 @@ public class MapleSkillMakerFetcher {
catalyst = 0;
quantity = 0;
tuc = 0;
recipePos = 0;
recipeProb = 0;
recipeCount = 0;
recipeItem = 0;
recipeList = null;
randomList = null;
}
private static String getName(String token) {
int i, j;
char[] dest;
@@ -101,16 +78,16 @@ public class MapleSkillMakerFetcher {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
String s = d.trim();
s.replaceFirst("^0+(?!$)", "");
return(s);
return (s);
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -120,157 +97,125 @@ public class MapleSkillMakerFetcher {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
String s = d.trim();
s.replaceFirst("^0+(?!$)", "");
return(s);
return (s);
}
private static int[] generateRecipeItem() {
int pair[] = new int[2];
int[] pair = new int[2];
pair[0] = recipeItem;
pair[1] = recipeCount;
return pair;
}
private static int[] generateRandomItem() {
int tuple[] = new int[3];
int[] tuple = new int[3];
tuple[0] = recipeItem;
tuple[1] = recipeCount;
tuple[2] = recipeProb;
return tuple;
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateToken(String token) {
String d;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
if(status == 2) { //close item maker data
if (status == 2) { //close item maker data
generateUpdatedItemFee(); // for equipments, this will try to update reqMeso to be conformant with the client.
makerList.add(new MapleMakerItemEntry(id, itemid, reqLevel, reqMakerLevel, reqItem, reqMeso, reqEquip, catalyst, quantity, tuc, recipeCount, recipeItem, recipeList, randomList));
makerList.add(new MakerItemEntry(id, itemid, reqLevel, reqMakerLevel, reqItem, reqMeso, reqEquip, catalyst, quantity, tuc, recipeCount, recipeItem, recipeList, randomList));
resetMakerDataFields();
} else if(status == 4) { //close recipe/random item
if(state == 0) recipeList.add(generateRecipeItem());
else if(state == 1) randomList.add(generateRandomItem());
} else if (status == 4) { //close recipe/random item
if (state == 0) {
recipeList.add(generateRecipeItem());
} else if (state == 1) {
randomList.add(generateRandomItem());
}
}
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting id
} else if (token.contains("imgdir")) {
if (status == 1) { //getting id
d = getName(token);
id = Integer.parseInt(d);
System.out.println("Parsing maker id " + id);
}
else if(status == 2) { //getting target item id
} else if (status == 2) { //getting target item id
d = getName(token);
itemid = Integer.parseInt(d);
}
else if(status == 3) {
} else if (status == 3) {
d = getName(token);
switch(d) {
case "recipe":
switch (d) {
case "recipe" -> {
recipeList = new LinkedList<>();
state = 0;
break;
case "randomReward":
}
case "randomReward" -> {
randomList = new LinkedList<>();
state = 1;
break;
default:
forwardCursor(3); // unused content, read until end of block
break;
}
default -> forwardCursor(3); // unused content, read until end of block
}
}
else if(status == 4) { // inside recipe/random
} else if (status == 4) { // inside recipe/random
d = getName(token);
recipePos = Integer.parseInt(d);
}
status += 1;
} else {
if(status == 3) {
if (status == 3) {
d = getName(token);
switch(d) {
case "itemNum":
quantity = Integer.valueOf(getValue(token));
break;
case "meso":
reqMeso = Integer.valueOf(getValue(token));
break;
case "reqItem":
reqItem = Integer.valueOf(getValue(token));
break;
case "reqLevel":
reqLevel = Integer.valueOf(getValue(token));
break;
case "reqSkillLevel":
reqMakerLevel = Integer.valueOf(getValue(token));
break;
case "tuc":
tuc = Integer.valueOf(getValue(token));
break;
case "catalyst":
catalyst = Integer.valueOf(getValue(token));
break;
case "reqEquip":
reqEquip = Integer.valueOf(getValue(token));
break;
default:
switch (d) {
case "itemNum" -> quantity = Integer.parseInt(getValue(token));
case "meso" -> reqMeso = Integer.parseInt(getValue(token));
case "reqItem" -> reqItem = Integer.parseInt(getValue(token));
case "reqLevel" -> reqLevel = Integer.parseInt(getValue(token));
case "reqSkillLevel" -> reqMakerLevel = Integer.parseInt(getValue(token));
case "tuc" -> tuc = Integer.parseInt(getValue(token));
case "catalyst" -> catalyst = Integer.parseInt(getValue(token));
case "reqEquip" -> reqEquip = Integer.parseInt(getValue(token));
default -> {
System.out.println("Unhandled case: '" + d + "'");
state = 2;
break;
}
}
}
else if(status == 5) { // inside recipe/random item
} else if (status == 5) { // inside recipe/random item
d = getName(token);
if(d.equals("item")) {
if (d.equals("item")) {
recipeItem = Integer.parseInt(getValue(token));
} else {
if(state == 0) {
if (state == 0) {
recipeCount = Integer.parseInt(getValue(token));
} else {
if(d.equals("itemNum")) {
if (d.equals("itemNum")) {
recipeCount = Integer.parseInt(getValue(token));
} else {
recipeProb = Integer.parseInt(getValue(token));
@@ -280,17 +225,17 @@ public class MapleSkillMakerFetcher {
}
}
}
private static void generateUpdatedItemFee() {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
float adjPrice = reqMeso;
if(itemid < 2000000) {
if (itemid < 2000000) {
Map<String, Integer> stats = ii.getEquipStats(itemid);
if(stats != null) {
if (stats != null) {
int val = itemid / 100000;
if(val == 13 || val == 14) { // is weapon-type
if (val == 13 || val == 14) { // is weapon-type
adjPrice /= 10;
adjPrice += reqMeso;
@@ -314,82 +259,78 @@ public class MapleSkillMakerFetcher {
reqMeso = 1000 * (int) Math.ceil(adjPrice);
}
}
private static void WriteMakerTableFile() {
printWriter.println(" # SQL File autogenerated from the MapleSkillMakerFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data is conformant with the ItemMake.img.xml file used to compile this.");
printWriter.println();
StringBuilder sb_create = new StringBuilder("INSERT IGNORE INTO `makercreatedata` (`id`, `itemid`, `req_level`, `req_maker_level`, `req_meso`, `req_item`, `req_equip`, `catalyst`, `quantity`, `tuc`) VALUES\r\n");
StringBuilder sb_recipe = new StringBuilder("INSERT IGNORE INTO `makerrecipedata` (`itemid`, `req_item`, `count`) VALUES\r\n");
StringBuilder sb_reward = new StringBuilder("INSERT IGNORE INTO `makerrewarddata` (`itemid`, `rewardid`, `quantity`, `prob`) VALUES\r\n");
for(MapleMakerItemEntry it : makerList) {
for (MakerItemEntry it : makerList) {
sb_create.append(" (" + it.id + ", " + it.itemid + ", " + it.reqLevel + ", " + it.reqMakerLevel + ", " + it.reqMeso + ", " + it.reqItem + ", " + it.reqEquip + ", " + it.catalyst + ", " + it.quantity + ", " + it.tuc + "),\r\n");
if(it.recipeList != null) {
for(int[] rit : it.recipeList) {
sb_recipe.append(" (" + it.itemid + ", " + rit[0] + ", " + rit[1] + "),\r\n");
if (it.recipeList != null) {
for (int[] rit : it.recipeList) {
sb_recipe.append(" (" + it.itemid + ", " + rit[0] + ", " + rit[1] + "),\r\n");
}
}
if(it.randomList != null) {
for(int[] rit : it.randomList) {
if (it.randomList != null) {
for (int[] rit : it.randomList) {
sb_reward.append(" (" + it.itemid + ", " + rit[0] + ", " + rit[1] + ", " + rit[2] + "),\r\n");
}
}
}
sb_create.setLength(sb_create.length() - 3);
sb_create.append(";\r\n");
sb_recipe.setLength(sb_recipe.length() - 3);
sb_recipe.append(";\r\n");
sb_reward.setLength(sb_reward.length() - 3);
sb_reward.append(";");
printWriter.println(sb_create);
printWriter.println(sb_recipe);
printWriter.println(sb_reward);
}
private static void WriteMakerTableData() {
private static void writeMakerTableData() {
// This will reference one line at a time
String line = null;
try {
printWriter = new PrintWriter(newFile, "UTF-8");
fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
resetMakerDataFields();
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
WriteMakerTableFile();
printWriter.close();
bufferedReader.close();
fileReader.close();
}
catch(FileNotFoundException ex) {
System.out.println("Unable to open file '" + fileName + "'");
}
catch(IOException ex) {
System.out.println("Error reading file '" + fileName + "'");
}
catch(Exception e) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + INPUT_FILE + "'");
} catch (IOException ex) {
System.out.println("Error reading file '" + INPUT_FILE + "'");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WriteMakerTableData();
DatabaseConnection.initializeConnectionPool(); // Using MapleItemInformationProvider which loads som unrelated things from the db
writeMakerTableData();
}
}

View File

@@ -1,57 +1,31 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleskillmakerreagentindexer;
import provider.wz.WZFiles;
import tools.Pair;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
* @author RonanLana
*
* <p>
* The main objective of this program is to index relevant reagent data
* from the Item.wz folder and generate a SQL table with them, to be used
* by the server source.
*
*/
public class MapleSkillMakerReagentIndexer {
static String host = "jdbc:mysql://localhost:3306/cosmic";
static String driver = "com.mysql.jdbc.Driver";
static String username = "cosmic_server";
static String password = "snailshell";
public class SkillMakerReagentIndexer {
private static final File INPUT_FILE = new File(WZFiles.ITEM.getFile(), "Etc/0425.img.xml");
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("maker-reagent-data.sql");
private static final int INITIAL_STRING_LENGTH = 50;
private static final List<Pair<Integer, Pair<String, Integer>>> reagentList = new ArrayList<>();
static String fileName = "../../wz/Item.wz/Etc/0425.img.xml";
static String newFile = "lib/MakerReagentData.sql";
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static byte status = 0;
private static int id = -1;
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static byte status = 0;
static int id = -1;
static List<Pair<Integer, Pair<String, Integer>>> reagentList = new ArrayList<>();
static int initialStringLength = 50;
private static String getName(String token) {
int i, j;
char[] dest;
@@ -61,13 +35,13 @@ public class MapleSkillMakerReagentIndexer {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -77,140 +51,132 @@ public class MapleSkillMakerReagentIndexer {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void translateToken(String token) {
String d;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
if(status == 1) { //getting id
} else if (token.contains("imgdir")) {
if (status == 1) { //getting id
d = getName(token);
id = Integer.parseInt(d);
System.out.println("Parsing maker reagent id " + id);
} else if(status == 2) {
} else if (status == 2) {
d = getName(token);
if(!d.equals("info")) {
if (!d.equals("info")) {
System.out.println("not info");
forwardCursor(status);
}
}
status += 1;
} else {
if(status == 3) {
if(token.contains("int")) {
if (status == 3) {
if (token.contains("int")) {
d = getName(token);
if(d.contains("inc") || d.contains("rand")) {
if (d.contains("inc") || d.contains("rand")) {
Integer v = Integer.valueOf(getValue(token));
Pair<String, Integer> reagBuff = new Pair<>(d, v);
Pair<Integer, Pair<String, Integer>> reagItem = new Pair<>(id, reagBuff);
reagentList.add(reagItem);
}
} else {
if(token.contains("canvas")) {
if (token.contains("canvas")) {
forwardCursor(status + 1);
}
}
}
}
}
private static void SortReagentList() {
Collections.sort(reagentList, (p1, p2) -> p1.getLeft().compareTo(p2.getLeft()));
reagentList.sort((p1, p2) -> p1.getLeft().compareTo(p2.getLeft()));
}
private static void WriteMakerReagentTableFile() {
printWriter.println(" # SQL File autogenerated from the MapleSkillMakerReagentIndexer feature by Ronan Lana.");
printWriter.println(" # Generated data is conformant with the Item.wz folder used to compile this.");
printWriter.println();
printWriter.println("CREATE TABLE IF NOT EXISTS `makerreagentdata` (");
printWriter.println(" `itemid` int(11) NOT NULL,");
printWriter.println(" `stat` varchar(20) NOT NULL,");
printWriter.println(" `value` smallint(6) NOT NULL,");
printWriter.println(" PRIMARY KEY (`itemid`)");
printWriter.println(") ENGINE=MyISAM DEFAULT CHARSET=latin1;");
printWriter.println(");");
printWriter.println();
StringBuilder sb = new StringBuilder("INSERT IGNORE INTO `makerreagentdata` (`itemid`, `stat`, `value`) VALUES\r\n");
for(Pair<Integer, Pair<String, Integer>> it : reagentList) {
for (Pair<Integer, Pair<String, Integer>> it : reagentList) {
sb.append(" (" + it.left + ", \"" + it.right.left + "\", " + it.right.right + "),\r\n");
}
sb.setLength(sb.length() - 3);
sb.append(";");
printWriter.println(sb);
}
private static void WriteMakerReagentTableData() {
private static void writeMakerReagentTableData() {
// This will reference one line at a time
String line = null;
try {
fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(INPUT_FILE), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
bufferedReader.close();
fileReader.close();
SortReagentList();
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
WriteMakerReagentTableFile();
printWriter.close();
}
catch(FileNotFoundException ex) {
System.out.println("Unable to open file '" + fileName + "'");
}
catch(IOException ex) {
System.out.println("Error reading file '" + fileName + "'");
}
catch(Exception e) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open file '" + OUTPUT_FILE + "'");
} catch (IOException ex) {
System.out.println("Error reading file '" + OUTPUT_FILE + "'");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WriteMakerReagentTableData();
writeMakerReagentTableData();
}
}

View File

@@ -1,94 +1,74 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleskillbookchancefetcher;
import life.MapleLifeFactory;
import life.MapleMonsterStats;
import tools.DatabaseConnection;
import server.life.MapleMonsterStats;
import tools.Pair;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.*;
import java.util.Map.Entry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* @author RonanLana
*
* <p>
* This application traces missing meso drop data on the underlying DB (that must be
* defined on the DatabaseConnection file of this project) and generates a
* SQL file that proposes missing drop entries for the drop_data table.
*
* <p>
* The meso range is calculated accordingly with the target mob stats, such as level
* and if it's a boss or not, similarly as how it has been done for the actual meso
* drops.
*
*/
public class MapleSkillbookChanceFetcher {
public class SkillbookChanceFetcher {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("skillbook_drop_data.sql");
private static final Map<Pair<Integer, Integer>, Integer> skillbookChances = new HashMap<>();
private static PrintWriter printWriter;
private static String newFile = "lib/skillbook_drop_data.sql";
private static Map<Integer, MapleMonsterStats> mobStats;
private static Map<Pair<Integer, Integer>, Integer> skillbookChances = new HashMap<>();
private static List<Entry<Pair<Integer, Integer>, Integer>> sortedSkillbookChances() {
List<Entry<Pair<Integer, Integer>, Integer>> skillbookChancesList = new ArrayList<>(skillbookChances.entrySet());
Collections.sort(skillbookChancesList, (o1, o2) -> {
private static List<Map.Entry<Pair<Integer, Integer>, Integer>> sortedSkillbookChances() {
List<Map.Entry<Pair<Integer, Integer>, Integer>> skillbookChancesList = new ArrayList<>(skillbookChances.entrySet());
skillbookChancesList.sort((o1, o2) -> {
if (o1.getKey().getLeft().equals(o2.getKey().getLeft())) {
return o1.getKey().getRight() < o2.getKey().getRight() ? -1 : (o1.getKey().getRight().equals(o2.getKey().getRight()) ? 0 : 1);
}
return (o1.getKey().getLeft() < o2.getKey().getLeft()) ? -1 : 1;
});
return skillbookChancesList;
}
private static boolean isLegendSkillUpgradeBook(int itemid) {
int itemidBranch = itemid / 10000;
return (itemidBranch == 228 && itemid >= 2280013 || itemidBranch == 229 && itemid >= 2290126); // drop rate of Legends are higher
}
private static void fetchSkillbookDropChances() {
Connection con = DatabaseConnection.getConnection();
Connection con = SimpleDatabaseConnection.getConnection();
try {
PreparedStatement ps = con.prepareStatement("SELECT dropperid, itemid FROM drop_data WHERE itemid >= 2280000 AND itemid < 2300000");
ResultSet rs = ps.executeQuery();
while(rs.next()) {
while (rs.next()) {
int mobid = rs.getInt("dropperid");
int itemid = rs.getInt("itemid");
int expectedChance = 250;
if (mobStats.get(mobid) != null) {
int level = mobStats.get(mobid).getLevel();
expectedChance *= Math.max(2, (level - 80) / 15);
if (mobStats.get(mobid).isBoss()) {
expectedChance *= 20;
} else {
@@ -97,53 +77,52 @@ public class MapleSkillbookChanceFetcher {
} else {
expectedChance = 1287;
}
if (isLegendSkillUpgradeBook(itemid)) { // drop rate of Legends seems to be higher than explorers, in retrospect from values in DB
expectedChance *= 3;
}
skillbookChances.put(new Pair<>(mobid, itemid), expectedChance);
}
rs.close();
ps.close();
con.close();
} catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void printSkillbookChanceUpdateSqlHeader() {
printWriter.println(" # SQL File autogenerated from the MapleSkillbookChanceFetcher feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account mob stats such as level and boss for the chance rates.");
printWriter.println();
printWriter.println(" REPLACE INTO drop_data (`dropperid`, `itemid`, `minimum_quantity`, `maximum_quantity`, `questid`, `chance`) VALUES");
}
private static void generateSkillbookChanceUpdateFile() {
try {
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printSkillbookChanceUpdateSqlHeader();
List<Entry<Pair<Integer, Integer>, Integer>> skillbookChancesList = sortedSkillbookChances();
for (Entry<Pair<Integer, Integer>, Integer> e : skillbookChancesList) {
List<Map.Entry<Pair<Integer, Integer>, Integer>> skillbookChancesList = sortedSkillbookChances();
for (Map.Entry<Pair<Integer, Integer>, Integer> e : skillbookChancesList) {
printWriter.println("(" + e.getKey().getLeft() + ", " + e.getKey().getRight() + ", 1, 1, 0, " + e.getValue() + "),");
}
printWriter.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
public static void main(String[] args) {
// load mob stats from WZ
mobStats = MapleLifeFactory.getAllMonsterStats();
mobStats = MonsterStatFetcher.getAllMonsterStats();
fetchSkillbookDropChances();
generateSkillbookChanceUpdateFile();
}
}

View File

@@ -0,0 +1,157 @@
package tools.mapletools;
import provider.wz.WZFiles;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* @author RonanLana
* <p>
* This application parses skillbook XMLs, filling up stack amount of those
* items to 100 (eliminating limitations on held skillbooks, now using
* default stack quantity expected from USE items).
* <p>
* Estimated parse time: 10 seconds
*/
public class SkillbookStackUpdate {
private static final File INPUT_DIRECTORY = new File(WZFiles.ITEM.getFile(), "Consume");
private static final File OUTPUT_DIRECTORY = ToolConstants.getOutputFile("skillbook-update");
private static final int INITIAL_STRING_LENGTH = 50;
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static int status = 0;
private static boolean isSkillMasteryBook(int itemid) {
return itemid >= 2280000 && itemid < 2300000;
}
private static String getName(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("name");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
if (j < i) {
return "0"; //node value containing 'name' in it's scope, cheap fix since we don't deal with strings anyway
}
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
String d;
i = token.lastIndexOf("value");
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
}
printWriter.println(token);
}
private static void translateItemToken(String token) {
if (token.contains("/imgdir")) {
status -= 1;
} else if (token.contains("imgdir")) {
status += 1;
if (status == 2) { //itemid
int itemid = Integer.parseInt(getName(token));
if (!isSkillMasteryBook(itemid)) {
printWriter.println(token);
forwardCursor(status);
return;
}
}
} else {
if (status == 3) {
if (getName(token).contentEquals("slotMax")) {
printWriter.println(" <int name=\"slotMax\" value=\"100\"/>");
return;
}
}
}
printWriter.println(token);
}
private static void parseItemFile(File file, File outputFile) {
setupDirectories(outputFile);
// This will reference one line at a time
String line = null;
try {
printWriter = new PrintWriter(outputFile);
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
while ((line = bufferedReader.readLine()) != null) {
translateItemToken(line);
}
bufferedReader.close();
fileReader.close();
printWriter.close();
} catch (IOException ex) {
System.out.println("Error reading file '" + file.getName() + "'");
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void setupDirectories(File file) {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
}
private static void parseItemDirectory(File inputDirectory, File outputDirectory) {
for (File f : inputDirectory.listFiles()) {
parseItemFile(f, new File(outputDirectory, f.getName()));
}
}
public static void main(String[] args) {
System.out.println("Reading item files...");
parseItemDirectory(INPUT_DIRECTORY, OUTPUT_DIRECTORY);
System.out.println("Done!");
}
}

View File

@@ -0,0 +1,18 @@
package tools.mapletools;
import java.io.File;
class ToolConstants {
static final File INPUT_DIRECTORY = new File("tools/input");
static final File OUTPUT_DIRECTORY = new File("tools/output");
static final String SCRIPTS_PATH = "scripts";
static final String HANDBOOK_PATH = "handbook";
static File getInputFile(String fileName) {
return new File(INPUT_DIRECTORY, fileName);
}
static File getOutputFile(String fileName) {
return new File(OUTPUT_DIRECTORY, fileName);
}
}

View File

@@ -1,57 +1,32 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 RonanLana
package tools.mapletools;
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package mapleworldmapchecker;
import provider.wz.WZFiles;
import tools.Pair;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;
/**
*
* @author RonanLana
This application parses the Map.wz file inputted and reports areas (mapids) that are supposed to be referenced
throughout the map tree (area map -> continent map -> world map) but are currently missing.
* <p>
* This application parses the Map.wz file inputted and reports areas (mapids) that are supposed to be referenced
* throughout the map tree (area map -> continent map -> world map) but are currently missing.
*/
public class MapleWorldmapChecker {
static String newFile = "lib/Report.txt";
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
static String worldmapPath = "../../wz/Map.wz/WorldMap";
static int initialStringLength = 50;
static Map<String, Set<Integer>> worldMapids = new HashMap<>();
static Map<String, String> parentWorldmaps = new HashMap<>();
static Set<String> rootWorldmaps = new HashSet<>();
//static String rootWorldmap = "";
static Set<Integer> currentWorldMapids;
static String currentParent;
static byte status = 0;
static boolean isInfo;
public class WorldmapChecker {
private static final File OUTPUT_FILE = ToolConstants.getOutputFile("worldmap_report.txt");
private static final int INITIAL_STRING_LENGTH = 50;
private static final Map<String, Set<Integer>> worldMapids = new HashMap<>();
private static final Map<String, String> parentWorldmaps = new HashMap<>();
private static final Set<String> rootWorldmaps = new HashSet<>();
private static PrintWriter printWriter = null;
private static BufferedReader bufferedReader = null;
private static Set<Integer> currentWorldMapids;
private static String currentParent;
private static byte status = 0;
private static boolean isInfo;
private static String getName(String token) {
int i, j;
char[] dest;
@@ -61,13 +36,13 @@ public class MapleWorldmapChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static String getValue(String token) {
int i, j;
char[] dest;
@@ -77,68 +52,57 @@ public class MapleWorldmapChecker {
i = token.indexOf("\"", i) + 1; //lower bound of the string
j = token.indexOf("\"", i); //upper bound
dest = new char[initialStringLength];
dest = new char[INITIAL_STRING_LENGTH];
token.getChars(i, j, dest, 0);
d = new String(dest);
return(d.trim());
return (d.trim());
}
private static void forwardCursor(int st) {
String line = null;
try {
while(status >= st && (line = bufferedReader.readLine()) != null) {
while (status >= st && (line = bufferedReader.readLine()) != null) {
simpleToken(line);
}
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
private static void simpleToken(String token) {
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
}
}
private static void translateToken(String token) {
String d;
if(token.contains("/imgdir")) {
if (token.contains("/imgdir")) {
status -= 1;
}
else if(token.contains("imgdir")) {
} else if (token.contains("imgdir")) {
status += 1;
if (status == 2) {
d = getName(token);
switch (d) {
case "MapList":
isInfo = false;
break;
case "info":
isInfo = true;
break;
default:
forwardCursor(status);
case "MapList" -> isInfo = false;
case "info" -> isInfo = true;
default -> forwardCursor(status);
}
} else if (status == 4) {
d = getName(token);
if (!d.contentEquals("mapNo")) {
forwardCursor(status);
}
}
}
else {
} else {
if (status == 4) {
currentWorldMapids.add(Integer.valueOf(getValue(token)));
} else if (status == 2 && isInfo) {
@@ -151,64 +115,63 @@ public class MapleWorldmapChecker {
}
} catch (Exception e) {
System.out.println("failed '" + token + "'");
}
}
}
}
private static void parseWorldmapFile(File worldmapFile) throws IOException {
String line;
fileReader = new InputStreamReader(new FileInputStream(worldmapFile), "UTF-8");
InputStreamReader fileReader = new InputStreamReader(new FileInputStream(worldmapFile), StandardCharsets.UTF_8);
bufferedReader = new BufferedReader(fileReader);
currentParent = "";
status = 0;
currentWorldMapids = new HashSet<>();
while((line = bufferedReader.readLine()) != null) {
while ((line = bufferedReader.readLine()) != null) {
translateToken(line);
}
String worldmapName = worldmapFile.getName();
worldMapids.put(worldmapName, currentWorldMapids);
if (!currentParent.isEmpty()) parentWorldmaps.put(worldmapName, currentParent);
else rootWorldmaps.add(worldmapName);
if (!currentParent.isEmpty()) {
parentWorldmaps.put(worldmapName, currentParent);
} else {
rootWorldmaps.add(worldmapName);
}
bufferedReader.close();
fileReader.close();
}
private static void parseWorldmapDirectory() {
System.out.println("Parsing directory '" + worldmapPath + "'");
File folder = new File(worldmapPath);
File folder = new File(WZFiles.MAP.getFilePath(), "WorldMap");
System.out.println("Parsing directory '" + folder.getPath() + "'");
for (File file : folder.listFiles()) {
if (file.isFile()) {
try {
parseWorldmapFile(file);
}
catch(FileNotFoundException ex) {
} catch (FileNotFoundException ex) {
System.out.println("Unable to open worldmap file " + file.getAbsolutePath() + ".");
}
catch(IOException ex) {
} catch (IOException ex) {
System.out.println("Error reading worldmap file " + file.getAbsolutePath() + ".");
}
catch(Exception e) {
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static void printReportFileHeader() {
printWriter.println(" # Report File autogenerated from the MapleWorldmapChecker feature by Ronan Lana.");
printWriter.println(" # Generated data takes into account several data info from the server-side WZ.xmls.");
printWriter.println();
}
private static void printReportFileResults(List<Pair<String, List<Pair<Integer, String>>>> results) {
printWriter.println("Missing mapid references in top hierarchy:\n");
for (Pair<String, List<Pair<Integer, String>>> res : results) {
@@ -221,32 +184,32 @@ public class MapleWorldmapChecker {
printWriter.println("\n");
}
}
private static void verifyWorldmapTreeMapids() {
try {
printWriter = new PrintWriter(newFile, "UTF-8");
printWriter = new PrintWriter(OUTPUT_FILE, StandardCharsets.UTF_8);
printReportFileHeader();
if (rootWorldmaps.size() > 1) {
printWriter.println("[WARNING] Detected several root worldmaps: " + rootWorldmaps + "\n");
}
Set<String> worldmaps = new HashSet<>(parentWorldmaps.keySet());
worldmaps.addAll(rootWorldmaps);
Map<String, Set<Integer>> tempMapids = new HashMap<>(worldMapids.size());
for (Entry<String, Set<Integer>> e : worldMapids.entrySet()) {
for (Map.Entry<String, Set<Integer>> e : worldMapids.entrySet()) {
tempMapids.put(e.getKey(), new HashSet<>(e.getValue()));
}
Map<String, List<Pair<Integer, String>>> unreferencedMapids = new HashMap<>();
for (String s : worldmaps) {
List<Pair<Integer, String>> currentUnreferencedMapids = new ArrayList<>();
for (Integer i : tempMapids.get(s)) {
String parent = parentWorldmaps.get(s);
while (parent != null) {
Set<Integer> mapids = worldMapids.get(parent);
if (!mapids.contains(i)) {
@@ -255,39 +218,39 @@ public class MapleWorldmapChecker {
} else {
tempMapids.get(parent).remove(i);
}
parent = parentWorldmaps.get(parent);
}
}
if (!currentUnreferencedMapids.isEmpty()) {
unreferencedMapids.put(s, currentUnreferencedMapids);
}
}
if (!unreferencedMapids.isEmpty()) {
List<Pair<String, List<Pair<Integer, String>>>> unreferencedEntries = new ArrayList<>(20);
for (Entry<String, List<Pair<Integer, String>>> e : unreferencedMapids.entrySet()) {
for (Map.Entry<String, List<Pair<Integer, String>>> e : unreferencedMapids.entrySet()) {
List<Pair<Integer, String>> list = new ArrayList<>(e.getValue());
Collections.sort(list, (o1, o2) -> o1.getLeft().compareTo(o2.getLeft()));
list.sort((o1, o2) -> o1.getLeft().compareTo(o2.getLeft()));
unreferencedEntries.add(new Pair<>(e.getKey(), list));
}
Collections.sort(unreferencedEntries, (o1, o2) -> o1.getLeft().compareTo(o2.getLeft()));
unreferencedEntries.sort((o1, o2) -> o1.getLeft().compareTo(o2.getLeft()));
printReportFileResults(unreferencedEntries);
}
printWriter.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
parseWorldmapDirectory();
verifyWorldmapTreeMapids();
}
}

View File

@@ -1,766 +0,0 @@
# SQL File autogenerated from the MapleArrowFetcher feature by Ronan Lana.
# Generated data takes into account mob stats such as level and boss for the raw arrow ranges.
# Only current arrows entries on the DB it was compiled are being updated here.
UPDATE drop_data
SET minimum_quantity = CASE
WHEN dropperid = 100100 AND itemid = 2060000 THEN 1
WHEN dropperid = 100100 AND itemid = 2061000 THEN 1
WHEN dropperid = 100101 AND itemid = 2060000 THEN 2
WHEN dropperid = 100101 AND itemid = 2061000 THEN 2
WHEN dropperid = 100120 AND itemid = 2060000 THEN 1
WHEN dropperid = 100120 AND itemid = 2061000 THEN 1
WHEN dropperid = 100121 AND itemid = 2060000 THEN 4
WHEN dropperid = 100123 AND itemid = 2061000 THEN 9
WHEN dropperid = 100124 AND itemid = 2060000 THEN 11
WHEN dropperid = 100124 AND itemid = 2061000 THEN 11
WHEN dropperid = 120100 AND itemid = 2060000 THEN 2
WHEN dropperid = 120100 AND itemid = 2061000 THEN 2
WHEN dropperid = 130100 AND itemid = 2060000 THEN 5
WHEN dropperid = 130100 AND itemid = 2061000 THEN 5
WHEN dropperid = 130101 AND itemid = 2060000 THEN 5
WHEN dropperid = 130101 AND itemid = 2061000 THEN 5
WHEN dropperid = 210100 AND itemid = 2060000 THEN 7
WHEN dropperid = 210100 AND itemid = 2061000 THEN 7
WHEN dropperid = 1110100 AND itemid = 2060000 THEN 19
WHEN dropperid = 1110100 AND itemid = 2061000 THEN 19
WHEN dropperid = 1110101 AND itemid = 2060000 THEN 13
WHEN dropperid = 1110101 AND itemid = 2061000 THEN 13
WHEN dropperid = 1110130 AND itemid = 2060000 THEN 19
WHEN dropperid = 1110130 AND itemid = 2061000 THEN 19
WHEN dropperid = 1120100 AND itemid = 2060000 THEN 15
WHEN dropperid = 1120100 AND itemid = 2061000 THEN 15
WHEN dropperid = 1130100 AND itemid = 2060000 THEN 22
WHEN dropperid = 1130100 AND itemid = 2061000 THEN 22
WHEN dropperid = 1140100 AND itemid = 2060000 THEN 24
WHEN dropperid = 1140100 AND itemid = 2061000 THEN 24
WHEN dropperid = 1140130 AND itemid = 2060000 THEN 24
WHEN dropperid = 1140130 AND itemid = 2061000 THEN 24
WHEN dropperid = 1210100 AND itemid = 2060000 THEN 9
WHEN dropperid = 1210100 AND itemid = 2061000 THEN 9
WHEN dropperid = 1210101 AND itemid = 2060000 THEN 13
WHEN dropperid = 1210101 AND itemid = 2061000 THEN 13
WHEN dropperid = 1210102 AND itemid = 2060000 THEN 10
WHEN dropperid = 1210102 AND itemid = 2061000 THEN 10
WHEN dropperid = 1210103 AND itemid = 2060000 THEN 19
WHEN dropperid = 1210103 AND itemid = 2061000 THEN 19
WHEN dropperid = 2100100 AND itemid = 2060000 THEN 26
WHEN dropperid = 2100100 AND itemid = 2061000 THEN 26
WHEN dropperid = 2100101 AND itemid = 2060000 THEN 27
WHEN dropperid = 2100101 AND itemid = 2061000 THEN 27
WHEN dropperid = 2100102 AND itemid = 2060000 THEN 28
WHEN dropperid = 2100102 AND itemid = 2061000 THEN 28
WHEN dropperid = 2100103 AND itemid = 2060000 THEN 32
WHEN dropperid = 2100103 AND itemid = 2061000 THEN 32
WHEN dropperid = 2100104 AND itemid = 2060000 THEN 36
WHEN dropperid = 2100104 AND itemid = 2061000 THEN 36
WHEN dropperid = 2100105 AND itemid = 2060000 THEN 30
WHEN dropperid = 2100105 AND itemid = 2061000 THEN 30
WHEN dropperid = 2100106 AND itemid = 2060000 THEN 31
WHEN dropperid = 2100106 AND itemid = 2061000 THEN 31
WHEN dropperid = 2100107 AND itemid = 2060000 THEN 35
WHEN dropperid = 2100107 AND itemid = 2061000 THEN 35
WHEN dropperid = 2100108 AND itemid = 2060000 THEN 37
WHEN dropperid = 2100108 AND itemid = 2061000 THEN 37
WHEN dropperid = 2110200 AND itemid = 2060000 THEN 28
WHEN dropperid = 2110200 AND itemid = 2061000 THEN 28
WHEN dropperid = 2110300 AND itemid = 2060000 THEN 31
WHEN dropperid = 2110300 AND itemid = 2061000 THEN 31
WHEN dropperid = 2110301 AND itemid = 2060000 THEN 37
WHEN dropperid = 2110301 AND itemid = 2061000 THEN 37
WHEN dropperid = 2130100 AND itemid = 2060000 THEN 28
WHEN dropperid = 2130100 AND itemid = 2061000 THEN 28
WHEN dropperid = 2220000 AND itemid = 2060000 THEN 36
WHEN dropperid = 2220000 AND itemid = 2061000 THEN 36
WHEN dropperid = 2220100 AND itemid = 2060000 THEN 26
WHEN dropperid = 2220100 AND itemid = 2061000 THEN 26
WHEN dropperid = 2230100 AND itemid = 2060000 THEN 35
WHEN dropperid = 2230100 AND itemid = 2061000 THEN 35
WHEN dropperid = 2230101 AND itemid = 2060000 THEN 31
WHEN dropperid = 2230101 AND itemid = 2061000 THEN 31
WHEN dropperid = 2230102 AND itemid = 2060000 THEN 32
WHEN dropperid = 2230102 AND itemid = 2061000 THEN 32
WHEN dropperid = 2230103 AND itemid = 2060000 THEN 30
WHEN dropperid = 2230103 AND itemid = 2061000 THEN 30
WHEN dropperid = 2230104 AND itemid = 2060000 THEN 36
WHEN dropperid = 2230104 AND itemid = 2061000 THEN 36
WHEN dropperid = 2230105 AND itemid = 2060000 THEN 30
WHEN dropperid = 2230105 AND itemid = 2061000 THEN 30
WHEN dropperid = 2230106 AND itemid = 2060000 THEN 32
WHEN dropperid = 2230106 AND itemid = 2061000 THEN 32
WHEN dropperid = 2230107 AND itemid = 2060000 THEN 31
WHEN dropperid = 2230107 AND itemid = 2061000 THEN 31
WHEN dropperid = 2230108 AND itemid = 2060000 THEN 28
WHEN dropperid = 2230108 AND itemid = 2061000 THEN 28
WHEN dropperid = 2230109 AND itemid = 2060000 THEN 36
WHEN dropperid = 2230109 AND itemid = 2061000 THEN 36
WHEN dropperid = 2230110 AND itemid = 2060000 THEN 30
WHEN dropperid = 2230110 AND itemid = 2061000 THEN 30
WHEN dropperid = 2230111 AND itemid = 2060000 THEN 31
WHEN dropperid = 2230111 AND itemid = 2061000 THEN 31
WHEN dropperid = 2230131 AND itemid = 2060000 THEN 31
WHEN dropperid = 2230131 AND itemid = 2061000 THEN 31
WHEN dropperid = 2230200 AND itemid = 2060000 THEN 37
WHEN dropperid = 2230200 AND itemid = 2061000 THEN 37
WHEN dropperid = 2300100 AND itemid = 2060000 THEN 26
WHEN dropperid = 2300100 AND itemid = 2061000 THEN 26
WHEN dropperid = 3000000 AND itemid = 2060000 THEN 39
WHEN dropperid = 3000000 AND itemid = 2061000 THEN 39
WHEN dropperid = 3000005 AND itemid = 2060000 THEN 39
WHEN dropperid = 3000005 AND itemid = 2061000 THEN 39
WHEN dropperid = 3000006 AND itemid = 2060000 THEN 39
WHEN dropperid = 3000006 AND itemid = 2061000 THEN 39
WHEN dropperid = 3100101 AND itemid = 2060000 THEN 41
WHEN dropperid = 3100101 AND itemid = 2061000 THEN 41
WHEN dropperid = 3100102 AND itemid = 2060000 THEN 39
WHEN dropperid = 3100102 AND itemid = 2061000 THEN 39
WHEN dropperid = 3110101 AND itemid = 2060000 THEN 41
WHEN dropperid = 3110101 AND itemid = 2061000 THEN 41
WHEN dropperid = 3110102 AND itemid = 2060000 THEN 41
WHEN dropperid = 3110102 AND itemid = 2061000 THEN 41
WHEN dropperid = 3110300 AND itemid = 2060000 THEN 41
WHEN dropperid = 3110300 AND itemid = 2061000 THEN 41
WHEN dropperid = 3110301 AND itemid = 2060000 THEN 41
WHEN dropperid = 3110301 AND itemid = 2061000 THEN 41
WHEN dropperid = 3110302 AND itemid = 2060000 THEN 45
WHEN dropperid = 3110302 AND itemid = 2061000 THEN 45
WHEN dropperid = 3110303 AND itemid = 2060000 THEN 49
WHEN dropperid = 3110303 AND itemid = 2061000 THEN 49
WHEN dropperid = 3210100 AND itemid = 2060005 THEN 6
WHEN dropperid = 3210203 AND itemid = 2060000 THEN 47
WHEN dropperid = 3210203 AND itemid = 2061000 THEN 47
WHEN dropperid = 3210204 AND itemid = 2060000 THEN 44
WHEN dropperid = 3210204 AND itemid = 2061000 THEN 44
WHEN dropperid = 3210205 AND itemid = 2060000 THEN 44
WHEN dropperid = 3210205 AND itemid = 2061000 THEN 44
WHEN dropperid = 3210206 AND itemid = 2060000 THEN 47
WHEN dropperid = 3210206 AND itemid = 2061000 THEN 47
WHEN dropperid = 3210207 AND itemid = 2060000 THEN 44
WHEN dropperid = 3210207 AND itemid = 2060005 THEN 7
WHEN dropperid = 3210207 AND itemid = 2061000 THEN 44
WHEN dropperid = 3210208 AND itemid = 2060000 THEN 47
WHEN dropperid = 3210208 AND itemid = 2061000 THEN 47
WHEN dropperid = 3210450 AND itemid = 2060000 THEN 47
WHEN dropperid = 3210450 AND itemid = 2061000 THEN 47
WHEN dropperid = 3210800 AND itemid = 2060005 THEN 8
WHEN dropperid = 3220000 AND itemid = 2060000 THEN 63
WHEN dropperid = 3220000 AND itemid = 2061000 THEN 63
WHEN dropperid = 3230100 AND itemid = 2060000 THEN 45
WHEN dropperid = 3230100 AND itemid = 2061000 THEN 45
WHEN dropperid = 3230103 AND itemid = 2060000 THEN 49
WHEN dropperid = 3230103 AND itemid = 2061000 THEN 49
WHEN dropperid = 3230200 AND itemid = 2060000 THEN 45
WHEN dropperid = 3230200 AND itemid = 2061000 THEN 45
WHEN dropperid = 3230302 AND itemid = 2060000 THEN 45
WHEN dropperid = 3230302 AND itemid = 2061000 THEN 45
WHEN dropperid = 3230303 AND itemid = 2060000 THEN 48
WHEN dropperid = 3230303 AND itemid = 2061000 THEN 48
WHEN dropperid = 3230304 AND itemid = 2060000 THEN 49
WHEN dropperid = 3230304 AND itemid = 2061000 THEN 49
WHEN dropperid = 3230305 AND itemid = 2060000 THEN 51
WHEN dropperid = 3230305 AND itemid = 2061000 THEN 51
WHEN dropperid = 3230306 AND itemid = 2060000 THEN 48
WHEN dropperid = 3230306 AND itemid = 2061000 THEN 48
WHEN dropperid = 3230307 AND itemid = 2060000 THEN 40
WHEN dropperid = 3230307 AND itemid = 2061000 THEN 40
WHEN dropperid = 3230308 AND itemid = 2060000 THEN 51
WHEN dropperid = 3230308 AND itemid = 2061000 THEN 51
WHEN dropperid = 3230400 AND itemid = 2060000 THEN 39
WHEN dropperid = 3230400 AND itemid = 2061000 THEN 39
WHEN dropperid = 3230405 AND itemid = 2060000 THEN 49
WHEN dropperid = 3230405 AND itemid = 2061000 THEN 49
WHEN dropperid = 3300000 AND itemid = 2060000 THEN 39
WHEN dropperid = 3300000 AND itemid = 2061000 THEN 39
WHEN dropperid = 3300001 AND itemid = 2060000 THEN 39
WHEN dropperid = 3300001 AND itemid = 2061000 THEN 39
WHEN dropperid = 3300002 AND itemid = 2060000 THEN 40
WHEN dropperid = 3300002 AND itemid = 2061000 THEN 40
WHEN dropperid = 3300003 AND itemid = 2060000 THEN 41
WHEN dropperid = 3300003 AND itemid = 2061000 THEN 41
WHEN dropperid = 3300004 AND itemid = 2060000 THEN 43
WHEN dropperid = 3300004 AND itemid = 2061000 THEN 43
WHEN dropperid = 3300006 AND itemid = 2060000 THEN 45
WHEN dropperid = 3300006 AND itemid = 2061000 THEN 45
WHEN dropperid = 3300007 AND itemid = 2060000 THEN 45
WHEN dropperid = 3300007 AND itemid = 2061000 THEN 45
WHEN dropperid = 3300008 AND itemid = 2060000 THEN 69
WHEN dropperid = 3300008 AND itemid = 2061000 THEN 69
WHEN dropperid = 4110300 AND itemid = 2060000 THEN 54
WHEN dropperid = 4110300 AND itemid = 2061000 THEN 54
WHEN dropperid = 4110301 AND itemid = 2060000 THEN 58
WHEN dropperid = 4110301 AND itemid = 2061000 THEN 58
WHEN dropperid = 4130103 AND itemid = 2060000 THEN 85
WHEN dropperid = 4130103 AND itemid = 2061000 THEN 85
WHEN dropperid = 4230103 AND itemid = 2060000 THEN 54
WHEN dropperid = 4230103 AND itemid = 2061000 THEN 54
WHEN dropperid = 4230106 AND itemid = 2060000 THEN 58
WHEN dropperid = 4230106 AND itemid = 2061000 THEN 58
WHEN dropperid = 4230108 AND itemid = 2060000 THEN 56
WHEN dropperid = 4230108 AND itemid = 2061000 THEN 56
WHEN dropperid = 4230109 AND itemid = 2060000 THEN 54
WHEN dropperid = 4230109 AND itemid = 2061000 THEN 54
WHEN dropperid = 4230110 AND itemid = 2060000 THEN 58
WHEN dropperid = 4230110 AND itemid = 2061000 THEN 58
WHEN dropperid = 4230111 AND itemid = 2060000 THEN 53
WHEN dropperid = 4230111 AND itemid = 2061000 THEN 53
WHEN dropperid = 4230112 AND itemid = 2060000 THEN 57
WHEN dropperid = 4230112 AND itemid = 2061000 THEN 57
WHEN dropperid = 4230113 AND itemid = 2060000 THEN 52
WHEN dropperid = 4230113 AND itemid = 2061000 THEN 52
WHEN dropperid = 4230114 AND itemid = 2060000 THEN 53
WHEN dropperid = 4230114 AND itemid = 2061000 THEN 53
WHEN dropperid = 4230115 AND itemid = 2060000 THEN 60
WHEN dropperid = 4230115 AND itemid = 2061000 THEN 60
WHEN dropperid = 4230116 AND itemid = 2060000 THEN 52
WHEN dropperid = 4230116 AND itemid = 2061000 THEN 52
WHEN dropperid = 4230117 AND itemid = 2060000 THEN 54
WHEN dropperid = 4230117 AND itemid = 2061000 THEN 54
WHEN dropperid = 4230118 AND itemid = 2060000 THEN 58
WHEN dropperid = 4230118 AND itemid = 2061000 THEN 58
WHEN dropperid = 4230119 AND itemid = 2060000 THEN 53
WHEN dropperid = 4230119 AND itemid = 2061000 THEN 53
WHEN dropperid = 4230120 AND itemid = 2060000 THEN 57
WHEN dropperid = 4230120 AND itemid = 2061000 THEN 57
WHEN dropperid = 4230121 AND itemid = 2060000 THEN 60
WHEN dropperid = 4230121 AND itemid = 2061000 THEN 60
WHEN dropperid = 4230123 AND itemid = 2060000 THEN 56
WHEN dropperid = 4230123 AND itemid = 2061000 THEN 56
WHEN dropperid = 4230124 AND itemid = 2060000 THEN 54
WHEN dropperid = 4230124 AND itemid = 2061000 THEN 54
WHEN dropperid = 4230125 AND itemid = 2060000 THEN 57
WHEN dropperid = 4230125 AND itemid = 2061000 THEN 57
WHEN dropperid = 4230126 AND itemid = 2060000 THEN 61
WHEN dropperid = 4230126 AND itemid = 2061000 THEN 61
WHEN dropperid = 4230201 AND itemid = 2060000 THEN 52
WHEN dropperid = 4230201 AND itemid = 2061000 THEN 52
WHEN dropperid = 4230300 AND itemid = 2060000 THEN 58
WHEN dropperid = 4230300 AND itemid = 2061000 THEN 58
WHEN dropperid = 4230400 AND itemid = 2060000 THEN 58
WHEN dropperid = 4230400 AND itemid = 2061000 THEN 58
WHEN dropperid = 4230500 AND itemid = 2060000 THEN 52
WHEN dropperid = 4230500 AND itemid = 2061000 THEN 52
WHEN dropperid = 4230501 AND itemid = 2060000 THEN 53
WHEN dropperid = 4230501 AND itemid = 2061000 THEN 53
WHEN dropperid = 4230502 AND itemid = 2060000 THEN 56
WHEN dropperid = 4230502 AND itemid = 2061000 THEN 56
WHEN dropperid = 4230503 AND itemid = 2060000 THEN 58
WHEN dropperid = 4230503 AND itemid = 2061000 THEN 58
WHEN dropperid = 4230504 AND itemid = 2060000 THEN 58
WHEN dropperid = 4230504 AND itemid = 2061000 THEN 58
WHEN dropperid = 4230600 AND itemid = 2060000 THEN 52
WHEN dropperid = 4230600 AND itemid = 2061000 THEN 52
WHEN dropperid = 4240000 AND itemid = 2060000 THEN 64
WHEN dropperid = 4240000 AND itemid = 2061000 THEN 64
WHEN dropperid = 5120100 AND itemid = 2060000 THEN 98
WHEN dropperid = 5120100 AND itemid = 2061000 THEN 98
WHEN dropperid = 5130104 AND itemid = 2060000 THEN 71
WHEN dropperid = 5130104 AND itemid = 2061000 THEN 71
WHEN dropperid = 5140000 AND itemid = 2060000 THEN 75
WHEN dropperid = 5140000 AND itemid = 2061000 THEN 75
WHEN dropperid = 5150000 AND itemid = 2060000 THEN 77
WHEN dropperid = 5150000 AND itemid = 2061000 THEN 77
WHEN dropperid = 5200000 AND itemid = 2060001 THEN 15
WHEN dropperid = 5200000 AND itemid = 2061001 THEN 15
WHEN dropperid = 5200001 AND itemid = 2060001 THEN 19
WHEN dropperid = 5200001 AND itemid = 2061001 THEN 19
WHEN dropperid = 5200002 AND itemid = 2060001 THEN 19
WHEN dropperid = 5200002 AND itemid = 2061001 THEN 19
WHEN dropperid = 5220000 AND itemid = 2060000 THEN 100
WHEN dropperid = 5220000 AND itemid = 2061000 THEN 100
WHEN dropperid = 5220002 AND itemid = 2060000 THEN 91
WHEN dropperid = 5220002 AND itemid = 2061000 THEN 91
WHEN dropperid = 5220003 AND itemid = 2060000 THEN 107
WHEN dropperid = 5220003 AND itemid = 2061000 THEN 107
WHEN dropperid = 5400000 AND itemid = 2060001 THEN 22
WHEN dropperid = 5400000 AND itemid = 2061001 THEN 22
WHEN dropperid = 6220000 AND itemid = 2060000 THEN 118
WHEN dropperid = 6220000 AND itemid = 2061000 THEN 118
WHEN dropperid = 7120103 AND itemid = 2060003 THEN 22
WHEN dropperid = 7220000 AND itemid = 2060001 THEN 64
WHEN dropperid = 7220000 AND itemid = 2061001 THEN 64
WHEN dropperid = 7220001 AND itemid = 2060001 THEN 63
WHEN dropperid = 7220001 AND itemid = 2061001 THEN 63
WHEN dropperid = 7220002 AND itemid = 2060001 THEN 70
WHEN dropperid = 7220002 AND itemid = 2061001 THEN 70
WHEN dropperid = 8220000 AND itemid = 2060001 THEN 75
WHEN dropperid = 8220000 AND itemid = 2061001 THEN 75
WHEN dropperid = 8220001 AND itemid = 2060001 THEN 82
WHEN dropperid = 8220001 AND itemid = 2061001 THEN 82
WHEN dropperid = 9300011 AND itemid = 2060000 THEN 51
WHEN dropperid = 9300011 AND itemid = 2061000 THEN 51
WHEN dropperid = 9300060 AND itemid = 2060000 THEN 54
WHEN dropperid = 9300060 AND itemid = 2061000 THEN 54
WHEN dropperid = 9300131 AND itemid = 2060000 THEN 51
WHEN dropperid = 9300131 AND itemid = 2061000 THEN 51
WHEN dropperid = 9300132 AND itemid = 2060000 THEN 39
WHEN dropperid = 9300132 AND itemid = 2061000 THEN 39
WHEN dropperid = 9300133 AND itemid = 2061000 THEN 39
WHEN dropperid = 9300160 AND itemid = 2060000 THEN 82
WHEN dropperid = 9300160 AND itemid = 2061000 THEN 82
WHEN dropperid = 9300161 AND itemid = 2060000 THEN 82
WHEN dropperid = 9300161 AND itemid = 2061000 THEN 82
WHEN dropperid = 9300274 AND itemid = 2060000 THEN 10
WHEN dropperid = 9300274 AND itemid = 2061000 THEN 10
WHEN dropperid = 9300332 AND itemid = 2060000 THEN 52
WHEN dropperid = 9300334 AND itemid = 2060000 THEN 64
WHEN dropperid = 9300341 AND itemid = 2060000 THEN 7
WHEN dropperid = 9300341 AND itemid = 2061000 THEN 7
WHEN dropperid = 9300342 AND itemid = 2060000 THEN 10
WHEN dropperid = 9300342 AND itemid = 2061000 THEN 10
WHEN dropperid = 9300343 AND itemid = 2060000 THEN 9
WHEN dropperid = 9300343 AND itemid = 2061000 THEN 9
WHEN dropperid = 9303005 AND itemid = 2060001 THEN 17
WHEN dropperid = 9303005 AND itemid = 2061001 THEN 17
WHEN dropperid = 9303008 AND itemid = 2060001 THEN 17
WHEN dropperid = 9303008 AND itemid = 2061001 THEN 17
WHEN dropperid = 9303009 AND itemid = 2060001 THEN 33
WHEN dropperid = 9303009 AND itemid = 2061001 THEN 33
WHEN dropperid = 9400000 AND itemid = 2060001 THEN 16
WHEN dropperid = 9400009 AND itemid = 2060001 THEN 130
WHEN dropperid = 9400011 AND itemid = 2060002 THEN 17
WHEN dropperid = 9400100 AND itemid = 2060003 THEN 14
WHEN dropperid = 9400101 AND itemid = 2061003 THEN 15
WHEN dropperid = 9400204 AND itemid = 2060003 THEN 17
WHEN dropperid = 9400239 AND itemid = 2060000 THEN 31
WHEN dropperid = 9400239 AND itemid = 2061000 THEN 31
WHEN dropperid = 9400244 AND itemid = 2060000 THEN 77
WHEN dropperid = 9400244 AND itemid = 2061000 THEN 77
WHEN dropperid = 9400248 AND itemid = 2060000 THEN 31
WHEN dropperid = 9400248 AND itemid = 2061000 THEN 31
WHEN dropperid = 9400540 AND itemid = 2060004 THEN 6
WHEN dropperid = 9400540 AND itemid = 2061004 THEN 6
WHEN dropperid = 9400541 AND itemid = 2060004 THEN 6
WHEN dropperid = 9400541 AND itemid = 2061004 THEN 6
WHEN dropperid = 9400542 AND itemid = 2060004 THEN 9
WHEN dropperid = 9400542 AND itemid = 2061004 THEN 9
WHEN dropperid = 9400543 AND itemid = 2060004 THEN 10
WHEN dropperid = 9400543 AND itemid = 2061004 THEN 10
WHEN dropperid = 9400547 AND itemid = 2060000 THEN 35
WHEN dropperid = 9400547 AND itemid = 2061000 THEN 35
WHEN dropperid = 9400548 AND itemid = 2060000 THEN 39
WHEN dropperid = 9400548 AND itemid = 2061000 THEN 39
WHEN dropperid = 9400550 AND itemid = 2060000 THEN 35
WHEN dropperid = 9400550 AND itemid = 2061000 THEN 35
WHEN dropperid = 9400558 AND itemid = 2060000 THEN 39
WHEN dropperid = 9400558 AND itemid = 2061000 THEN 39
WHEN dropperid = 9400563 AND itemid = 2060000 THEN 52
WHEN dropperid = 9400563 AND itemid = 2061000 THEN 52
WHEN dropperid = 9400638 AND itemid = 2060000 THEN 26
WHEN dropperid = 9400638 AND itemid = 2061000 THEN 26
WHEN dropperid = 9420500 AND itemid = 2060000 THEN 36
WHEN dropperid = 9420500 AND itemid = 2061000 THEN 36
WHEN dropperid = 9420502 AND itemid = 2060000 THEN 23
WHEN dropperid = 9420502 AND itemid = 2061000 THEN 23
WHEN dropperid = 9420506 AND itemid = 2060000 THEN 30
WHEN dropperid = 9420506 AND itemid = 2061000 THEN 30
WHEN dropperid = 9420508 AND itemid = 2060000 THEN 56
WHEN dropperid = 9420508 AND itemid = 2061000 THEN 56
WHEN dropperid = 9420527 AND itemid = 2060001 THEN 29
WHEN dropperid = 9420527 AND itemid = 2061001 THEN 29
WHEN dropperid = 9420531 AND itemid = 2060001 THEN 38
WHEN dropperid = 9420531 AND itemid = 2061001 THEN 38
WHEN dropperid = 9500112 AND itemid = 2060001 THEN 22
WHEN dropperid = 9500112 AND itemid = 2061001 THEN 22
WHEN dropperid = 9500119 AND itemid = 2060000 THEN 51
WHEN dropperid = 9500119 AND itemid = 2061000 THEN 51
WHEN dropperid = 9500120 AND itemid = 2060000 THEN 58
WHEN dropperid = 9500120 AND itemid = 2061000 THEN 58
WHEN dropperid = 9500122 AND itemid = 2060000 THEN 64
WHEN dropperid = 9500123 AND itemid = 2060000 THEN 77
WHEN dropperid = 9500123 AND itemid = 2061000 THEN 77
WHEN dropperid = 9500308 AND itemid = 2060000 THEN 91
WHEN dropperid = 9500308 AND itemid = 2061000 THEN 91
WHEN dropperid = 9500310 AND itemid = 2060000 THEN 107
WHEN dropperid = 9500310 AND itemid = 2061000 THEN 107
WHEN dropperid = 9500312 AND itemid = 2060001 THEN 63
WHEN dropperid = 9500312 AND itemid = 2061001 THEN 63
WHEN dropperid = 9500313 AND itemid = 2060001 THEN 64
WHEN dropperid = 9500313 AND itemid = 2061001 THEN 64
WHEN dropperid = 9500314 AND itemid = 2060001 THEN 70
WHEN dropperid = 9500314 AND itemid = 2061001 THEN 70
WHEN dropperid = 9500321 AND itemid = 2060001 THEN 6
WHEN dropperid = 9500321 AND itemid = 2061001 THEN 6
WHEN dropperid = 9500366 AND itemid = 2060000 THEN 19
WHEN dropperid = 9500369 AND itemid = 2060000 THEN 19
ELSE minimum_quantity END,
maximum_quantity = CASE
WHEN dropperid = 100100 AND itemid = 2060000 THEN 2
WHEN dropperid = 100100 AND itemid = 2061000 THEN 2
WHEN dropperid = 100101 AND itemid = 2060000 THEN 3
WHEN dropperid = 100101 AND itemid = 2061000 THEN 3
WHEN dropperid = 100120 AND itemid = 2060000 THEN 2
WHEN dropperid = 100120 AND itemid = 2061000 THEN 2
WHEN dropperid = 100121 AND itemid = 2060000 THEN 5
WHEN dropperid = 100123 AND itemid = 2061000 THEN 12
WHEN dropperid = 100124 AND itemid = 2060000 THEN 14
WHEN dropperid = 100124 AND itemid = 2061000 THEN 14
WHEN dropperid = 120100 AND itemid = 2060000 THEN 3
WHEN dropperid = 120100 AND itemid = 2061000 THEN 3
WHEN dropperid = 130100 AND itemid = 2060000 THEN 7
WHEN dropperid = 130100 AND itemid = 2061000 THEN 7
WHEN dropperid = 130101 AND itemid = 2060000 THEN 7
WHEN dropperid = 130101 AND itemid = 2061000 THEN 7
WHEN dropperid = 210100 AND itemid = 2060000 THEN 9
WHEN dropperid = 210100 AND itemid = 2061000 THEN 9
WHEN dropperid = 1110100 AND itemid = 2060000 THEN 24
WHEN dropperid = 1110100 AND itemid = 2061000 THEN 24
WHEN dropperid = 1110101 AND itemid = 2060000 THEN 17
WHEN dropperid = 1110101 AND itemid = 2061000 THEN 17
WHEN dropperid = 1110130 AND itemid = 2060000 THEN 24
WHEN dropperid = 1110130 AND itemid = 2061000 THEN 24
WHEN dropperid = 1120100 AND itemid = 2060000 THEN 19
WHEN dropperid = 1120100 AND itemid = 2061000 THEN 19
WHEN dropperid = 1130100 AND itemid = 2060000 THEN 28
WHEN dropperid = 1130100 AND itemid = 2061000 THEN 28
WHEN dropperid = 1140100 AND itemid = 2060000 THEN 30
WHEN dropperid = 1140100 AND itemid = 2061000 THEN 30
WHEN dropperid = 1140130 AND itemid = 2060000 THEN 30
WHEN dropperid = 1140130 AND itemid = 2061000 THEN 30
WHEN dropperid = 1210100 AND itemid = 2060000 THEN 12
WHEN dropperid = 1210100 AND itemid = 2061000 THEN 12
WHEN dropperid = 1210101 AND itemid = 2060000 THEN 17
WHEN dropperid = 1210101 AND itemid = 2061000 THEN 17
WHEN dropperid = 1210102 AND itemid = 2060000 THEN 13
WHEN dropperid = 1210102 AND itemid = 2061000 THEN 13
WHEN dropperid = 1210103 AND itemid = 2060000 THEN 24
WHEN dropperid = 1210103 AND itemid = 2061000 THEN 24
WHEN dropperid = 2100100 AND itemid = 2060000 THEN 33
WHEN dropperid = 2100100 AND itemid = 2061000 THEN 33
WHEN dropperid = 2100101 AND itemid = 2060000 THEN 34
WHEN dropperid = 2100101 AND itemid = 2061000 THEN 34
WHEN dropperid = 2100102 AND itemid = 2060000 THEN 35
WHEN dropperid = 2100102 AND itemid = 2061000 THEN 35
WHEN dropperid = 2100103 AND itemid = 2060000 THEN 40
WHEN dropperid = 2100103 AND itemid = 2061000 THEN 40
WHEN dropperid = 2100104 AND itemid = 2060000 THEN 45
WHEN dropperid = 2100104 AND itemid = 2061000 THEN 45
WHEN dropperid = 2100105 AND itemid = 2060000 THEN 38
WHEN dropperid = 2100105 AND itemid = 2061000 THEN 38
WHEN dropperid = 2100106 AND itemid = 2060000 THEN 39
WHEN dropperid = 2100106 AND itemid = 2061000 THEN 39
WHEN dropperid = 2100107 AND itemid = 2060000 THEN 44
WHEN dropperid = 2100107 AND itemid = 2061000 THEN 44
WHEN dropperid = 2100108 AND itemid = 2060000 THEN 47
WHEN dropperid = 2100108 AND itemid = 2061000 THEN 47
WHEN dropperid = 2110200 AND itemid = 2060000 THEN 35
WHEN dropperid = 2110200 AND itemid = 2061000 THEN 35
WHEN dropperid = 2110300 AND itemid = 2060000 THEN 39
WHEN dropperid = 2110300 AND itemid = 2061000 THEN 39
WHEN dropperid = 2110301 AND itemid = 2060000 THEN 47
WHEN dropperid = 2110301 AND itemid = 2061000 THEN 47
WHEN dropperid = 2130100 AND itemid = 2060000 THEN 35
WHEN dropperid = 2130100 AND itemid = 2061000 THEN 35
WHEN dropperid = 2220000 AND itemid = 2060000 THEN 45
WHEN dropperid = 2220000 AND itemid = 2061000 THEN 45
WHEN dropperid = 2220100 AND itemid = 2060000 THEN 33
WHEN dropperid = 2220100 AND itemid = 2061000 THEN 33
WHEN dropperid = 2230100 AND itemid = 2060000 THEN 44
WHEN dropperid = 2230100 AND itemid = 2061000 THEN 44
WHEN dropperid = 2230101 AND itemid = 2060000 THEN 39
WHEN dropperid = 2230101 AND itemid = 2061000 THEN 39
WHEN dropperid = 2230102 AND itemid = 2060000 THEN 40
WHEN dropperid = 2230102 AND itemid = 2061000 THEN 40
WHEN dropperid = 2230103 AND itemid = 2060000 THEN 38
WHEN dropperid = 2230103 AND itemid = 2061000 THEN 38
WHEN dropperid = 2230104 AND itemid = 2060000 THEN 45
WHEN dropperid = 2230104 AND itemid = 2061000 THEN 45
WHEN dropperid = 2230105 AND itemid = 2060000 THEN 38
WHEN dropperid = 2230105 AND itemid = 2061000 THEN 38
WHEN dropperid = 2230106 AND itemid = 2060000 THEN 40
WHEN dropperid = 2230106 AND itemid = 2061000 THEN 40
WHEN dropperid = 2230107 AND itemid = 2060000 THEN 39
WHEN dropperid = 2230107 AND itemid = 2061000 THEN 39
WHEN dropperid = 2230108 AND itemid = 2060000 THEN 35
WHEN dropperid = 2230108 AND itemid = 2061000 THEN 35
WHEN dropperid = 2230109 AND itemid = 2060000 THEN 45
WHEN dropperid = 2230109 AND itemid = 2061000 THEN 45
WHEN dropperid = 2230110 AND itemid = 2060000 THEN 38
WHEN dropperid = 2230110 AND itemid = 2061000 THEN 38
WHEN dropperid = 2230111 AND itemid = 2060000 THEN 39
WHEN dropperid = 2230111 AND itemid = 2061000 THEN 39
WHEN dropperid = 2230131 AND itemid = 2060000 THEN 39
WHEN dropperid = 2230131 AND itemid = 2061000 THEN 39
WHEN dropperid = 2230200 AND itemid = 2060000 THEN 47
WHEN dropperid = 2230200 AND itemid = 2061000 THEN 47
WHEN dropperid = 2300100 AND itemid = 2060000 THEN 33
WHEN dropperid = 2300100 AND itemid = 2061000 THEN 33
WHEN dropperid = 3000000 AND itemid = 2060000 THEN 49
WHEN dropperid = 3000000 AND itemid = 2061000 THEN 49
WHEN dropperid = 3000005 AND itemid = 2060000 THEN 49
WHEN dropperid = 3000005 AND itemid = 2061000 THEN 49
WHEN dropperid = 3000006 AND itemid = 2060000 THEN 49
WHEN dropperid = 3000006 AND itemid = 2061000 THEN 49
WHEN dropperid = 3100101 AND itemid = 2060000 THEN 52
WHEN dropperid = 3100101 AND itemid = 2061000 THEN 52
WHEN dropperid = 3100102 AND itemid = 2060000 THEN 49
WHEN dropperid = 3100102 AND itemid = 2061000 THEN 49
WHEN dropperid = 3110101 AND itemid = 2060000 THEN 52
WHEN dropperid = 3110101 AND itemid = 2061000 THEN 52
WHEN dropperid = 3110102 AND itemid = 2060000 THEN 52
WHEN dropperid = 3110102 AND itemid = 2061000 THEN 52
WHEN dropperid = 3110300 AND itemid = 2060000 THEN 52
WHEN dropperid = 3110300 AND itemid = 2061000 THEN 52
WHEN dropperid = 3110301 AND itemid = 2060000 THEN 52
WHEN dropperid = 3110301 AND itemid = 2061000 THEN 52
WHEN dropperid = 3110302 AND itemid = 2060000 THEN 57
WHEN dropperid = 3110302 AND itemid = 2061000 THEN 57
WHEN dropperid = 3110303 AND itemid = 2060000 THEN 62
WHEN dropperid = 3110303 AND itemid = 2061000 THEN 62
WHEN dropperid = 3210100 AND itemid = 2060005 THEN 8
WHEN dropperid = 3210203 AND itemid = 2060000 THEN 59
WHEN dropperid = 3210203 AND itemid = 2061000 THEN 59
WHEN dropperid = 3210204 AND itemid = 2060000 THEN 55
WHEN dropperid = 3210204 AND itemid = 2061000 THEN 55
WHEN dropperid = 3210205 AND itemid = 2060000 THEN 55
WHEN dropperid = 3210205 AND itemid = 2061000 THEN 55
WHEN dropperid = 3210206 AND itemid = 2060000 THEN 59
WHEN dropperid = 3210206 AND itemid = 2061000 THEN 59
WHEN dropperid = 3210207 AND itemid = 2060000 THEN 55
WHEN dropperid = 3210207 AND itemid = 2060005 THEN 9
WHEN dropperid = 3210207 AND itemid = 2061000 THEN 55
WHEN dropperid = 3210208 AND itemid = 2060000 THEN 59
WHEN dropperid = 3210208 AND itemid = 2061000 THEN 59
WHEN dropperid = 3210450 AND itemid = 2060000 THEN 59
WHEN dropperid = 3210450 AND itemid = 2061000 THEN 59
WHEN dropperid = 3210800 AND itemid = 2060005 THEN 10
WHEN dropperid = 3220000 AND itemid = 2060000 THEN 79
WHEN dropperid = 3220000 AND itemid = 2061000 THEN 79
WHEN dropperid = 3230100 AND itemid = 2060000 THEN 57
WHEN dropperid = 3230100 AND itemid = 2061000 THEN 57
WHEN dropperid = 3230103 AND itemid = 2060000 THEN 62
WHEN dropperid = 3230103 AND itemid = 2061000 THEN 62
WHEN dropperid = 3230200 AND itemid = 2060000 THEN 57
WHEN dropperid = 3230200 AND itemid = 2061000 THEN 57
WHEN dropperid = 3230302 AND itemid = 2060000 THEN 57
WHEN dropperid = 3230302 AND itemid = 2061000 THEN 57
WHEN dropperid = 3230303 AND itemid = 2060000 THEN 60
WHEN dropperid = 3230303 AND itemid = 2061000 THEN 60
WHEN dropperid = 3230304 AND itemid = 2060000 THEN 62
WHEN dropperid = 3230304 AND itemid = 2061000 THEN 62
WHEN dropperid = 3230305 AND itemid = 2060000 THEN 64
WHEN dropperid = 3230305 AND itemid = 2061000 THEN 64
WHEN dropperid = 3230306 AND itemid = 2060000 THEN 60
WHEN dropperid = 3230306 AND itemid = 2061000 THEN 60
WHEN dropperid = 3230307 AND itemid = 2060000 THEN 50
WHEN dropperid = 3230307 AND itemid = 2061000 THEN 50
WHEN dropperid = 3230308 AND itemid = 2060000 THEN 64
WHEN dropperid = 3230308 AND itemid = 2061000 THEN 64
WHEN dropperid = 3230400 AND itemid = 2060000 THEN 49
WHEN dropperid = 3230400 AND itemid = 2061000 THEN 49
WHEN dropperid = 3230405 AND itemid = 2060000 THEN 62
WHEN dropperid = 3230405 AND itemid = 2061000 THEN 62
WHEN dropperid = 3300000 AND itemid = 2060000 THEN 49
WHEN dropperid = 3300000 AND itemid = 2061000 THEN 49
WHEN dropperid = 3300001 AND itemid = 2060000 THEN 49
WHEN dropperid = 3300001 AND itemid = 2061000 THEN 49
WHEN dropperid = 3300002 AND itemid = 2060000 THEN 50
WHEN dropperid = 3300002 AND itemid = 2061000 THEN 50
WHEN dropperid = 3300003 AND itemid = 2060000 THEN 52
WHEN dropperid = 3300003 AND itemid = 2061000 THEN 52
WHEN dropperid = 3300004 AND itemid = 2060000 THEN 54
WHEN dropperid = 3300004 AND itemid = 2061000 THEN 54
WHEN dropperid = 3300006 AND itemid = 2060000 THEN 57
WHEN dropperid = 3300006 AND itemid = 2061000 THEN 57
WHEN dropperid = 3300007 AND itemid = 2060000 THEN 57
WHEN dropperid = 3300007 AND itemid = 2061000 THEN 57
WHEN dropperid = 3300008 AND itemid = 2060000 THEN 87
WHEN dropperid = 3300008 AND itemid = 2061000 THEN 87
WHEN dropperid = 4110300 AND itemid = 2060000 THEN 68
WHEN dropperid = 4110300 AND itemid = 2061000 THEN 68
WHEN dropperid = 4110301 AND itemid = 2060000 THEN 73
WHEN dropperid = 4110301 AND itemid = 2061000 THEN 73
WHEN dropperid = 4130103 AND itemid = 2060000 THEN 107
WHEN dropperid = 4130103 AND itemid = 2061000 THEN 107
WHEN dropperid = 4230103 AND itemid = 2060000 THEN 68
WHEN dropperid = 4230103 AND itemid = 2061000 THEN 68
WHEN dropperid = 4230106 AND itemid = 2060000 THEN 73
WHEN dropperid = 4230106 AND itemid = 2061000 THEN 73
WHEN dropperid = 4230108 AND itemid = 2060000 THEN 70
WHEN dropperid = 4230108 AND itemid = 2061000 THEN 70
WHEN dropperid = 4230109 AND itemid = 2060000 THEN 68
WHEN dropperid = 4230109 AND itemid = 2061000 THEN 68
WHEN dropperid = 4230110 AND itemid = 2060000 THEN 73
WHEN dropperid = 4230110 AND itemid = 2061000 THEN 73
WHEN dropperid = 4230111 AND itemid = 2060000 THEN 67
WHEN dropperid = 4230111 AND itemid = 2061000 THEN 67
WHEN dropperid = 4230112 AND itemid = 2060000 THEN 72
WHEN dropperid = 4230112 AND itemid = 2061000 THEN 72
WHEN dropperid = 4230113 AND itemid = 2060000 THEN 65
WHEN dropperid = 4230113 AND itemid = 2061000 THEN 65
WHEN dropperid = 4230114 AND itemid = 2060000 THEN 67
WHEN dropperid = 4230114 AND itemid = 2061000 THEN 67
WHEN dropperid = 4230115 AND itemid = 2060000 THEN 75
WHEN dropperid = 4230115 AND itemid = 2061000 THEN 75
WHEN dropperid = 4230116 AND itemid = 2060000 THEN 65
WHEN dropperid = 4230116 AND itemid = 2061000 THEN 65
WHEN dropperid = 4230117 AND itemid = 2060000 THEN 68
WHEN dropperid = 4230117 AND itemid = 2061000 THEN 68
WHEN dropperid = 4230118 AND itemid = 2060000 THEN 73
WHEN dropperid = 4230118 AND itemid = 2061000 THEN 73
WHEN dropperid = 4230119 AND itemid = 2060000 THEN 67
WHEN dropperid = 4230119 AND itemid = 2061000 THEN 67
WHEN dropperid = 4230120 AND itemid = 2060000 THEN 72
WHEN dropperid = 4230120 AND itemid = 2061000 THEN 72
WHEN dropperid = 4230121 AND itemid = 2060000 THEN 75
WHEN dropperid = 4230121 AND itemid = 2061000 THEN 75
WHEN dropperid = 4230123 AND itemid = 2060000 THEN 70
WHEN dropperid = 4230123 AND itemid = 2061000 THEN 70
WHEN dropperid = 4230124 AND itemid = 2060000 THEN 68
WHEN dropperid = 4230124 AND itemid = 2061000 THEN 68
WHEN dropperid = 4230125 AND itemid = 2060000 THEN 72
WHEN dropperid = 4230125 AND itemid = 2061000 THEN 72
WHEN dropperid = 4230126 AND itemid = 2060000 THEN 77
WHEN dropperid = 4230126 AND itemid = 2061000 THEN 77
WHEN dropperid = 4230201 AND itemid = 2060000 THEN 65
WHEN dropperid = 4230201 AND itemid = 2061000 THEN 65
WHEN dropperid = 4230300 AND itemid = 2060000 THEN 73
WHEN dropperid = 4230300 AND itemid = 2061000 THEN 73
WHEN dropperid = 4230400 AND itemid = 2060000 THEN 73
WHEN dropperid = 4230400 AND itemid = 2061000 THEN 73
WHEN dropperid = 4230500 AND itemid = 2060000 THEN 65
WHEN dropperid = 4230500 AND itemid = 2061000 THEN 65
WHEN dropperid = 4230501 AND itemid = 2060000 THEN 67
WHEN dropperid = 4230501 AND itemid = 2061000 THEN 67
WHEN dropperid = 4230502 AND itemid = 2060000 THEN 70
WHEN dropperid = 4230502 AND itemid = 2061000 THEN 70
WHEN dropperid = 4230503 AND itemid = 2060000 THEN 73
WHEN dropperid = 4230503 AND itemid = 2061000 THEN 73
WHEN dropperid = 4230504 AND itemid = 2060000 THEN 73
WHEN dropperid = 4230504 AND itemid = 2061000 THEN 73
WHEN dropperid = 4230600 AND itemid = 2060000 THEN 65
WHEN dropperid = 4230600 AND itemid = 2061000 THEN 65
WHEN dropperid = 4240000 AND itemid = 2060000 THEN 80
WHEN dropperid = 4240000 AND itemid = 2061000 THEN 80
WHEN dropperid = 5120100 AND itemid = 2060000 THEN 123
WHEN dropperid = 5120100 AND itemid = 2061000 THEN 123
WHEN dropperid = 5130104 AND itemid = 2060000 THEN 89
WHEN dropperid = 5130104 AND itemid = 2061000 THEN 89
WHEN dropperid = 5140000 AND itemid = 2060000 THEN 94
WHEN dropperid = 5140000 AND itemid = 2061000 THEN 94
WHEN dropperid = 5150000 AND itemid = 2060000 THEN 97
WHEN dropperid = 5150000 AND itemid = 2061000 THEN 97
WHEN dropperid = 5200000 AND itemid = 2060001 THEN 19
WHEN dropperid = 5200000 AND itemid = 2061001 THEN 19
WHEN dropperid = 5200001 AND itemid = 2060001 THEN 24
WHEN dropperid = 5200001 AND itemid = 2061001 THEN 24
WHEN dropperid = 5200002 AND itemid = 2060001 THEN 24
WHEN dropperid = 5200002 AND itemid = 2061001 THEN 24
WHEN dropperid = 5220000 AND itemid = 2060000 THEN 125
WHEN dropperid = 5220000 AND itemid = 2061000 THEN 125
WHEN dropperid = 5220002 AND itemid = 2060000 THEN 114
WHEN dropperid = 5220002 AND itemid = 2061000 THEN 114
WHEN dropperid = 5220003 AND itemid = 2060000 THEN 134
WHEN dropperid = 5220003 AND itemid = 2061000 THEN 134
WHEN dropperid = 5400000 AND itemid = 2060001 THEN 28
WHEN dropperid = 5400000 AND itemid = 2061001 THEN 28
WHEN dropperid = 6220000 AND itemid = 2060000 THEN 148
WHEN dropperid = 6220000 AND itemid = 2061000 THEN 148
WHEN dropperid = 7120103 AND itemid = 2060003 THEN 28
WHEN dropperid = 7220000 AND itemid = 2060001 THEN 81
WHEN dropperid = 7220000 AND itemid = 2061001 THEN 81
WHEN dropperid = 7220001 AND itemid = 2060001 THEN 79
WHEN dropperid = 7220001 AND itemid = 2061001 THEN 79
WHEN dropperid = 7220002 AND itemid = 2060001 THEN 87
WHEN dropperid = 7220002 AND itemid = 2061001 THEN 87
WHEN dropperid = 8220000 AND itemid = 2060001 THEN 94
WHEN dropperid = 8220000 AND itemid = 2061001 THEN 94
WHEN dropperid = 8220001 AND itemid = 2060001 THEN 102
WHEN dropperid = 8220001 AND itemid = 2061001 THEN 102
WHEN dropperid = 9300011 AND itemid = 2060000 THEN 64
WHEN dropperid = 9300011 AND itemid = 2061000 THEN 64
WHEN dropperid = 9300060 AND itemid = 2060000 THEN 68
WHEN dropperid = 9300060 AND itemid = 2061000 THEN 68
WHEN dropperid = 9300131 AND itemid = 2060000 THEN 64
WHEN dropperid = 9300131 AND itemid = 2061000 THEN 64
WHEN dropperid = 9300132 AND itemid = 2060000 THEN 49
WHEN dropperid = 9300132 AND itemid = 2061000 THEN 49
WHEN dropperid = 9300133 AND itemid = 2061000 THEN 49
WHEN dropperid = 9300160 AND itemid = 2060000 THEN 103
WHEN dropperid = 9300160 AND itemid = 2061000 THEN 103
WHEN dropperid = 9300161 AND itemid = 2060000 THEN 103
WHEN dropperid = 9300161 AND itemid = 2061000 THEN 103
WHEN dropperid = 9300274 AND itemid = 2060000 THEN 13
WHEN dropperid = 9300274 AND itemid = 2061000 THEN 13
WHEN dropperid = 9300332 AND itemid = 2060000 THEN 65
WHEN dropperid = 9300334 AND itemid = 2060000 THEN 80
WHEN dropperid = 9300341 AND itemid = 2060000 THEN 9
WHEN dropperid = 9300341 AND itemid = 2061000 THEN 9
WHEN dropperid = 9300342 AND itemid = 2060000 THEN 13
WHEN dropperid = 9300342 AND itemid = 2061000 THEN 13
WHEN dropperid = 9300343 AND itemid = 2060000 THEN 12
WHEN dropperid = 9300343 AND itemid = 2061000 THEN 12
WHEN dropperid = 9303005 AND itemid = 2060001 THEN 21
WHEN dropperid = 9303005 AND itemid = 2061001 THEN 21
WHEN dropperid = 9303008 AND itemid = 2060001 THEN 21
WHEN dropperid = 9303008 AND itemid = 2061001 THEN 21
WHEN dropperid = 9303009 AND itemid = 2060001 THEN 41
WHEN dropperid = 9303009 AND itemid = 2061001 THEN 41
WHEN dropperid = 9400000 AND itemid = 2060001 THEN 20
WHEN dropperid = 9400009 AND itemid = 2060001 THEN 163
WHEN dropperid = 9400011 AND itemid = 2060002 THEN 21
WHEN dropperid = 9400100 AND itemid = 2060003 THEN 18
WHEN dropperid = 9400101 AND itemid = 2061003 THEN 19
WHEN dropperid = 9400204 AND itemid = 2060003 THEN 22
WHEN dropperid = 9400239 AND itemid = 2060000 THEN 39
WHEN dropperid = 9400239 AND itemid = 2061000 THEN 39
WHEN dropperid = 9400244 AND itemid = 2060000 THEN 97
WHEN dropperid = 9400244 AND itemid = 2061000 THEN 97
WHEN dropperid = 9400248 AND itemid = 2060000 THEN 39
WHEN dropperid = 9400248 AND itemid = 2061000 THEN 39
WHEN dropperid = 9400540 AND itemid = 2060004 THEN 8
WHEN dropperid = 9400540 AND itemid = 2061004 THEN 8
WHEN dropperid = 9400541 AND itemid = 2060004 THEN 8
WHEN dropperid = 9400541 AND itemid = 2061004 THEN 8
WHEN dropperid = 9400542 AND itemid = 2060004 THEN 11
WHEN dropperid = 9400542 AND itemid = 2061004 THEN 11
WHEN dropperid = 9400543 AND itemid = 2060004 THEN 13
WHEN dropperid = 9400543 AND itemid = 2061004 THEN 13
WHEN dropperid = 9400547 AND itemid = 2060000 THEN 44
WHEN dropperid = 9400547 AND itemid = 2061000 THEN 44
WHEN dropperid = 9400548 AND itemid = 2060000 THEN 49
WHEN dropperid = 9400548 AND itemid = 2061000 THEN 49
WHEN dropperid = 9400550 AND itemid = 2060000 THEN 44
WHEN dropperid = 9400550 AND itemid = 2061000 THEN 44
WHEN dropperid = 9400558 AND itemid = 2060000 THEN 49
WHEN dropperid = 9400558 AND itemid = 2061000 THEN 49
WHEN dropperid = 9400563 AND itemid = 2060000 THEN 65
WHEN dropperid = 9400563 AND itemid = 2061000 THEN 65
WHEN dropperid = 9400638 AND itemid = 2060000 THEN 33
WHEN dropperid = 9400638 AND itemid = 2061000 THEN 33
WHEN dropperid = 9420500 AND itemid = 2060000 THEN 45
WHEN dropperid = 9420500 AND itemid = 2061000 THEN 45
WHEN dropperid = 9420502 AND itemid = 2060000 THEN 29
WHEN dropperid = 9420502 AND itemid = 2061000 THEN 29
WHEN dropperid = 9420506 AND itemid = 2060000 THEN 38
WHEN dropperid = 9420506 AND itemid = 2061000 THEN 38
WHEN dropperid = 9420508 AND itemid = 2060000 THEN 70
WHEN dropperid = 9420508 AND itemid = 2061000 THEN 70
WHEN dropperid = 9420527 AND itemid = 2060001 THEN 36
WHEN dropperid = 9420527 AND itemid = 2061001 THEN 36
WHEN dropperid = 9420531 AND itemid = 2060001 THEN 48
WHEN dropperid = 9420531 AND itemid = 2061001 THEN 48
WHEN dropperid = 9500112 AND itemid = 2060001 THEN 28
WHEN dropperid = 9500112 AND itemid = 2061001 THEN 28
WHEN dropperid = 9500119 AND itemid = 2060000 THEN 64
WHEN dropperid = 9500119 AND itemid = 2061000 THEN 64
WHEN dropperid = 9500120 AND itemid = 2060000 THEN 73
WHEN dropperid = 9500120 AND itemid = 2061000 THEN 73
WHEN dropperid = 9500122 AND itemid = 2060000 THEN 80
WHEN dropperid = 9500123 AND itemid = 2060000 THEN 97
WHEN dropperid = 9500123 AND itemid = 2061000 THEN 97
WHEN dropperid = 9500308 AND itemid = 2060000 THEN 114
WHEN dropperid = 9500308 AND itemid = 2061000 THEN 114
WHEN dropperid = 9500310 AND itemid = 2060000 THEN 134
WHEN dropperid = 9500310 AND itemid = 2061000 THEN 134
WHEN dropperid = 9500312 AND itemid = 2060001 THEN 79
WHEN dropperid = 9500312 AND itemid = 2061001 THEN 79
WHEN dropperid = 9500313 AND itemid = 2060001 THEN 81
WHEN dropperid = 9500313 AND itemid = 2061001 THEN 81
WHEN dropperid = 9500314 AND itemid = 2060001 THEN 87
WHEN dropperid = 9500314 AND itemid = 2061001 THEN 87
WHEN dropperid = 9500321 AND itemid = 2060001 THEN 8
WHEN dropperid = 9500321 AND itemid = 2061001 THEN 8
WHEN dropperid = 9500366 AND itemid = 2060000 THEN 24
WHEN dropperid = 9500369 AND itemid = 2060000 THEN 24
ELSE maximum_quantity END
;

View File

@@ -1,46 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package life;
public enum Element {
NEUTRAL, FIRE, ICE, LIGHTING, POISON, HOLY, DARK;
public static Element getFromChar(char c) {
switch (Character.toUpperCase(c)) {
case 'F':
return FIRE;
case 'I':
return ICE;
case 'L':
return LIGHTING;
case 'S':
return POISON;
case 'H':
return HOLY;
case 'D':
return DARK;
case 'P':
return NEUTRAL;
}
throw new IllegalArgumentException("unknown elemnt char " + c);
}
}

View File

@@ -1,41 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package life;
public enum ElementalEffectiveness {
NORMAL, IMMUNE, STRONG, WEAK, NEUTRAL;
public static ElementalEffectiveness getByNumber(int num) {
switch (num) {
case 1:
return IMMUNE;
case 2:
return STRONG;
case 3:
return WEAK;
case 4:
return NEUTRAL;
default:
throw new IllegalArgumentException("Unkown effectiveness: " + num);
}
}
}

View File

@@ -1,240 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package life;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import provider.MapleData;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataFileEntry;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.MapleDataType;
import tools.Pair;
public class MapleLifeFactory {
private static String wzPath = "../../wz";
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(wzPath + "/Mob.wz"));
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(new File(wzPath + "/String.wz"));
private static MapleData mobStringData = stringDataWZ.getData("Mob.img");
private static MapleData npcStringData = stringDataWZ.getData("Npc.img");
private static Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
private static int getMonsterId(String fileName) {
return Integer.parseInt(fileName.substring(0, 7));
}
public static Map<Integer, MapleMonsterStats> getAllMonsterStats() {
MapleDataDirectoryEntry root = data.getRoot();
System.out.print("Parsing mob stats... ");
for (MapleDataFileEntry mFile : root.getFiles()) {
try {
String fileName = mFile.getName();
//System.out.println("Parsing '" + fileName + "'");
MapleData monsterData = data.getData(fileName);
if (monsterData == null) {
continue;
}
Integer mid = getMonsterId(fileName);
MapleData monsterInfoData = monsterData.getChildByPath("info");
MapleMonsterStats stats = new MapleMonsterStats();
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1);
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0));
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0));
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0));
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0);
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0);
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0);
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0);
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1));
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0);
MapleData special = monsterInfoData.getChildByPath("coolDamage");
if (special != null) {
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
stats.setCool(new Pair<>(coolDmg, coolProb));
}
special = monsterInfoData.getChildByPath("loseItem");
if (special != null) {
for (MapleData liData : special.getChildren()) {
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
}
}
special = monsterInfoData.getChildByPath("selfDestruction");
if (special != null) {
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
}
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
int firstAttack = 0;
if (firstAttackData != null) {
if (firstAttackData.getType() == MapleDataType.FLOAT) {
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
} else {
firstAttack = MapleDataTool.getInt(firstAttackData);
}
}
stats.setFirstAttack(firstAttack > 0);
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000);
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
for (MapleData idata : monsterData) {
if (!idata.getName().equals("info")) {
int delay = 0;
for (MapleData pic : idata.getChildren()) {
delay += MapleDataTool.getIntConvert("delay", pic, 0);
}
stats.setAnimationTime(idata.getName(), delay);
}
}
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
if (reviveInfo != null) {
List<Integer> revives = new LinkedList<>();
for (MapleData data_ : reviveInfo) {
revives.add(MapleDataTool.getInt(data_));
}
stats.setRevives(revives);
}
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
MapleData monsterSkillData = monsterInfoData.getChildByPath("skill");
if (monsterSkillData != null) {
int i = 0;
List<Pair<Integer, Integer>> skills = new ArrayList<>();
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0))));
i++;
}
stats.setSkills(skills);
}
MapleData banishData = monsterInfoData.getChildByPath("ban");
if (banishData != null) {
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
}
monsterStats.put(mid, stats);
} catch(NullPointerException npe) {
//System.out.println("[SEVERE] " + mFile.getName() + " failed to load. Issue: " + npe.getMessage() + "\n\n");
}
}
System.out.println("done!");
return monsterStats;
}
private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) {
for (int i = 0; i < elemAttr.length(); i += 2) {
stats.setEffectiveness(Element.getFromChar(elemAttr.charAt(i)), ElementalEffectiveness.getByNumber(Integer.valueOf(String.valueOf(elemAttr.charAt(i + 1)))));
}
}
public static class BanishInfo {
private int map;
private String portal, msg;
public BanishInfo(String msg, int map, String portal) {
this.msg = msg;
this.map = map;
this.portal = portal;
}
public int getMap() {
return map;
}
public String getPortal() {
return portal;
}
public String getMsg() {
return msg;
}
}
public static class loseItem {
private int id;
private byte chance, x;
private loseItem(int id, byte chance, byte x) {
this.id = id;
this.chance = chance;
this.x = x;
}
public int getId() {
return id;
}
public byte getChance() {
return chance;
}
public byte getX() {
return x;
}
}
public static class selfDestruction {
private byte action;
private int removeAfter;
private int hp;
private selfDestruction(byte action, int removeAfter, int hp) {
this.action = action;
this.removeAfter = removeAfter;
this.hp = hp;
}
public int getHp() {
return hp;
}
public byte getAction() {
return action;
}
public int removeAfter() {
return removeAfter;
}
}
}

View File

@@ -1,330 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package life;
import life.MapleLifeFactory.BanishInfo;
import life.MapleLifeFactory.loseItem;
import life.MapleLifeFactory.selfDestruction;
import tools.Pair;
import java.util.*;
/**
* @author Frz
*/
public class MapleMonsterStats {
private boolean changeable;
private int exp, hp, mp, level, PADamage, PDDamage, MADamage, MDDamage, dropPeriod, cp, buffToGive, removeAfter;
private boolean boss, undead, ffaLoot, isExplosiveReward, firstAttack, removeOnMiss;
private String name;
private Map<String, Integer> animationTimes = new HashMap<String, Integer>();
private Map<Element, ElementalEffectiveness> resistance = new HashMap<Element, ElementalEffectiveness>();
private List<Integer> revives = Collections.emptyList();
private byte tagColor, tagBgColor;
private List<Pair<Integer, Integer>> skills = new ArrayList<Pair<Integer, Integer>>();
private Pair<Integer, Integer> cool = null;
private BanishInfo banish = null;
private List<loseItem> loseItem = null;
private selfDestruction selfDestruction = null;
private boolean friendly;
public void setChange(boolean change) {
this.changeable = change;
}
public boolean isChangeable() {
return changeable;
}
public int getExp() {
return exp;
}
public void setExp(int exp) {
this.exp = exp;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getMp() {
return mp;
}
public void setMp(int mp) {
this.mp = mp;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int removeAfter() {
return removeAfter;
}
public void setRemoveAfter(int removeAfter) {
this.removeAfter = removeAfter;
}
public int getDropPeriod() {
return dropPeriod;
}
public void setDropPeriod(int dropPeriod) {
this.dropPeriod = dropPeriod;
}
public void setBoss(boolean boss) {
this.boss = boss;
}
public boolean isBoss() {
return boss;
}
public void setFfaLoot(boolean ffaLoot) {
this.ffaLoot = ffaLoot;
}
public boolean isFfaLoot() {
return ffaLoot;
}
public void setAnimationTime(String name, int delay) {
animationTimes.put(name, delay);
}
public int getAnimationTime(String name) {
Integer ret = animationTimes.get(name);
if (ret == null) {
return 500;
}
return ret.intValue();
}
public boolean isMobile() {
return animationTimes.containsKey("move") || animationTimes.containsKey("fly");
}
public List<Integer> getRevives() {
return revives;
}
public void setRevives(List<Integer> revives) {
this.revives = revives;
}
public void setUndead(boolean undead) {
this.undead = undead;
}
public boolean getUndead() {
return undead;
}
public void setEffectiveness(Element e, ElementalEffectiveness ee) {
resistance.put(e, ee);
}
public ElementalEffectiveness getEffectiveness(Element e) {
ElementalEffectiveness elementalEffectiveness = resistance.get(e);
if (elementalEffectiveness == null) {
return ElementalEffectiveness.NORMAL;
} else {
return elementalEffectiveness;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte getTagColor() {
return tagColor;
}
public void setTagColor(int tagColor) {
this.tagColor = (byte) tagColor;
}
public byte getTagBgColor() {
return tagBgColor;
}
public void setTagBgColor(int tagBgColor) {
this.tagBgColor = (byte) tagBgColor;
}
public void setSkills(List<Pair<Integer, Integer>> skills) {
this.skills.addAll(skills);
}
public List<Pair<Integer, Integer>> getSkills() {
return Collections.unmodifiableList(this.skills);
}
public int getNoSkills() {
return this.skills.size();
}
public boolean hasSkill(int skillId, int level) {
for (Pair<Integer, Integer> skill : skills) {
if (skill.getLeft() == skillId && skill.getRight() == level) {
return true;
}
}
return false;
}
public void setFirstAttack(boolean firstAttack) {
this.firstAttack = firstAttack;
}
public boolean isFirstAttack() {
return firstAttack;
}
public void setBuffToGive(int buff) {
this.buffToGive = buff;
}
public int getBuffToGive() {
return buffToGive;
}
void removeEffectiveness(Element e) {
resistance.remove(e);
}
public BanishInfo getBanishInfo() {
return banish;
}
public void setBanishInfo(BanishInfo banish) {
this.banish = banish;
}
public int getPADamage() {
return PADamage;
}
public void setPADamage(int PADamage) {
this.PADamage = PADamage;
}
public int getCP() {
return cp;
}
public void setCP(int cp) {
this.cp = cp;
}
public List<loseItem> loseItem() {
return loseItem;
}
public void addLoseItem(loseItem li) {
if (loseItem == null) {
loseItem = new LinkedList<loseItem>();
}
loseItem.add(li);
}
public selfDestruction selfDestruction() {
return selfDestruction;
}
public void setSelfDestruction(selfDestruction sd) {
this.selfDestruction = sd;
}
public void setExplosiveReward(boolean isExplosiveReward) {
this.isExplosiveReward = isExplosiveReward;
}
public boolean isExplosiveReward() {
return isExplosiveReward;
}
public void setRemoveOnMiss(boolean removeOnMiss) {
this.removeOnMiss = removeOnMiss;
}
public boolean removeOnMiss() {
return removeOnMiss;
}
public void setCool(Pair<Integer, Integer> cool) {
this.cool = cool;
}
public Pair<Integer, Integer> getCool() {
return cool;
}
public int getPDDamage() {
return PDDamage;
}
public int getMADamage() {
return MADamage;
}
public int getMDDamage() {
return MDDamage;
}
public boolean isFriendly() {
return friendly;
}
public void setFriendly(boolean value) {
this.friendly = value;
}
public void setPDDamage(int PDDamage) {
this.PDDamage = PDDamage;
}
public void setMADamage(int MADamage) {
this.MADamage = MADamage;
}
public void setMDDamage(int MDDamage) {
this.MDDamage = MDDamage;
}
}

View File

@@ -1,30 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
import java.awt.image.BufferedImage;
public interface MapleCanvas {
int getHeight();
int getWidth();
BufferedImage getImage();
}

View File

@@ -1,34 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
import java.util.List;
import provider.wz.MapleDataType;
public interface MapleData extends MapleDataEntity, Iterable<MapleData> {
@Override
public String getName();
public MapleDataType getType();
public List<MapleData> getChildren();
public MapleData getChildByPath(String path);
public Object getData();
}

View File

@@ -1,34 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
import java.util.List;
/**
*
* @author Matze
*/
public interface MapleDataDirectoryEntry extends MapleDataEntry {
public List<MapleDataDirectoryEntry> getSubdirectories();
public List<MapleDataFileEntry> getFiles();
public MapleDataEntry getEntry(String name);
}

View File

@@ -1,31 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
/**
*
* @author Matze
*/
public interface MapleDataEntity {
public String getName();
public MapleDataEntity getParent();
}

View File

@@ -1,33 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
/**
*
* @author Matze
*/
public interface MapleDataEntry extends MapleDataEntity {
public String getName();
public int getSize();
public int getChecksum();
public int getOffset();
}

View File

@@ -1,30 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
/**
*
* @author Matze
*/
public interface MapleDataFileEntry extends MapleDataEntry {
public void setOffset(int offset);
}

View File

@@ -1,27 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
public interface MapleDataProvider {
MapleData getData(String path);
MapleDataDirectoryEntry getRoot();
}

View File

@@ -1,55 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
import java.io.File;
import java.io.IOException;
import provider.wz.WZFile;
import provider.wz.XMLWZFile;
public class MapleDataProviderFactory {
private final static String wzPath = System.getProperty("wzpath");
private static MapleDataProvider getWZ(File in, boolean provideImages) {
if (in.getName().toLowerCase().endsWith("wz") && !in.isDirectory()) {
try {
return new WZFile(in, provideImages);
} catch (IOException e) {
throw new RuntimeException("Loading WZ File failed", e);
}
} else {
return new XMLWZFile(in);
}
}
public static MapleDataProvider getDataProvider(File in) {
return getWZ(in, false);
}
public static MapleDataProvider getImageProvidingDataProvider(File in) {
return getWZ(in, true);
}
public static File fileInWZPath(String filename) {
return new File(wzPath, filename);
}
}

View File

@@ -1,145 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider;
import java.awt.Point;
import java.awt.image.BufferedImage;
import provider.wz.MapleDataType;
public class MapleDataTool {
public static String getString(MapleData data) {
return ((String) data.getData());
}
public static String getString(MapleData data, String def) {
if (data == null || data.getData() == null) {
return def;
} else {
return ((String) data.getData());
}
}
public static String getString(String path, MapleData data) {
return getString(data.getChildByPath(path));
}
public static String getString(String path, MapleData data, String def) {
return getString(data.getChildByPath(path), def);
}
public static double getDouble(MapleData data) {
return ((Double) data.getData()).doubleValue();
}
public static float getFloat(MapleData data) {
return ((Float) data.getData()).floatValue();
}
public static int getInt(MapleData data) {
if (data == null || data.getData() == null) {
return 0;// DEF?
}
return ((Integer) data.getData()).intValue();
}
public static int getInt(String path, MapleData data) {
return getInt(data.getChildByPath(path));
}
public static int getIntConvert(MapleData data) {
if (data.getType() == MapleDataType.STRING) {
return Integer.parseInt(getString(data));
} else {
return getInt(data);
}
}
public static int getIntConvert(String path, MapleData data) {
MapleData d = data.getChildByPath(path);
if (d.getType() == MapleDataType.STRING) {
return Integer.parseInt(getString(d));
} else {
return getInt(d);
}
}
public static int getInt(MapleData data, int def) {
if (data == null || data.getData() == null) {
return def;
} else if (data.getType() == MapleDataType.STRING) {
return Integer.parseInt(getString(data));
} else {
return ((Integer) data.getData()).intValue();
}
}
public static int getInt(String path, MapleData data, int def) {
return getInt(data.getChildByPath(path), def);
}
public static int getIntConvert(String path, MapleData data, int def) {
MapleData d = data.getChildByPath(path);
if (d == null) {
return def;
}
if (d.getType() == MapleDataType.STRING) {
try {
return Integer.parseInt(getString(d));
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
return def;
}
} else {
return getInt(d, def);
}
}
public static BufferedImage getImage(MapleData data) {
return ((MapleCanvas) data.getData()).getImage();
}
public static Point getPoint(MapleData data) {
return ((Point) data.getData());
}
public static Point getPoint(String path, MapleData data) {
return getPoint(data.getChildByPath(path));
}
public static Point getPoint(String path, MapleData data, Point def) {
final MapleData pointData = data.getChildByPath(path);
if (pointData == null) {
return def;
}
return getPoint(pointData);
}
public static String getFullDataPath(MapleData data) {
String path = "";
MapleDataEntity myData = data;
while (myData != null) {
path = myData.getName() + "/" + path;
myData = myData.getParent();
}
return path.substring(0, path.length() - 1);
}
}

View File

@@ -1,70 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import provider.MapleCanvas;
public class FileStoredPngMapleCanvas implements MapleCanvas {
private File file;
private int width;
private int height;
private BufferedImage image;
public FileStoredPngMapleCanvas(int width, int height, File fileIn) {
this.width = width;
this.height = height;
this.file = fileIn;
}
@Override
public int getHeight() {
return height;
}
@Override
public int getWidth() {
return width;
}
@Override
public BufferedImage getImage() {
loadImageIfNecessary();
return image;
}
private void loadImageIfNecessary() {
if (image == null) {
try {
image = ImageIO.read(file);
// replace the dimensions loaded from the wz by the REAL dimensions from the image - should be equal tho
width = image.getWidth();
height = image.getHeight();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -1,39 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
public class ImgMapleSound {
private int dataLength, offset;
public ImgMapleSound(int dataLength, int offset) {
this.dataLength = dataLength;
this.offset = offset;
}
public int getDataLength() {
return dataLength;
}
public int getOffset() {
return offset;
}
}

View File

@@ -1,86 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import provider.MapleDataProviderFactory;
import tools.data.input.GenericLittleEndianAccessor;
import tools.data.input.InputStreamByteStream;
import tools.data.input.LittleEndianAccessor;
public class ListWZFile {
private LittleEndianAccessor lea;
private List<String> entries = new ArrayList<String>();
private static Collection<String> modernImgs = new HashSet<String>();
public static byte[] xorBytes(byte[] a, byte[] b) {
byte[] wusched = new byte[a.length];
for (int i = 0; i < a.length; i++) {
wusched[i] = (byte) (a[i] ^ b[i]);
}
return wusched;
}
public ListWZFile(File listwz) throws FileNotFoundException {
lea = new GenericLittleEndianAccessor(new InputStreamByteStream(new BufferedInputStream(new FileInputStream(listwz))));
while (lea.available() > 0) {
int l = lea.readInt() * 2;
byte[] chunk = new byte[l];
for (int i = 0; i < chunk.length; i++) {
chunk[i] = lea.readByte();
}
lea.readChar();
final String value = String.valueOf(WZTool.readListString(chunk));
entries.add(value);
}
entries = Collections.unmodifiableList(entries);
}
public List<String> getEntries() {
return entries;
}
public static void init() {
final String listWz = System.getProperty("listwz");
if (listWz != null) {
ListWZFile listwz;
try {
listwz = new ListWZFile(MapleDataProviderFactory.fileInWZPath("List.wz"));
modernImgs = new HashSet<String>(listwz.getEntries());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
public static boolean isModernImgFile(String path) {
return modernImgs.contains(path);
}
}

View File

@@ -1,26 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
public enum MapleDataType {
NONE, IMG_0x00, SHORT, INT, FLOAT, DOUBLE, STRING, EXTENDED, PROPERTY, CANVAS, VECTOR, CONVEX, SOUND, UOL, UNKNOWN_TYPE, UNKNOWN_EXTENDED_TYPE;
}

View File

@@ -1,151 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.PixelInterleavedSampleModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import provider.MapleCanvas;
public class PNGMapleCanvas implements MapleCanvas {
private static final int[] ZAHLEN = new int[]{2, 1, 0, 3};
private int height;
private int width;
private int dataLength;
private int format;
private byte[] data;
public PNGMapleCanvas(int width, int height, int dataLength, int format, byte[] data) {
super();
this.height = height;
this.width = width;
this.dataLength = dataLength;
this.format = format;
this.data = data;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public int getFormat() {
return format;
}
private byte[] getData() {
return data;
}
@Override
public BufferedImage getImage() {
int sizeUncompressed = 0;
int size8888 = 0;
int maxWriteBuf = 2;
int maxHeight = 3;
byte[] writeBuf = new byte[maxWriteBuf];
@SuppressWarnings ("unused")
byte[] rowPointers = new byte[maxHeight];
switch (getFormat()) {
case 1:
case 513:
sizeUncompressed = getHeight() * getWidth() * 4;
break;
case 2:
sizeUncompressed = getHeight() * getWidth() * 8;
break;
case 517:
sizeUncompressed = getHeight() * getWidth() / 128;
break;
}
size8888 = getHeight() * getWidth() * 8;
if (size8888 > maxWriteBuf) {
maxWriteBuf = size8888;
writeBuf = new byte[maxWriteBuf];
}
if (getHeight() > maxHeight) {
maxHeight = getHeight();
rowPointers = new byte[maxHeight];
}
Inflater dec = new Inflater();
dec.setInput(getData(), 0, dataLength);
int declen = 0;
byte[] uc = new byte[sizeUncompressed];
try {
declen = dec.inflate(uc);
} catch (DataFormatException ex) {
throw new RuntimeException("zlib fucks", ex);
}
dec.end();
if (getFormat() == 1) {
for (int i = 0; i < sizeUncompressed; i++) {
byte low = (byte) (uc[i] & 0x0F);
byte high = (byte) (uc[i] & 0xF0);
writeBuf[(i << 1)] = (byte) (((low << 4) | low) & 0xFF);
writeBuf[(i << 1) + 1] = (byte) (high | ((high >>> 4) & 0xF));
}
} else if (getFormat() == 2) {
writeBuf = uc;
} else if (getFormat() == 513) {
for (int i = 0; i < declen; i += 2) {
byte bBits = (byte) ((uc[i] & 0x1F) << 3);
byte gBits = (byte) (((uc[i + 1] & 0x07) << 5) | ((uc[i] & 0xE0) >> 3));
byte rBits = (byte) (uc[i + 1] & 0xF8);
writeBuf[(i << 1)] = (byte) (bBits | (bBits >> 5));
writeBuf[(i << 1) + 1] = (byte) (gBits | (gBits >> 6));
writeBuf[(i << 1) + 2] = (byte) (rBits | (rBits >> 5));
writeBuf[(i << 1) + 3] = (byte) 0xFF;
}
} else if (getFormat() == 517) {
byte b = 0x00;
int pixelIndex = 0;
for (int i = 0; i < declen; i++) {
for (int j = 0; j < 8; j++) {
b = (byte) (((uc[i] & (0x01 << (7 - j))) >> (7 - j)) * 255);
for (int k = 0; k < 16; k++) {
pixelIndex = (i << 9) + (j << 6) + k * 2;
writeBuf[pixelIndex] = b;
writeBuf[pixelIndex + 1] = b;
writeBuf[pixelIndex + 2] = b;
writeBuf[pixelIndex + 3] = (byte) 0xFF;
}
}
}
}
DataBufferByte imgData = new DataBufferByte(writeBuf, sizeUncompressed);
SampleModel sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, getWidth(), getHeight(), 4, getWidth() * 4, ZAHLEN);
WritableRaster imgRaster = Raster.createWritableRaster(sm, imgData, new Point(0, 0));
BufferedImage aa = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
aa.setData(imgRaster);
return aa;
}
}

View File

@@ -1,68 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataEntity;
import provider.MapleDataEntry;
import provider.MapleDataFileEntry;
public class WZDirectoryEntry extends WZEntry implements MapleDataDirectoryEntry {
private List<MapleDataDirectoryEntry> subdirs = new ArrayList<MapleDataDirectoryEntry>();
private List<MapleDataFileEntry> files = new ArrayList<MapleDataFileEntry>();
private Map<String, MapleDataEntry> entries = new HashMap<String, MapleDataEntry>();
public WZDirectoryEntry(String name, int size, int checksum, MapleDataEntity parent) {
super(name, size, checksum, parent);
}
public WZDirectoryEntry() {
super(null, 0, 0, null);
}
public void addDirectory(MapleDataDirectoryEntry dir) {
subdirs.add(dir);
entries.put(dir.getName(), dir);
}
public void addFile(MapleDataFileEntry fileEntry) {
files.add(fileEntry);
entries.put(fileEntry.getName(), fileEntry);
}
public List<MapleDataDirectoryEntry> getSubdirectories() {
return Collections.unmodifiableList(subdirs);
}
public List<MapleDataFileEntry> getFiles() {
return Collections.unmodifiableList(files);
}
public MapleDataEntry getEntry(String name) {
return entries.get(name);
}
}

View File

@@ -1,61 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import provider.MapleDataEntity;
import provider.MapleDataEntry;
public class WZEntry implements MapleDataEntry {
private String name;
private int size;
private int checksum;
private int offset;
private MapleDataEntity parent;
public WZEntry(String name, int size, int checksum, MapleDataEntity parent) {
super();
this.name = name;
this.size = size;
this.checksum = checksum;
this.parent = parent;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public int getChecksum() {
return checksum;
}
public int getOffset() {
return offset;
}
public MapleDataEntity getParent() {
return parent;
}
}

View File

@@ -1,154 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import provider.MapleData;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataFileEntry;
import provider.MapleDataProvider;
import tools.data.input.GenericLittleEndianAccessor;
import tools.data.input.GenericSeekableLittleEndianAccessor;
import tools.data.input.InputStreamByteStream;
import tools.data.input.LittleEndianAccessor;
import tools.data.input.RandomAccessByteStream;
import tools.data.input.SeekableLittleEndianAccessor;
public class WZFile implements MapleDataProvider {
static {
ListWZFile.init();
}
private File wzfile;
private LittleEndianAccessor lea;
private SeekableLittleEndianAccessor slea;
private int headerSize;
private WZDirectoryEntry root;
private boolean provideImages;
private int cOffset;
public WZFile(File wzfile, boolean provideImages) throws IOException {
this.wzfile = wzfile;
lea = new GenericLittleEndianAccessor(new InputStreamByteStream(new BufferedInputStream(new FileInputStream(wzfile))));
RandomAccessFile raf = new RandomAccessFile(wzfile, "r");
slea = new GenericSeekableLittleEndianAccessor(new RandomAccessByteStream(raf));
root = new WZDirectoryEntry(wzfile.getName(), 0, 0, null);
this.provideImages = provideImages;
load();
}
private void load() throws IOException {
lea.readAsciiString(4);
lea.readInt();
lea.readInt();
headerSize = lea.readInt();
lea.readNullTerminatedAsciiString();
lea.readShort();
parseDirectory(root);
cOffset = (int) lea.getBytesRead();
getOffsets(root);
}
private void getOffsets(MapleDataDirectoryEntry dir) {
for (MapleDataFileEntry file : dir.getFiles()) {
file.setOffset(cOffset);
cOffset += file.getSize();
}
for (MapleDataDirectoryEntry sdir : dir.getSubdirectories()) {
getOffsets(sdir);
}
}
private void parseDirectory(WZDirectoryEntry dir) {
int entries = WZTool.readValue(lea);
for (int i = 0; i < entries; i++) {
byte marker = lea.readByte();
String name = null;
int size, checksum;
switch (marker) {
case 0x02:
name = WZTool.readDecodedStringAtOffsetAndReset(slea, lea.readInt() + this.headerSize + 1);
size = WZTool.readValue(lea);
checksum = WZTool.readValue(lea);
lea.readInt(); //dummy int
dir.addFile(new WZFileEntry(name, size, checksum, dir));
break;
case 0x03:
case 0x04:
name = WZTool.readDecodedString(lea);
size = WZTool.readValue(lea);
checksum = WZTool.readValue(lea);
lea.readInt(); //dummy int
if (marker == 3) {
dir.addDirectory(new WZDirectoryEntry(name, size, checksum, dir));
} else {
dir.addFile(new WZFileEntry(name, size, checksum, dir));
}
break;
default:
}
}
for (MapleDataDirectoryEntry idir : dir.getSubdirectories()) {
parseDirectory((WZDirectoryEntry) idir);
}
}
public WZIMGFile getImgFile(String path) throws IOException {
String segments[] = path.split("/");
WZDirectoryEntry dir = root;
for (int x = 0; x < segments.length - 1; x++) {
dir = (WZDirectoryEntry) dir.getEntry(segments[x]);
if (dir == null) {
return null;
}
}
WZFileEntry entry = (WZFileEntry) dir.getEntry(segments[segments.length - 1]);
if (entry == null) {
return null;
}
String fullPath = wzfile.getName().substring(0, wzfile.getName().length() - 3).toLowerCase() + "/" + path;
return new WZIMGFile(this.wzfile, entry, provideImages, ListWZFile.isModernImgFile(fullPath));
}
@Override
public synchronized MapleData getData(String path) {
try {
WZIMGFile imgFile = getImgFile(path);
if (imgFile == null) {
return null;
}
MapleData ret = imgFile.getRoot();
return ret;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public MapleDataDirectoryEntry getRoot() {
return root;
}
}

View File

@@ -1,42 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import provider.MapleDataEntity;
import provider.MapleDataFileEntry;
public class WZFileEntry extends WZEntry implements MapleDataFileEntry {
private int offset;
public WZFileEntry(String name, int size, int checksum, MapleDataEntity parent) {
super(name, size, checksum, parent);
}
@Override
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
}

View File

@@ -1,118 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import provider.MapleData;
import provider.MapleDataEntity;
public class WZIMGEntry implements MapleData {
private String name;
private MapleDataType type;
private List<MapleData> children = new ArrayList<MapleData>(10);
private Object data;
private MapleDataEntity parent;
public WZIMGEntry(MapleDataEntity parent) {
this.parent = parent;
}
@Override
public String getName() {
return name;
}
@Override
public MapleDataType getType() {
return type;
}
@Override
public List<MapleData> getChildren() {
return Collections.unmodifiableList(children);
}
@Override
public MapleData getChildByPath(String path) {
String segments[] = path.split("/");
if (segments[0].equals("..")) {
return ((MapleData) getParent()).getChildByPath(path.substring(path.indexOf("/") + 1));
}
MapleData ret = this;
for (int x = 0; x < segments.length; x++) {
boolean foundChild = false;
for (MapleData child : ret.getChildren()) {
if (child.getName().equals(segments[x])) {
ret = child;
foundChild = true;
break;
}
}
if (!foundChild) {
return null;
}
}
return ret;
}
@Override
public Object getData() {
return data;
}
public void setName(String name) {
this.name = name;
}
public void setType(MapleDataType type) {
this.type = type;
}
public void setData(Object data) {
this.data = data;
}
public void addChild(WZIMGEntry entry) {
children.add(entry);
}
@Override
public Iterator<MapleData> iterator() {
return getChildren().iterator();
}
@Override
public String toString() {
return getName() + ":" + getData();
}
public MapleDataEntity getParent() {
return parent;
}
public void finish() {
((ArrayList<MapleData>) children).trimToSize();
}
}

View File

@@ -1,227 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.awt.Point;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import tools.data.input.GenericSeekableLittleEndianAccessor;
import tools.data.input.RandomAccessByteStream;
import tools.data.input.SeekableLittleEndianAccessor;
public class WZIMGFile {
private WZFileEntry file;
private WZIMGEntry root;
private boolean provideImages;
@SuppressWarnings ("unused")
private boolean modernImg;
public WZIMGFile(File wzfile, WZFileEntry file, boolean provideImages, boolean modernImg) throws IOException {
RandomAccessFile raf = new RandomAccessFile(wzfile, "r");
SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new RandomAccessByteStream(raf));
slea.seek(file.getOffset());
this.file = file;
this.provideImages = provideImages;
root = new WZIMGEntry(file.getParent());
root.setName(file.getName());
root.setType(MapleDataType.EXTENDED);
this.modernImg = modernImg;
parseExtended(root, slea, 0);
root.finish();
raf.close();
}
protected void dumpImg(OutputStream out, SeekableLittleEndianAccessor slea) throws IOException {
DataOutputStream os = new DataOutputStream(out);
long oldPos = slea.getPosition();
slea.seek(file.getOffset());
for (int x = 0; x < file.getSize(); x++) {
os.write(slea.readByte());
}
slea.seek(oldPos);
}
public WZIMGEntry getRoot() {
return root;
}
private void parse(WZIMGEntry entry, SeekableLittleEndianAccessor slea) {
byte marker = slea.readByte();
switch (marker) {
case 0: {
String name = WZTool.readDecodedString(slea);
entry.setName(name);
break;
}
case 1: {
String name = WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt());
entry.setName(name);
break;
}
default:
System.out.println("Unknown Image identifier: " + marker + " at offset " + (slea.getPosition() - file.getOffset()));
}
marker = slea.readByte();
switch (marker) {
case 0:
entry.setType(MapleDataType.IMG_0x00);
break;
case 2:
case 11: //??? no idea, since 0.49
entry.setType(MapleDataType.SHORT);
entry.setData(Short.valueOf(slea.readShort()));
break;
case 3:
entry.setType(MapleDataType.INT);
entry.setData(Integer.valueOf(WZTool.readValue(slea)));
break;
case 4:
entry.setType(MapleDataType.FLOAT);
entry.setData(Float.valueOf(WZTool.readFloatValue(slea)));
break;
case 5:
entry.setType(MapleDataType.DOUBLE);
entry.setData(Double.valueOf(slea.readDouble()));
break;
case 8:
entry.setType(MapleDataType.STRING);
byte iMarker = slea.readByte();
if (iMarker == 0) {
entry.setData(WZTool.readDecodedString(slea));
} else if (iMarker == 1) {
entry.setData(WZTool.readDecodedStringAtOffsetAndReset(slea, slea.readInt() + file.getOffset()));
} else {
System.out.println("Unknown String type " + iMarker);
}
break;
case 9:
entry.setType(MapleDataType.EXTENDED);
long endOfExtendedBlock = slea.readInt();
endOfExtendedBlock += slea.getPosition();
parseExtended(entry, slea, endOfExtendedBlock);
break;
default:
System.out.println("Unknown Image type " + marker);
}
}
private void parseExtended(WZIMGEntry entry, SeekableLittleEndianAccessor slea, long endOfExtendedBlock) {
byte marker = slea.readByte();
String type;
switch (marker) {
case 0x73:
type = WZTool.readDecodedString(slea);
break;
case 0x1B:
type = WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt());
break;
default:
throw new RuntimeException("Unknown extended image identifier: " + marker + " at offset " +
(slea.getPosition() - file.getOffset()));
}
if (type.equals("Property")) {
entry.setType(MapleDataType.PROPERTY);
slea.readByte();
slea.readByte();
int children = WZTool.readValue(slea);
for (int i = 0; i < children; i++) {
WZIMGEntry cEntry = new WZIMGEntry(entry);
parse(cEntry, slea);
cEntry.finish();
entry.addChild(cEntry);
}
} else if (type.equals("Canvas")) {
entry.setType(MapleDataType.CANVAS);
slea.readByte();
marker = slea.readByte();
if (marker == 0) {
// do nothing
} else if (marker == 1) {
slea.readByte();
slea.readByte();
int children = WZTool.readValue(slea);
for (int i = 0; i < children; i++) {
WZIMGEntry child = new WZIMGEntry(entry);
parse(child, slea);
child.finish();
entry.addChild(child);
}
} else {
System.out.println("Canvas marker != 1 (" + marker + ")");
}
int width = WZTool.readValue(slea);
int height = WZTool.readValue(slea);
int format = WZTool.readValue(slea);
int format2 = slea.readByte();
slea.readInt();
int dataLength = slea.readInt() - 1;
slea.readByte();
if (provideImages) {
byte[] pngdata = slea.read(dataLength);
entry.setData(new PNGMapleCanvas(width, height, dataLength, format + format2, pngdata));
} else {
entry.setData(new PNGMapleCanvas(width, height, dataLength, format + format2, null));
slea.skip(dataLength);
}
} else if (type.equals("Shape2D#Vector2D")) {
entry.setType(MapleDataType.VECTOR);
int x = WZTool.readValue(slea);
int y = WZTool.readValue(slea);
entry.setData(new Point(x, y));
} else if (type.equals("Shape2D#Convex2D")) {
int children = WZTool.readValue(slea);
for (int i = 0; i < children; i++) {
WZIMGEntry cEntry = new WZIMGEntry(entry);
parseExtended(cEntry, slea, 0);
cEntry.finish();
entry.addChild(cEntry);
}
} else if (type.equals("Sound_DX8")) {
entry.setType(MapleDataType.SOUND);
slea.readByte();
int dataLength = WZTool.readValue(slea);
WZTool.readValue(slea); // no clue what this is
int offset = (int) slea.getPosition();
entry.setData(new ImgMapleSound(dataLength, offset - file.getOffset()));
slea.seek(endOfExtendedBlock);
} else if (type.equals("UOL")) {
entry.setType(MapleDataType.UOL);
slea.readByte();
byte uolmarker = slea.readByte();
switch (uolmarker) {
case 0:
entry.setData(WZTool.readDecodedString(slea));
break;
case 1:
entry.setData(WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt()));
break;
default:
System.out.println("Unknown UOL marker: " + uolmarker + " " + entry.getName());
}
} else {
throw new RuntimeException("Unhandled extended type: " + type);
}
}
}

View File

@@ -1,188 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import tools.data.input.LittleEndianAccessor;
import tools.data.input.SeekableLittleEndianAccessor;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/*
* Ported Code, see WZFile.java for more info
*/
public class WZTool {
private static byte[] encKey;
static {
byte[] iv = new byte[]{(byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,
(byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,
(byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,
(byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,};
byte[] key = new byte[]{(byte) 0x13, 0x00, 0x00, 0x00,
(byte) 0x08, 0x00, 0x00, 0x00,
(byte) 0x06, 0x00, 0x00, 0x00,
(byte) 0xB4, 0x00, 0x00, 0x00,
(byte) 0x1B, 0x00, 0x00, 0x00,
(byte) 0x0F, 0x00, 0x00, 0x00,
(byte) 0x33, 0x00, 0x00, 0x00,
(byte) 0x52, 0x00, 0x00, 0x00
};
Cipher cipher = null;
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
try {
cipher = Cipher.getInstance("AES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
}
try {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
} catch (InvalidKeyException e) {
e.printStackTrace();
}
encKey = new byte[0xFFFF];
for (int i = 0; i < (0xFFFF / 16); i++) {
try {
iv = cipher.doFinal(iv);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
System.arraycopy(iv, 0, encKey, (i * 16), 16);
}
try {
iv = cipher.doFinal(iv);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
}
System.arraycopy(iv, 0, encKey, 65520, 15);
}
public static byte[] readListString(byte[] str) {
for (int i = 0; i < str.length; i++) {
str[i] = (byte) (str[i] ^ encKey[i]);
}
return str;
}
public static String readDecodedString(LittleEndianAccessor llea) {
int strLength;
byte b = llea.readByte();
if (b == 0x00) {
return "";
}
if (b >= 0) {
if (b == 0x7F) {
strLength = llea.readInt();
} else {
strLength = b;
}
if (strLength < 0) {
return "";
}
byte str[] = new byte[strLength * 2];
for (int i = 0; i < strLength * 2; i++) {
str[i] = llea.readByte();
}
return DecryptUnicodeStr(str);
} else {
if (b == -128) {
strLength = llea.readInt();
} else {
strLength = -b;
}
if (strLength < 0) {
return "";
}
byte str[] = new byte[strLength];
for (int i = 0; i < strLength; i++) {
str[i] = llea.readByte();
}
return DecryptAsciiStr(str);
}
}
public static String DecryptAsciiStr(byte[] str) {
byte xorByte = (byte) 0xAA;
for (int i = 0; i < str.length; i++) {
str[i] = (byte) (str[i] ^ xorByte ^ encKey[i]);
xorByte++;
}
return new String(str);
}
public static String DecryptUnicodeStr(byte[] str) {
int xorByte = 0xAAAA;
char[] charRet = new char[str.length / 2];
for (int i = 0; i < str.length; i++) {
str[i] = (byte) (str[i] ^ encKey[i]);
}
for (int i = 0; i < (str.length / 2); i++) {
char toXor = (char) ((str[i] << 8) | str[i + 1]);
charRet[i] = (char) (toXor ^ xorByte);
xorByte++;
}
return String.valueOf(charRet);
}
public static String readDecodedStringAtOffset(SeekableLittleEndianAccessor slea, int offset) {
slea.seek(offset);
return readDecodedString(slea);
}
public static String readDecodedStringAtOffsetAndReset(SeekableLittleEndianAccessor slea, int offset) {
long pos = 0;
pos = slea.getPosition();
slea.seek(offset);
String ret = readDecodedString(slea);
slea.seek(pos);
return ret;
}
public static int readValue(LittleEndianAccessor lea) {
byte b = lea.readByte();
if (b == -128) {
return lea.readInt();
} else {
return b;
}
}
public static float readFloatValue(LittleEndianAccessor lea) {
byte b = lea.readByte();
if (b == -128) {
return lea.readFloat();
} else {
return 0;
}
}
}

View File

@@ -1,219 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.awt.Point;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.text.NumberFormat;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import provider.MapleData;
import provider.MapleDataEntity;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class XMLDomMapleData implements MapleData {
private Node node;
private File imageDataDir;
private NumberFormat nf;
public XMLDomMapleData(FileInputStream fis, File imageDataDir) {
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(fis);
this.node = document.getFirstChild();
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
this.imageDataDir = imageDataDir;
this.nf = NumberFormat.getInstance(Locale.FRANCE);
}
private XMLDomMapleData(Node node) {
this.node = node;
this.nf = NumberFormat.getInstance(Locale.FRANCE);
}
@Override
public MapleData getChildByPath(String path) {
String segments[] = path.split("/");
if (segments[0].equals("..")) {
return ((MapleData) getParent()).getChildByPath(path.substring(path.indexOf("/") + 1));
}
Node myNode = node;
for (int x = 0; x < segments.length; x++) {
NodeList childNodes = myNode.getChildNodes();
boolean foundChild = false;
for (int i = 0; i < childNodes.getLength(); i++) {
Node childNode = childNodes.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE && childNode.getAttributes().getNamedItem("name").getNodeValue().equals(segments[x])) {
myNode = childNode;
foundChild = true;
break;
}
}
if (!foundChild) {
return null;
}
}
XMLDomMapleData ret = new XMLDomMapleData(myNode);
ret.imageDataDir = new File(imageDataDir, getName() + "/" + path).getParentFile();
return ret;
}
@Override
public List<MapleData> getChildren() {
List<MapleData> ret = new ArrayList<MapleData>();
NodeList childNodes = node.getChildNodes();
for (int i = 0; i < childNodes.getLength(); i++) {
Node childNode = childNodes.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
XMLDomMapleData child = new XMLDomMapleData(childNode);
child.imageDataDir = new File(imageDataDir, getName());
ret.add(child);
}
}
return ret;
}
@Override
public Object getData() {
NamedNodeMap attributes = node.getAttributes();
MapleDataType type = getType();
switch (type) {
case DOUBLE:
case FLOAT:
case INT:
case SHORT: {
String value = attributes.getNamedItem("value").getNodeValue();
Number nval;
try {
nval = nf.parse(value);
}
catch(java.text.ParseException pe) {
pe.printStackTrace();
nval = 0.0f;
}
switch (type) {
case DOUBLE:
return nval.doubleValue();
case FLOAT:
return nval.floatValue();
case INT:
return nval.intValue();
case SHORT:
return nval.shortValue();
default:
return null;
}
}
case STRING:
case UOL: {
String value = attributes.getNamedItem("value").getNodeValue();
return value;
}
case VECTOR: {
String x = attributes.getNamedItem("x").getNodeValue();
String y = attributes.getNamedItem("y").getNodeValue();
return new Point(Integer.parseInt(x), Integer.parseInt(y));
}
case CANVAS: {
String width = attributes.getNamedItem("width").getNodeValue();
String height = attributes.getNamedItem("height").getNodeValue();
return new FileStoredPngMapleCanvas(Integer.parseInt(width), Integer.parseInt(height), new File(
imageDataDir, getName() + ".png"));
}
default:
return null;
}
}
@Override
public MapleDataType getType() {
String nodeName = node.getNodeName();
if (nodeName.equals("imgdir")) {
return MapleDataType.PROPERTY;
} else if (nodeName.equals("canvas")) {
return MapleDataType.CANVAS;
} else if (nodeName.equals("convex")) {
return MapleDataType.CONVEX;
} else if (nodeName.equals("sound")) {
return MapleDataType.SOUND;
} else if (nodeName.equals("uol")) {
return MapleDataType.UOL;
} else if (nodeName.equals("double")) {
return MapleDataType.DOUBLE;
} else if (nodeName.equals("float")) {
return MapleDataType.FLOAT;
} else if (nodeName.equals("int")) {
return MapleDataType.INT;
} else if (nodeName.equals("short")) {
return MapleDataType.SHORT;
} else if (nodeName.equals("string")) {
return MapleDataType.STRING;
} else if (nodeName.equals("vector")) {
return MapleDataType.VECTOR;
} else if (nodeName.equals("null")) {
return MapleDataType.IMG_0x00;
}
return null;
}
@Override
public MapleDataEntity getParent() {
Node parentNode = node.getParentNode();
if (parentNode.getNodeType() == Node.DOCUMENT_NODE) {
return null;
}
XMLDomMapleData parentData = new XMLDomMapleData(parentNode);
parentData.imageDataDir = imageDataDir.getParentFile();
return parentData;
}
@Override
public String getName() {
return node.getAttributes().getNamedItem("name").getNodeValue();
}
@Override
public Iterator<MapleData> iterator() {
return getChildren().iterator();
}
}

View File

@@ -1,85 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package provider.wz;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import provider.MapleData;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataProvider;
public class XMLWZFile implements MapleDataProvider {
private File root;
private WZDirectoryEntry rootForNavigation;
public XMLWZFile(File fileIn) {
root = fileIn;
rootForNavigation = new WZDirectoryEntry(fileIn.getName(), 0, 0, null);
fillMapleDataEntitys(root, rootForNavigation);
}
private void fillMapleDataEntitys(File lroot, WZDirectoryEntry wzdir) {
for (File file : lroot.listFiles()) {
String fileName = file.getName();
if (file.isDirectory() && !fileName.endsWith(".img")) {
WZDirectoryEntry newDir = new WZDirectoryEntry(fileName, 0, 0, wzdir);
wzdir.addDirectory(newDir);
fillMapleDataEntitys(file, newDir);
} else if (fileName.endsWith(".xml")) {
wzdir.addFile(new WZFileEntry(fileName.substring(0, fileName.length() - 4), 0, 0, wzdir));
}
}
}
@Override
public MapleData getData(String path) {
File dataFile = new File(root, path + ".xml");
File imageDataDir = new File(root, path);
if (!dataFile.exists()) {
return null;//bitches
}
FileInputStream fis;
try {
fis = new FileInputStream(dataFile);
} catch (FileNotFoundException e) {
throw new RuntimeException("Datafile " + path + " does not exist in " + root.getAbsolutePath());
}
final XMLDomMapleData domMapleData;
try {
domMapleData = new XMLDomMapleData(fis, imageDataDir.getParentFile());
} finally {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return domMapleData;
}
@Override
public MapleDataDirectoryEntry getRoot() {
return rootForNavigation;
}
}

View File

@@ -1,51 +0,0 @@
package tools;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @author Frz (Big Daddy)
* @author The Real Spookster - some modifications to this beautiful code
*/
public class DatabaseConnection {
private static String DB_URL = "jdbc:mysql://localhost:3306/cosmic";
private static String DB_USER = "cosmic_server";
private static String DB_PASS = "snailshell";
public static final int RETURN_GENERATED_KEYS = 1;
private static ThreadLocal<Connection> con = new ThreadLocalConnection();
public static Connection getConnection() {
Connection c = con.get();
try {
c.getMetaData();
} catch (SQLException e) { // connection is dead, therefore discard old object 5ever
con.remove();
c = con.get();
}
return c;
}
private static class ThreadLocalConnection extends ThreadLocal<Connection> {
@Override
protected Connection initialValue() {
try {
Class.forName("com.mysql.jdbc.Driver"); // touch the mysql driver
} catch (ClassNotFoundException e) {
System.out.println("[SEVERE] SQL Driver Not Found. Consider death by clams.");
e.printStackTrace();
return null;
}
try {
return DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
} catch (SQLException e) {
System.out.println("[SEVERE] Unable to make database connection.");
e.printStackTrace();
return null;
}
}
}
}

View File

@@ -1,79 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools;
import java.io.ByteArrayOutputStream;
public class HexTool {
private static final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
private static String toString(byte byteValue) {
int tmp = byteValue << 8;
char[] retstr = new char[]{HEX[(tmp >> 12) & 0x0F], HEX[(tmp >> 8) & 0x0F]};
return String.valueOf(retstr);
}
public static String toString(byte[] bytes) {
StringBuilder hexed = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
hexed.append(toString(bytes[i]));
hexed.append(' ');
}
return hexed.substring(0, hexed.length() - 1);
}
public static byte[] getByteArrayFromHexString(String hex) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int nexti = 0;
int nextb = 0;
boolean highoc = true;
outer:
for (;;) {
int number = -1;
while (number == -1) {
if (nexti == hex.length()) {
break outer;
}
char chr = hex.charAt(nexti);
if (chr >= '0' && chr <= '9') {
number = chr - '0';
} else if (chr >= 'a' && chr <= 'f') {
number = chr - 'a' + 10;
} else if (chr >= 'A' && chr <= 'F') {
number = chr - 'A' + 10;
} else {
number = -1;
}
nexti++;
}
if (highoc) {
nextb = number << 4;
highoc = false;
} else {
nextb |= number;
highoc = true;
baos.write(nextb);
}
}
return baos.toByteArray();
}
}

View File

@@ -1,121 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 ~ 2010 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation. You may not use, modify
or distribute this program under any other version of the
GNU Affero General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools;
/**
* Represents a pair of values.
*
* @author Frz
* @since Revision 333
* @version 1.0
*
* @param <E> The type of the left value.
* @param <F> The type of the right value.
*/
public class Pair<E, F> {
public E left;
public F right;
/**
* Class constructor - pairs two objects together.
*
* @param left The left object.
* @param right The right object.
*/
public Pair(E left, F right) {
this.left = left;
this.right = right;
}
/**
* Gets the left value.
*
* @return The left value.
*/
public E getLeft() {
return left;
}
/**
* Gets the right value.
*
* @return The right value.
*/
public F getRight() {
return right;
}
/**
* Turns the pair into a string.
*
* @return Each value of the pair as a string joined with a colon.
*/
@Override
public String toString() {
return left.toString() + ":" + right.toString();
}
/**
* Gets the hash code of this pair.
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((left == null) ? 0 : left.hashCode());
result = prime * result + ((right == null) ? 0 : right.hashCode());
return result;
}
/**
* Checks to see if two pairs are equal.
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Pair other = (Pair) obj;
if (left == null) {
if (other.left != null) {
return false;
}
} else if (!left.equals(other.left)) {
return false;
}
if (right == null) {
if (other.right != null) {
return false;
}
} else if (!right.equals(other.right)) {
return false;
}
return true;
}
}

View File

@@ -1,72 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.data.input;
import java.io.IOException;
import tools.HexTool;
public class ByteArrayByteStream implements SeekableInputStreamBytestream {
private int pos = 0;
private long bytesRead = 0;
private byte[] arr;
public ByteArrayByteStream(byte[] arr) {
this.arr = arr;
}
@Override
public long getPosition() {
return pos;
}
@Override
public void seek(long offset) throws IOException {
pos = (int) offset;
}
@Override
public long getBytesRead() {
return bytesRead;
}
@Override
public int readByte() {
bytesRead++;
return ((int) arr[pos++]) & 0xFF;
}
@Override
public String toString() {
String nows = "kevintjuh93 pwns";//I lol'd
if (arr.length - pos > 0) {
byte[] now = new byte[arr.length - pos];
System.arraycopy(arr, pos, now, 0, arr.length - pos);
nows = HexTool.toString(now);
}
return "All: " + HexTool.toString(arr) + "\nNow: " + nows;
}
@Override
public long available() {
return arr.length - pos;
}
}

View File

@@ -1,35 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.data.input;
/**
* Represents an abstract stream of bytes.
*
* @author Frz
* @version 1.0
* @since Revision 323
*/
public interface ByteInputStream {
int readByte();
long getBytesRead();
long available();
}

View File

@@ -1,239 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.data.input;
import java.awt.Point;
import java.io.ByteArrayOutputStream;
/**
* Provides a generic interface to a Little Endian stream of bytes.
*
* @version 1.0
* @author Frz
* @since Revision 323
*/
public class GenericLittleEndianAccessor implements LittleEndianAccessor {
private ByteInputStream bs;
/**
* Class constructor - Wraps the accessor around a stream of bytes.
*
* @param bs The byte stream to wrap the accessor around.
*/
public GenericLittleEndianAccessor(ByteInputStream bs) {
this.bs = bs;
}
/**
* Read a single byte from the stream.
*
* @return The byte read.
* @see tools.data.input.ByteInputStream#readByte
*/
@Override
public byte readByte() {
return (byte) bs.readByte();
}
/**
* Reads an integer from the stream.
*
* @return The integer read.
*/
@Override
public int readInt() {
return bs.readByte() + (bs.readByte() << 8) + (bs.readByte() << 16) + (bs.readByte() << 24);
}
/**
* Reads a short integer from the stream.
*
* @return The short read.
*/
@Override
public short readShort() {
return (short) (bs.readByte() + (bs.readByte() << 8));
}
/**
* Reads a single character from the stream.
*
* @return The character read.
*/
@Override
public char readChar() {
return (char) readShort();
}
/**
* Reads a long integer from the stream.
*
* @return The long integer read.
*/
@Override
public long readLong() {
long byte1 = bs.readByte();
long byte2 = bs.readByte();
long byte3 = bs.readByte();
long byte4 = bs.readByte();
long byte5 = bs.readByte();
long byte6 = bs.readByte();
long byte7 = bs.readByte();
long byte8 = bs.readByte();
return (byte8 << 56) + (byte7 << 48) + (byte6 << 40) + (byte5 << 32) + (byte4 << 24) + (byte3 << 16) + (byte2 << 8) + byte1;
}
/**
* Reads a floating point integer from the stream.
*
* @return The float-type integer read.
*/
@Override
public float readFloat() {
return Float.intBitsToFloat(readInt());
}
/**
* Reads a double-precision integer from the stream.
*
* @return The double-type integer read.
*/
@Override
public double readDouble() {
return Double.longBitsToDouble(readLong());
}
/**
* Reads an ASCII string from the stream with length <code>n</code>.
*
* @param n Number of characters to read.
* @return The string read.
*/
public final String readAsciiString(int n) {
char ret[] = new char[n];
for (int x = 0; x < n; x++) {
ret[x] = (char) readByte();
}
return String.valueOf(ret);
}
/**
* Reads a null-terminated string from the stream.
*
* @return The string read.
*/
public final String readNullTerminatedAsciiString() {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte b;
while (true) {
b = readByte();
if (b == 0) {
break;
}
baos.write(b);
}
byte[] buf = baos.toByteArray();
char[] chrBuf = new char[buf.length];
for (int x = 0; x < buf.length; x++) {
chrBuf[x] = (char) buf[x];
}
return String.valueOf(chrBuf);
}
/**
* Gets the number of bytes read from the stream so far.
*
* @return A long integer representing the number of bytes read.
* @see tools.data.input.ByteInputStream#getBytesRead()
*/
public long getBytesRead() {
return bs.getBytesRead();
}
/**
* Reads a MapleStory convention lengthed ASCII string.
* This consists of a short integer telling the length of the string,
* then the string itself.
*
* @return The string read.
*/
@Override
public String readMapleAsciiString() {
return readAsciiString(readShort());
}
/**
* Reads <code>num</code> bytes off the stream.
*
* @param num The number of bytes to read.
* @return An array of bytes with the length of <code>num</code>
*/
@Override
public byte[] read(int num) {
byte[] ret = new byte[num];
for (int x = 0; x < num; x++) {
ret[x] = readByte();
}
return ret;
}
/**
* Reads a MapleStory Position information.
* This consists of 2 short integer.
*
* @return The Position read.
*/
@Override
public final Point readPos() {
final int x = readShort();
final int y = readShort();
return new Point(x, y);
}
/**
* Skips the current position of the stream <code>num</code> bytes ahead.
*
* @param num Number of bytes to skip.
*/
@Override
public void skip(int num) {
for (int x = 0; x < num; x++) {
readByte();
}
}
/**
* @see tools.data.input.ByteInputStream#available
*/
@Override
public long available() {
return bs.available();
}
/**
* @see java.lang.Object#toString
*/
@Override
public String toString() {
return bs.toString();
}
}

View File

@@ -1,91 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package tools.data.input;
import java.io.IOException;
/**
* Provides an abstract accessor to a generic Little Endian byte stream. This
* accessor is seekable.
*
* @author Frz
* @version 1.0
* @since Revision 323
* @see tools.data.input.GenericLittleEndianAccessor
*/
public class GenericSeekableLittleEndianAccessor extends GenericLittleEndianAccessor implements SeekableLittleEndianAccessor {
private SeekableInputStreamBytestream bs;
/**
* Class constructor
* Provide a seekable input stream to wrap this object around.
*
* @param bs The byte stream to wrap this around.
*/
public GenericSeekableLittleEndianAccessor(SeekableInputStreamBytestream bs) {
super(bs);
this.bs = bs;
}
/**
* Seek the pointer to <code>offset</code>
*
* @param offset The offset to seek to.
* @see tools.data.input.SeekableInputStreamBytestream#seek
*/
@Override
public void seek(long offset) {
try {
bs.seek(offset);
} catch (IOException e) {
e.printStackTrace();
System.out.println("Seek failed " + e);
}
}
/**
* Get the current position of the pointer.
*
* @return The current position of the pointer as a long integer.
* @see tools.data.input.SeekableInputStreamBytestream#getPosition
*/
@Override
public long getPosition() {
try {
return bs.getPosition();
} catch (IOException e) {
e.printStackTrace();
System.out.println("getPosition failed" + e);
return -1;
}
}
/**
* Skip <code>num</code> number of bytes in the stream.
*
* @param num The number of bytes to skip.
*/
@Override
public void skip(int num) {
seek(getPosition() + num);
}
}

Some files were not shown because too many files have changed in this diff Show More