L2ClientDat decoder.

This commit is contained in:
MobiusDev
2018-05-13 13:35:03 +00:00
parent 574f600afa
commit 836beafd0f
211 changed files with 26666 additions and 0 deletions

View File

@ -0,0 +1,403 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.UIManager;
import javax.swing.filechooser.FileNameExtensionFilter;
import com.l2jmobius.actions.MassRecryptor;
import com.l2jmobius.actions.MassTxtPacker;
import com.l2jmobius.actions.MassTxtUnpacker;
import com.l2jmobius.actions.OpenDat;
import com.l2jmobius.clientcryptor.DatFile;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.config.ConfigDebug;
import com.l2jmobius.config.ConfigWindow;
import com.l2jmobius.data.GameDataName;
import com.l2jmobius.forms.JPopupTextArea;
import com.l2jmobius.util.DebugUtil;
import com.l2jmobius.util.Util;
import com.l2jmobius.xml.CryptVersionParser;
import com.l2jmobius.xml.Descriptor;
import com.l2jmobius.xml.DescriptorParser;
import com.l2jmobius.xml.DescriptorWriter;
public class L2ClientDat extends JFrame
{
private static JPopupTextArea textPaneLog;
private static JPopupTextArea textPaneMain;
private static JComboBox<String> jComboBoxChronicle;
private static JComboBox<String> jComboBoxDecrypt;
private static JComboBox<String> jComboBoxEncrypt;
private static File currentFileWindow;
public L2ClientDat()
{
setTitle("L2ClientDat decoder");
setMinimumSize(new Dimension(1000, 600));
this.setSize(new Dimension(ConfigWindow.WINDOW_WIDTH, ConfigWindow.WINDOW_HEIGHT));
getContentPane().setLayout(new BorderLayout());
setDefaultCloseOperation(3);
setLocationRelativeTo(null);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent evt)
{
ConfigWindow.save("WINDOW_HEIGHT", String.valueOf(L2ClientDat.this.getHeight()));
ConfigWindow.save("WINDOW_WIDTH", String.valueOf(L2ClientDat.this.getWidth()));
System.exit(0);
}
});
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BorderLayout());
JPanel buttonPane0 = new JPanel();
JLabel structureLabel = new JLabel("structure:");
buttonPane0.add(structureLabel);
jComboBoxChronicle = new JComboBox<>();
jComboBoxChronicle.setModel(new DefaultComboBoxModel<>(Util.getFilesNames("./structure/", ".xml")));
jComboBoxChronicle.setSelectedItem(ConfigWindow.CURRENT_CHRONICLE);
jComboBoxChronicle.addActionListener(e -> saveComboBox(jComboBoxChronicle, "CURRENT_CHRONICLE"));
buttonPane0.add(jComboBoxChronicle);
JLabel decryptLabel = new JLabel("decrypt:");
buttonPane0.add(decryptLabel);
jComboBoxDecrypt = new JComboBox<>();
jComboBoxDecrypt.setModel(new DefaultComboBoxModel<>(CryptVersionParser.getInstance().getDecryptKeys().keySet().toArray(new String[CryptVersionParser.getInstance().getDecryptKeys().keySet().size()])));
jComboBoxDecrypt.setSelectedItem(ConfigWindow.CURRENT_DECRYPT);
jComboBoxDecrypt.addActionListener(e -> saveComboBox(jComboBoxDecrypt, "CURRENT_DECRYPT"));
buttonPane0.add(jComboBoxDecrypt);
JLabel encryptLabel = new JLabel("encrypt:");
buttonPane0.add(encryptLabel);
jComboBoxEncrypt = new JComboBox<>();
jComboBoxEncrypt.setModel(new DefaultComboBoxModel<>(CryptVersionParser.getInstance().getEncryptKey().keySet().toArray(new String[CryptVersionParser.getInstance().getDecryptKeys().keySet().size()])));
jComboBoxEncrypt.setSelectedItem(ConfigWindow.CURRENT_ENCRYPT);
jComboBoxEncrypt.addActionListener(e -> saveComboBox(jComboBoxEncrypt, "CURRENT_ENCRYPT"));
buttonPane0.add(jComboBoxEncrypt);
buttonPane.add(buttonPane0, "First");
JPanel buttonPane1 = new JPanel();
JButton open = new JButton();
open.setText("Open");
open.addActionListener(this::openSelectFileWindow);
buttonPane1.add(open);
JButton save = new JButton();
save.setText("Save txt");
save.addActionListener(this::saveActionPerformed);
buttonPane1.add(save);
JButton saveAs = new JButton();
saveAs.setText("Save dat");
saveAs.addActionListener(this::saveAsActionPerformed);
buttonPane1.add(saveAs);
JButton massTxtUnpack = new JButton();
massTxtUnpack.setText("Extract all to txt");
massTxtUnpack.addActionListener(this::massTxtUnpackActionPerformed);
buttonPane1.add(massTxtUnpack);
JButton massTxtPack = new JButton();
massTxtPack.setText("Download all dat");
massTxtPack.addActionListener(this::massTxtPackActionPerformed);
buttonPane1.add(massTxtPack);
JButton massRecrypt = new JButton();
massRecrypt.setText("Patch dat");
massRecrypt.addActionListener(this::massRecryptActionPerformed);
buttonPane1.add(massRecrypt);
buttonPane.add(buttonPane1);
JSplitPane jsp = new JSplitPane(0, false);
jsp.setResizeWeight(0.7);
jsp.setOneTouchExpandable(true);
textPaneMain = new JPopupTextArea();
textPaneMain.setBackground(new Color(41, 49, 52));
textPaneMain.setForeground(Color.WHITE);
textPaneMain.setFont(new Font("Verdana", 1, 12));
JScrollPane jScrollPane1 = new JScrollPane();
jScrollPane1.setAutoscrolls(true);
jScrollPane1.setViewportView(textPaneMain);
jsp.setTopComponent(jScrollPane1);
textPaneLog = new JPopupTextArea();
textPaneLog.setBackground(new Color(41, 49, 52));
textPaneLog.setForeground(Color.GREEN);
textPaneLog.setEditable(false);
JScrollPane jScrollPane2 = new JScrollPane();
jScrollPane2.setViewportView(textPaneLog);
jScrollPane2.setAutoscrolls(true);
jsp.setBottomComponent(jScrollPane2);
getContentPane().add(buttonPane, "First");
getContentPane().add(jsp);
pack();
setVisible(true);
}
private void massTxtPackActionPerformed(ActionEvent evt)
{
JFileChooser fileopen = new JFileChooser();
fileopen.setFileSelectionMode(1);
fileopen.setAcceptAllFileFilterUsed(false);
fileopen.setCurrentDirectory(new File(ConfigWindow.FILE_OPEN_CURRENT_DIRECTORY_PACK));
fileopen.setPreferredSize(new Dimension(600, 600));
int ret = fileopen.showDialog(null, "Open");
if (ret == 0)
{
currentFileWindow = fileopen.getSelectedFile();
ConfigWindow.save("FILE_OPEN_CURRENT_DIRECTORY_PACK", currentFileWindow.getPath());
L2ClientDat.addLogConsole("---------------------------------------", true);
L2ClientDat.addLogConsole("selected folder: " + currentFileWindow.getPath(), true);
try
{
MassTxtPacker.getInstance().pack(String.valueOf(jComboBoxChronicle.getSelectedItem()), currentFileWindow.getPath(), CryptVersionParser.getInstance().getEncryptKey(String.valueOf(jComboBoxEncrypt.getSelectedItem())));
}
catch (Exception ex)
{
Logger.getLogger(L2ClientDat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void massTxtUnpackActionPerformed(ActionEvent evt)
{
JFileChooser fileopen = new JFileChooser();
fileopen.setFileSelectionMode(1);
fileopen.setAcceptAllFileFilterUsed(false);
fileopen.setCurrentDirectory(new File(ConfigWindow.FILE_OPEN_CURRENT_DIRECTORY_UNPACK));
fileopen.setPreferredSize(new Dimension(600, 600));
int ret = fileopen.showDialog(null, "Open");
if (ret == 0)
{
currentFileWindow = fileopen.getSelectedFile();
ConfigWindow.save("FILE_OPEN_CURRENT_DIRECTORY_UNPACK", currentFileWindow.getPath());
L2ClientDat.addLogConsole("---------------------------------------", true);
L2ClientDat.addLogConsole("selected folder: " + currentFileWindow.getPath(), true);
try
{
MassTxtUnpacker.getInstance().unpack(String.valueOf(jComboBoxChronicle.getSelectedItem()), currentFileWindow.getPath(), CryptVersionParser.getInstance().getDecryptKey(String.valueOf(jComboBoxDecrypt.getSelectedItem())));
}
catch (Exception ex)
{
Logger.getLogger(L2ClientDat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void massRecryptActionPerformed(ActionEvent evt)
{
JFileChooser fileopen = new JFileChooser();
fileopen.setFileSelectionMode(1);
fileopen.setAcceptAllFileFilterUsed(false);
fileopen.setCurrentDirectory(new File(ConfigWindow.FILE_OPEN_CURRENT_DIRECTORY));
fileopen.setPreferredSize(new Dimension(600, 600));
int ret = fileopen.showDialog(null, "Open");
if (ret == 0)
{
currentFileWindow = fileopen.getSelectedFile();
ConfigWindow.save("FILE_OPEN_CURRENT_DIRECTORY", currentFileWindow.getPath());
L2ClientDat.addLogConsole("---------------------------------------", true);
L2ClientDat.addLogConsole("selected folder: " + currentFileWindow.getPath(), true);
try
{
MassRecryptor.getInstance().recrypt(String.valueOf(jComboBoxChronicle.getSelectedItem()), currentFileWindow.getPath(), CryptVersionParser.getInstance().getDecryptKey(String.valueOf(jComboBoxDecrypt.getSelectedItem())), CryptVersionParser.getInstance().getEncryptKey(String.valueOf(jComboBoxEncrypt.getSelectedItem())));
}
catch (Exception ex)
{
Logger.getLogger(L2ClientDat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void openSelectFileWindow(ActionEvent evt)
{
textPaneMain.removeAll();
JFileChooser fileopen = new JFileChooser();
fileopen.setFileSelectionMode(0);
fileopen.setAcceptAllFileFilterUsed(false);
fileopen.setFileFilter(new FileNameExtensionFilter(".dat, .ini, .txt, .htm", "dat", "ini", "txt", "htm"));
fileopen.setSelectedFile(new File(ConfigWindow.LAST_FILE_SELECTED));
fileopen.setPreferredSize(new Dimension(600, 600));
int ret = fileopen.showDialog(null, "Open");
if (ret == 0)
{
currentFileWindow = fileopen.getSelectedFile();
ConfigWindow.save("LAST_FILE_SELECTED", currentFileWindow.getAbsolutePath());
L2ClientDat.addLogConsole("---------------------------------------", true);
L2ClientDat.addLogConsole("Open file: " + currentFileWindow.getName(), true);
try
{
OpenDat.start(String.valueOf(jComboBoxChronicle.getSelectedItem()), currentFileWindow, currentFileWindow.getName(), CryptVersionParser.getInstance().getDecryptKey(String.valueOf(jComboBoxDecrypt.getSelectedItem())));
}
catch (Exception ex)
{
Logger.getLogger(L2ClientDat.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void saveActionPerformed(ActionEvent evt)
{
JFileChooser fileSave = new JFileChooser();
fileSave.setCurrentDirectory(new File(ConfigWindow.FILE_SAVE_CURRENT_DIRECTORY));
if (currentFileWindow != null)
{
fileSave.setSelectedFile(new File(currentFileWindow.getName().split("\\.")[0] + ".txt"));
fileSave.setFileFilter(new FileNameExtensionFilter(".txt", "txt"));
fileSave.setAcceptAllFileFilterUsed(false);
fileSave.setPreferredSize(new Dimension(600, 600));
int ret = fileSave.showSaveDialog(null);
if (ret == 0)
{
File f = fileSave.getSelectedFile();
try
{
PrintWriter out = new PrintWriter(new FileOutputStream(f.getPath()), true);
ConfigWindow.save("FILE_SAVE_CURRENT_DIRECTORY", f.getParentFile().toString());
out.print(textPaneMain.getText());
out.close();
}
catch (Exception out)
{
// empty catch block
}
L2ClientDat.addLogConsole("---------------------------------------", true);
L2ClientDat.addLogConsole("Saved: " + f.getPath(), true);
}
}
else
{
L2ClientDat.addLogConsole("No open file!", true);
}
}
/*
* Enabled aggressive block sorting Enabled unnecessary exception pruning Enabled aggressive exception aggregation
*/
private void saveAsActionPerformed(ActionEvent evt)
{
if (currentFileWindow == null)
{
L2ClientDat.addLogConsole("Error saving dat. No file name.", true);
return;
}
byte[] buff = null;
DatCrypter crypter = null;
if (currentFileWindow.getName().endsWith(".dat") || currentFileWindow.getName().endsWith(".txt"))
{
try
{
Descriptor desc = DescriptorParser.getInstance().findDescriptorForFile(String.valueOf(jComboBoxChronicle.getSelectedItem()), currentFileWindow.getName().replace("txt", "dat"));
if (desc != null)
{
crypter = CryptVersionParser.getInstance().getEncryptKey(String.valueOf(jComboBoxEncrypt.getSelectedItem()));
buff = DescriptorWriter.parseData(currentFileWindow, crypter, desc, textPaneMain.getText());
GameDataName.getInstance().checkAndUpdate(currentFileWindow.getParent(), crypter);
}
L2ClientDat.addLogConsole("Not found the structure of the file: " + currentFileWindow.getName(), true);
}
catch (Exception e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
return;
}
}
else if (currentFileWindow.getName().endsWith(".ini"))
{
crypter = CryptVersionParser.getInstance().getEncryptKey(String.valueOf(jComboBoxEncrypt.getSelectedItem()));
buff = textPaneMain.getText().getBytes();
}
if (buff == null)
{
L2ClientDat.addLogConsole("buff == null.", true);
return;
}
try
{
if (ConfigDebug.ENCRYPT && (crypter != null))
{
DatFile.encrypt(buff, currentFileWindow.getPath(), crypter);
}
}
catch (Exception e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
return;
}
L2ClientDat.addLogConsole("Packed successfully.", true);
}
private void saveComboBox(JComboBox<?> jComboBox, String param)
{
ConfigWindow.save(param, String.valueOf(jComboBox.getSelectedItem()));
}
public static void main(String[] args)
{
ConfigWindow.load();
ConfigDebug.load();
CryptVersionParser.getInstance();
Util.compileJavaClass("./structure/format/");
DescriptorParser.getInstance();
try
{
for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
{
if (!"Nimbus".equals(info.getName()))
{
continue;
}
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
catch (Exception ex)
{
Logger.getLogger(L2ClientDat.class.getName()).log(Level.SEVERE, null, ex);
}
EventQueue.invokeLater(L2ClientDat::new);
}
public static void addLogConsole(String log, boolean isLog)
{
if (isLog)
{
DebugUtil.getLogger().info(log);
}
textPaneLog.append(log + "\n");
}
public static void addText(String log)
{
textPaneMain.setText(log);
}
}

View File

@ -0,0 +1,90 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.actions;
import java.io.File;
import java.io.FileInputStream;
import com.l2jmobius.L2ClientDat;
import com.l2jmobius.clientcryptor.DatFile;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
public class MassRecryptor
{
private static final MassRecryptor _instance = new MassRecryptor();
public static MassRecryptor getInstance()
{
return _instance;
}
public void recrypt(String chronicle, String path, DatCrypter decCrypter, DatCrypter enCrypter)
{
L2ClientDat.addLogConsole("Mass recrypt with using " + chronicle + " chronicles by path [" + path + "]", true);
File baseDir = new File(path);
if (!baseDir.exists())
{
L2ClientDat.addLogConsole("Directory [" + path + "] does not exists.", true);
return;
}
String recryptDirPath = path + "/" + "!recrypted";
File recryptDir = new File(recryptDirPath);
if (!recryptDir.exists() && !recryptDir.mkdir())
{
L2ClientDat.addLogConsole("Cannot create recrypt directory [" + recryptDirPath + "].", true);
return;
}
long startTime = System.currentTimeMillis();
for (File file : baseDir.listFiles(pathname -> pathname.getName().endsWith(".dat") || pathname.getName().endsWith(".ini") || pathname.getName().endsWith(".htm")))
{
try
{
try (FileInputStream fis = new FileInputStream(file);)
{
if (fis.available() < 28)
{
L2ClientDat.addLogConsole(file.getName() + " The file is too small.", true);
return;
}
byte[] head = new byte[28];
fis.read(head);
String header = new String(head, "UTF-16LE");
if (!header.startsWith("Lineage2Ver"))
{
L2ClientDat.addLogConsole("File " + file.getName() + " not encrypted. Skip decrypt.", true);
continue;
}
if (Integer.valueOf(header.substring(11)).intValue() != decCrypter.getCode())
{
L2ClientDat.addLogConsole("File " + file.getName() + " encrypted code: " + header + ". Skip decrypt.", true);
continue;
}
L2ClientDat.addLogConsole("Recrypting [" + file.getName() + "]", true);
DatFile dat = new DatFile(file.getAbsolutePath());
dat.decrypt(decCrypter);
DatFile.encrypt(dat.getBuff().array(), recryptDirPath + "/" + file.getName(), enCrypter);
}
}
catch (Exception e)
{
L2ClientDat.addLogConsole(file.getName() + " Decrypt failed.", true);
}
}
long diffTime = (System.currentTimeMillis() - startTime) / 1000L;
L2ClientDat.addLogConsole("Completed. Elapsed ".concat(String.valueOf(diffTime)).concat(" sec"), true);
}
}

View File

@ -0,0 +1,134 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.actions;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
import com.l2jmobius.L2ClientDat;
import com.l2jmobius.clientcryptor.DatFile;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.config.ConfigDebug;
import com.l2jmobius.data.GameDataName;
import com.l2jmobius.util.DebugUtil;
import com.l2jmobius.xml.Descriptor;
import com.l2jmobius.xml.DescriptorParser;
import com.l2jmobius.xml.DescriptorWriter;
import com.l2jmobius.xml.exceptions.PackDataException;
public class MassTxtPacker
{
private static final MassTxtPacker _instance = new MassTxtPacker();
public static MassTxtPacker getInstance()
{
return _instance;
}
public void pack(String chronicle, String path, DatCrypter crypter) throws Exception
{
L2ClientDat.addLogConsole("Mass packer with using " + chronicle + " chronicles by path [" + path + "]", true);
File baseDir = new File(path);
if (!baseDir.exists())
{
L2ClientDat.addLogConsole("Directory [" + path + "] does not exists.", true);
return;
}
String packDirPath = path + "/" + "!packed";
File packDir = new File(packDirPath);
if (!packDir.exists() && !packDir.mkdir())
{
L2ClientDat.addLogConsole("Cannot create directory [" + packDir + "].", true);
return;
}
File[] fList = baseDir.listFiles(pathname -> crypter.checkFileExtension(pathname.getName()));
GameDataName.getInstance().clear();
long startTime = System.currentTimeMillis();
CountDownLatch doneSignal = new CountDownLatch(fList.length);
Semaphore semaphore = new Semaphore(Runtime.getRuntime().availableProcessors());
for (File file : fList)
{
new Thread(() ->
{
try
{
semaphore.acquire();
L2ClientDat.addLogConsole("Packing [" + file.getName() + "]", true);
try
{
byte[] buff = null;
String name = file.getName();
if (crypter.isUseStructure())
{
name = name.replace(".txt", ".dat");
byte[] array = Files.readAllBytes(file.toPath());
String joined = new String(array, 0, array.length, "UTF-8");
Descriptor desc = DescriptorParser.getInstance().findDescriptorForFile(chronicle, name);
if (desc != null)
{
buff = DescriptorWriter.parseData(file, crypter, desc, joined);
}
else
{
L2ClientDat.addLogConsole("Not found the structure of the file: " + file.getName(), true);
}
}
else
{
buff = Files.readAllBytes(file.toPath());
}
String fResult = packDir + "/" + name;
if (buff != null)
{
if (ConfigDebug.ENCRYPT)
{
DatFile.encrypt(buff, fResult, crypter);
}
else
{
FileOutputStream os = new FileOutputStream(fResult, false);
os.write(buff);
os.close();
}
}
}
catch (PackDataException e)
{
DebugUtil.getLogger().error(e);
}
catch (Exception e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
}
doneSignal.countDown();
semaphore.release();
}
catch (InterruptedException e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
}
}).start();
}
doneSignal.await();
GameDataName.getInstance().checkAndUpdate(packDir.getPath(), crypter);
long diffTime = (System.currentTimeMillis() - startTime) / 1000L;
L2ClientDat.addLogConsole("Completed. Elapsed ".concat(String.valueOf(diffTime)).concat(" sec"), true);
}
}

View File

@ -0,0 +1,165 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.actions;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import com.l2jmobius.L2ClientDat;
import com.l2jmobius.clientcryptor.DatFile;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.data.GameDataName;
import com.l2jmobius.util.DebugUtil;
import com.l2jmobius.xml.Descriptor;
import com.l2jmobius.xml.DescriptorParser;
import com.l2jmobius.xml.DescriptorReader;
public class MassTxtUnpacker
{
private static final MassTxtUnpacker _instance = new MassTxtUnpacker();
public static MassTxtUnpacker getInstance()
{
return _instance;
}
public void unpack(String chronicle, String path, DatCrypter decCrypter)
{
L2ClientDat.addLogConsole("Mass unpacker with using " + chronicle + " chronicles by path [" + path + "]", true);
File baseDir = new File(path);
if (!baseDir.exists())
{
L2ClientDat.addLogConsole("Directory [" + path + "] does not exists.", true);
return;
}
String unpackDirPath = path + "/" + "!unpacked";
File unpackDir = new File(unpackDirPath);
if (!unpackDir.exists() && !unpackDir.mkdir())
{
L2ClientDat.addLogConsole("Cannot create recrypt directory [" + unpackDirPath + "].", true);
return;
}
GameDataName.getInstance().clear();
long startTime = System.currentTimeMillis();
for (File file : baseDir.listFiles(pathname -> decCrypter.checkFileExtension(pathname.getName())))
{
try
{
try (FileInputStream fis = new FileInputStream(file);)
{
String name;
String res;
String charset;
if (fis.available() < 28)
{
L2ClientDat.addLogConsole(file.getName() + " The file is too small.", true);
continue;
}
byte[] head = new byte[28];
fis.read(head);
fis.close();
String header = new String(head, "UTF-16LE");
if (!header.startsWith("Lineage2Ver"))
{
L2ClientDat.addLogConsole("File " + file.getName() + " not encrypted. Skip decrypt.", true);
continue;
}
if (Integer.valueOf(header.substring(11)).intValue() != decCrypter.getCode())
{
L2ClientDat.addLogConsole("File " + file.getName() + " encrypted code: " + header + ". Skip decrypt.", true);
continue;
}
L2ClientDat.addLogConsole("Unpacking [" + file.getName() + "]", true);
DatFile dat = new DatFile(file.getAbsolutePath());
dat.decrypt(decCrypter);
DebugUtil.save(dat.getBuff(), file);
res = null;
charset = "UTF-8";
if (decCrypter.isUseStructure() && file.getName().endsWith(".dat"))
{
Descriptor desc = DescriptorParser.getInstance().findDescriptorForFile(chronicle, file.getName());
try
{
res = DescriptorReader.getInstance().parseData(file, decCrypter, desc, dat.getBuff());
}
catch (Exception e23)
{
L2ClientDat.addLogConsole("Cannot parse [" + file.getName() + "]", true);
}
name = file.getName().replace(".dat", ".txt");
}
else
{
name = file.getName();
if (file.getName().endsWith(".unr"))
{
name = "dec-" + name;
}
if (file.getName().endsWith(".htm"))
{
charset = "UTF-16";
}
try
{
try (FileOutputStream fos = new FileOutputStream(unpackDirPath + "/" + name);)
{
fos.write(dat.getBuff().array());
}
catch (Throwable throwable)
{
throw throwable;
}
}
catch (UnsupportedEncodingException e3)
{
e3.printStackTrace();
}
}
if (res == null)
{
continue;
}
try
{
try (PrintWriter fos = new PrintWriter(new OutputStreamWriter(new FileOutputStream(unpackDirPath + "/" + name), charset));)
{
fos.write(res);
}
catch (Throwable throwable)
{
throw throwable;
}
}
catch (Exception e4)
{
DebugUtil.getLogger().error("Cannot write file.", e4);
}
}
}
catch (Exception e)
{
DebugUtil.getLogger().error(file.getName() + " Decrypt failed.", e);
}
}
long diffTime = (System.currentTimeMillis() - startTime) / 1000L;
L2ClientDat.addLogConsole("Completed. Elapsed ".concat(String.valueOf(diffTime)).concat(" sec"), true);
}
}

View File

@ -0,0 +1,143 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.actions;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import com.l2jmobius.L2ClientDat;
import com.l2jmobius.clientcryptor.DatFile;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.data.GameDataName;
import com.l2jmobius.util.DebugUtil;
import com.l2jmobius.xml.Descriptor;
import com.l2jmobius.xml.DescriptorParser;
import com.l2jmobius.xml.DescriptorReader;
public class OpenDat
{
public static void start(String dir, File path, String name, DatCrypter crypter) throws Exception
{
ByteBuffer buffer;
boolean crypt = true;
if (!path.exists())
{
L2ClientDat.addLogConsole("File does not exist.", true);
return;
}
if (!path.canRead())
{
L2ClientDat.addLogConsole("Unable to read file.", true);
return;
}
FileInputStream fis = new FileInputStream(path);
if (fis.available() < 28)
{
L2ClientDat.addLogConsole("The file is too small.", true);
fis.close();
return;
}
byte[] head = new byte[28];
fis.read(head);
String header = new String(head, "UTF-16LE");
if (!header.startsWith("Lineage2Ver"))
{
L2ClientDat.addLogConsole("File " + name + " not encrypted. Skip decrypt.", true);
crypt = false;
}
fis.close();
buffer = null;
if (crypt)
{
L2ClientDat.addLogConsole("File " + name + " encrypted. " + header + " decrypt ...", true);
try
{
DatFile dat = new DatFile(path.getPath());
dat.decrypt(crypter);
buffer = dat.getBuff();
if (buffer == null)
{
L2ClientDat.addLogConsole("Error decrypt file. Empty buffer.", true);
return;
}
}
catch (Exception e)
{
L2ClientDat.addLogConsole("Error decrypt file.", true);
return;
}
DebugUtil.save(buffer, path);
L2ClientDat.addLogConsole("Decrypt successfully.", true);
}
else
{
try
{
try (FileInputStream fIn = new FileInputStream(path);)
{
FileChannel fChan = fIn.getChannel();
ByteBuffer mBuf = ByteBuffer.allocate((int) fChan.size());
fChan.read(mBuf);
buffer = mBuf;
fChan.close();
fIn.close();
}
}
catch (IOException exc)
{
L2ClientDat.addLogConsole("Error reading.", true);
}
}
if (name.contains(".ini") || name.contains(".txt"))
{
if ((buffer != null) && buffer.hasArray())
{
L2ClientDat.addText(new String(buffer.array(), 0, buffer.array().length, "UTF-8"));
}
}
else if (name.contains(".htm"))
{
if ((buffer != null) && buffer.hasArray())
{
L2ClientDat.addText(new String(buffer.array(), 0, buffer.array().length, "UTF-16"));
}
}
else if (crypter.isUseStructure())
{
L2ClientDat.addLogConsole("Read the file structure ...", true);
String data = null;
Descriptor desc = DescriptorParser.getInstance().findDescriptorForFile(dir, name);
if ((desc != null) && (buffer != null))
{
buffer.position(0);
DebugUtil.debug("Buffer size: " + buffer.limit());
GameDataName.getInstance().clear();
data = DescriptorReader.getInstance().parseData(path, crypter, desc, buffer);
}
if (data == null)
{
L2ClientDat.addLogConsole("Structure is not found in the directory: " + dir + " file: " + name, true);
return;
}
L2ClientDat.addText(data);
}
L2ClientDat.addLogConsole("Completed.", true);
}
}

View File

@ -0,0 +1,140 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.clientcryptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.config.ConfigDebug;
public class DatFile extends File
{
private ByteBuffer _buff;
public DatFile(String pathname)
{
super(pathname);
}
public ByteBuffer getBuff()
{
return _buff;
}
public void decrypt(DatCrypter crypter) throws IOException
{
loadInfo(crypter);
try
{
try (FileInputStream fis = new FileInputStream(this);)
{
crypter.unlock();
crypter.aquire();
fis.skip(28L);
byte[] buff = new byte[crypter.getChunkSize(fis.available())];
for (int len = fis.available() - crypter.getSkipSize(); len > 0; len -= fis.read(buff))
{
crypter.update(buff);
if (!crypter.isLock())
{
continue;
}
}
if (crypter.isLock())
{
_buff = null;
return;
}
_buff = crypter.decryptResult();
crypter.release();
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static void encrypt(byte[] buff, String file, DatCrypter crypter) throws IOException
{
crypter.unlock();
crypter.aquire();
FileOutputStream os = new FileOutputStream(file, false);
String header = "Lineage2Ver" + crypter.getCode();
os.write(header.getBytes("UTF-16LE"));
crypter.update(buff);
byte[] res = crypter.encryptResult().array();
os.write(res);
if (ConfigDebug.DAT_ADD_END_BYTES)
{
byte[] endBytes = new byte[20];
endBytes[19] = 100;
os.write(endBytes);
}
os.close();
crypter.release();
}
@SuppressWarnings("resource")
private void loadInfo(DatCrypter crypter) throws IOException
{
if (!exists() || !canRead())
{
throw new IOException("Can not read the dat file");
}
FileInputStream fis = new FileInputStream(this);
if (fis.available() < 28)
{
throw new IOException("Can not read the dat file : too small");
}
byte[] head = new byte[28];
fis.read(head);
String header = new String(head, "UTF-16LE");
if (!header.startsWith("Lineage2Ver"))
{
throw new IOException("Can not read the dat file : wrong header");
}
if (header.endsWith("111") || header.endsWith("120"))
{
return;
}
if (header.endsWith("211") || header.endsWith("212"))
{
return;
}
if (header.endsWith("311"))
{
return;
}
if (header.endsWith("411") || header.endsWith("412") || header.endsWith("413") || header.endsWith("414"))
{
if (fis.available() < 20)
{
throw new IOException("Can not read the dat file : too small");
}
}
else
{
throw new IOException("Can not read the dat file : unknown header : '" + header + "'");
}
fis.close();
}
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.clientcryptor.crypt;
import java.nio.ByteBuffer;
public class BlowFishDatCrypter extends DatCrypter
{
private boolean encrypt = false;
private final BlowfishEngine blowfish = new BlowfishEngine();
public BlowFishDatCrypter(int code, String key, boolean deCrypt)
{
super(code);
encrypt = !deCrypt;
blowfish.init(encrypt, key.getBytes());
}
@Override
public ByteBuffer decryptResult()
{
return null;
}
@Override
public ByteBuffer encryptResult()
{
return null;
}
@Override
public void update(byte[] b)
{
}
@Override
public int getChunkSize(int available)
{
return available;
}
@Override
public int getSkipSize()
{
return 0;
}
@Override
public boolean isLock()
{
return false;
}
@Override
public boolean isEncrypt()
{
return encrypt;
}
@Override
public void unlock()
{
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,122 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.clientcryptor.crypt;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class DESDatCrypter extends DatCrypter
{
private ByteArrayOutputStream _result;
private final boolean encrypt;
private final Cipher _cipher;
public DESDatCrypter(int code, String sKey, boolean deCrypt) throws Exception
{
super(code);
encrypt = !deCrypt;
byte[] key = sKey.getBytes();
byte[] keyXor = new byte[key.length];
for (int i = 0; i < key.length; ++i)
{
byte[] arrby = keyXor;
int n = i % 8;
arrby[n] = (byte) (arrby[n] ^ key[i]);
}
DESKeySpec dks = new DESKeySpec(keyXor);
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey desKey = skf.generateSecret(dks);
_cipher = Cipher.getInstance("DES/ECB/NoPadding");
_cipher.init(deCrypt ? 2 : 1, desKey);
}
@Override
public ByteBuffer decryptResult()
{
return ByteBuffer.wrap(_result.toByteArray());
}
@Override
public ByteBuffer encryptResult()
{
return ByteBuffer.wrap(_result.toByteArray());
}
@Override
public void update(byte[] bArray)
{
try
{
if (!encrypt)
{
int size;
_result = new ByteArrayOutputStream(bArray.length);
byte[] bytes = new byte[8];
for (int position = 0; position < bArray.length; position += size)
{
size = Math.min(8, bArray.length - position);
System.arraycopy(bArray, position, bytes, 0, size);
_result.write(size == 8 ? _cipher.doFinal(bytes) : bytes, 0, size);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
@Override
public int getChunkSize(int available)
{
return available;
}
@Override
public int getSkipSize()
{
return 0;
}
@Override
public boolean isLock()
{
return false;
}
@Override
public boolean isEncrypt()
{
return encrypt;
}
@Override
public void unlock()
{
}
@Override
public void aquire()
{
super.aquire();
}
}

View File

@ -0,0 +1,92 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.clientcryptor.crypt;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
public abstract class DatCrypter
{
private final ReentrantLock _lock = new ReentrantLock(true);
private final int code;
private boolean useStructure;
private final List<String> fileEndNames = new ArrayList<>();
public DatCrypter(int code)
{
this.code = code;
}
public abstract void update(byte[] var1);
public abstract ByteBuffer decryptResult();
public abstract ByteBuffer encryptResult();
public abstract int getChunkSize(int var1);
public abstract int getSkipSize();
public abstract boolean isLock();
public boolean checkAquired()
{
return _lock.isHeldByCurrentThread();
}
public void aquire()
{
_lock.lock();
}
public void release()
{
_lock.unlock();
}
public abstract boolean isEncrypt();
public abstract void unlock();
public int getCode()
{
return code;
}
public void addFileExtension(String n)
{
fileEndNames.addAll(Arrays.asList(n.split(";")));
}
public boolean checkFileExtension(String n)
{
return n.contains(".") && fileEndNames.contains(n.split("\\.")[1]);
}
public boolean isUseStructure()
{
return useStructure;
}
public void setUseStructure(boolean useStructure)
{
this.useStructure = useStructure;
}
}

View File

@ -0,0 +1,231 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.clientcryptor.crypt;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.KeyFactory;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Arrays;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import javax.crypto.Cipher;
import com.l2jmobius.util.DebugUtil;
import com.l2jmobius.util.Util;
public class RSADatCrypter extends DatCrypter
{
private Cipher _cipher;
private ByteArrayOutputStream _result;
private boolean encrypt = false;
private boolean _errorLock = false;
public RSADatCrypter(int code, String modulus, String exp, boolean deCrypt)
{
super(code);
try
{
_cipher = Cipher.getInstance("RSA/ECB/nopadding");
if (deCrypt)
{
RSAPublicKeySpec keyspec = new RSAPublicKeySpec(new BigInteger(modulus, 16), new BigInteger(exp, 16));
RSAPublicKey rsaKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(keyspec);
_cipher.init(2, rsaKey);
}
else
{
encrypt = true;
RSAPrivateKeySpec keyspec = new RSAPrivateKeySpec(new BigInteger(modulus, 16), new BigInteger(exp, 16));
RSAPrivateKey rsaKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(keyspec);
_cipher.init(1, rsaKey);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
@Override
public ByteBuffer decryptResult()
{
if (!checkAquired())
{
throw new IllegalStateException("Dont even think about using a DatCrypter that you didnt aquired");
}
byte[] compressed = _result.toByteArray();
int inflatedSize = compressed[0] & 255;
inflatedSize += (compressed[1] << 8) & 65280;
inflatedSize += (compressed[2] << 16) & 16711680;
inflatedSize += (compressed[3] << 24) & -16777216;
ByteArrayInputStream bais = new ByteArrayInputStream(compressed, 4, compressed.length - 4);
InflaterInputStream iis = new InflaterInputStream(bais, new Inflater());
ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
byte[] inflatedResult = new byte[128];
try
{
int len;
while ((len = iis.read(inflatedResult)) > 0)
{
baos.write(inflatedResult, 0, len);
}
}
catch (IOException e)
{
e.printStackTrace();
}
if (baos.size() != inflatedSize)
{
DebugUtil.getLogger().error("[RSADatCrypter] Hum inflated result doesnt have the expected length..(" + baos.size() + "!=" + inflatedSize + ")");
}
return ByteBuffer.wrap(baos.toByteArray());
}
@Override
public ByteBuffer encryptResult()
{
if (!checkAquired())
{
throw new IllegalStateException("Dont even think about using a DatCrypter that you didnt aquired");
}
ByteArrayOutputStream result = new ByteArrayOutputStream();
try
{
int len;
ByteArrayInputStream input = new ByteArrayInputStream(_result.toByteArray());
byte[] buffer = new byte[124];
byte[] block = new byte[128];
while ((len = input.read(buffer)) > 0)
{
Arrays.fill(block, (byte) 0);
block[0] = (byte) ((len >> 24) & 255);
block[1] = (byte) ((len >> 16) & 255);
block[2] = (byte) ((len >> 8) & 255);
block[3] = (byte) (len & 255);
System.arraycopy(buffer, 0, block, 128 - len - ((124 - len) % 4), len);
result.write(_cipher.doFinal(block));
}
}
catch (Exception e)
{
e.printStackTrace();
}
return ByteBuffer.wrap(result.toByteArray());
}
@Override
public void update(byte[] b)
{
if (!checkAquired())
{
throw new IllegalStateException("Dont even think about using a DatCrypter that you didnt aquired");
}
try
{
if (!encrypt)
{
byte[] chunk = _cipher.doFinal(b);
int size = chunk[3];
size += (chunk[2] << 8) & 65280;
size += (chunk[1] << 16) & 16711680;
int pad = (-size & 1) + (-(size += (chunk[0] << 24) & -16777216) & 2);
DebugUtil.debug("Size:" + size + " pad:" + pad);
if (size > 128)
{
_errorLock = true;
return;
}
_result.write(chunk, 128 - size - pad, size);
DebugUtil.debug("--- BLOCK:\n" + Util.printData(chunk) + "-----");
}
else
{
try
{
ByteArrayOutputStream s = new ByteArrayOutputStream(b.length);
DeflaterOutputStream dos = new DeflaterOutputStream(s, new Deflater());
dos.write(b);
dos.finish();
dos.close();
int l = b.length;
_result = new ByteArrayOutputStream(10 + s.toByteArray().length);
_result.write(l & 255);
_result.write((l & 65280) >> 8);
_result.write((l & 16711680) >> 16);
_result.write((l & -16777216) >> 24);
_result.write(s.toByteArray());
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
catch (Exception e)
{
e.printStackTrace();
_errorLock = true;
}
}
@Override
public void aquire()
{
super.aquire();
_result = new ByteArrayOutputStream(128);
}
@Override
public boolean isEncrypt()
{
return encrypt;
}
@Override
public int getChunkSize(int available)
{
return 128;
}
@Override
public int getSkipSize()
{
return 20;
}
@Override
public boolean isLock()
{
return _errorLock;
}
@Override
public void unlock()
{
_errorLock = false;
}
}

View File

@ -0,0 +1,91 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.clientcryptor.crypt;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
public class XorDatCrypter extends DatCrypter
{
private ByteArrayOutputStream _result;
private final boolean encrypt;
private final int xorKey;
public XorDatCrypter(int code, int key, boolean deCrypt)
{
super(code);
encrypt = !deCrypt;
xorKey = key;
}
@Override
public ByteBuffer decryptResult()
{
return ByteBuffer.wrap(_result.toByteArray());
}
@Override
public ByteBuffer encryptResult()
{
return ByteBuffer.wrap(_result.toByteArray());
}
@Override
public void update(byte[] bArray)
{
for (byte b : bArray)
{
_result.write(b ^ xorKey);
}
}
@Override
public int getChunkSize(int available)
{
return 1;
}
@Override
public int getSkipSize()
{
return 0;
}
@Override
public boolean isLock()
{
return false;
}
@Override
public boolean isEncrypt()
{
return encrypt;
}
@Override
public void unlock()
{
}
@Override
public void aquire()
{
super.aquire();
_result = new ByteArrayOutputStream(128);
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.config;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import com.l2jmobius.util.DebugUtil;
import jfork.nproperty.Cfg;
import jfork.nproperty.CfgIgnore;
import jfork.nproperty.ConfigParser;
@Cfg
public class ConfigDebug extends ConfigParser
{
@CfgIgnore
private static final ConfigDebug _instance = new ConfigDebug();
public static boolean DAT_ADD_END_BYTES = true;
public static boolean DAT_DEBUG_MSG = false;
public static boolean DAT_DEBUG_POS = false;
public static int DAT_DEBUG_POS_LIMIT = 100000;
public static boolean DAT_REPLACEMENT_NAMES = true;
public static boolean ENCRYPT = true;
public static boolean SAVE_DECODE = false;
public static void load()
{
try
{
ConfigParser.parse((_instance), "./config/config_debug.ini");
}
catch (IOException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e)
{
DebugUtil.getLogger().error("Failed to load configuration file.", e);
}
}
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.config;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Properties;
import com.l2jmobius.util.DebugUtil;
import jfork.nproperty.Cfg;
import jfork.nproperty.CfgIgnore;
import jfork.nproperty.ConfigParser;
@Cfg
public class ConfigWindow extends ConfigParser
{
@CfgIgnore
private static final ConfigWindow _instance = new ConfigWindow();
@CfgIgnore
private static final String PATH = "./config/config_window.ini";
public static String FILE_OPEN_CURRENT_DIRECTORY_UNPACK = ".";
public static String FILE_OPEN_CURRENT_DIRECTORY_PACK = ".";
public static String FILE_OPEN_CURRENT_DIRECTORY = ".";
public static String FILE_SAVE_CURRENT_DIRECTORY = ".";
public static String CURRENT_CHRONICLE = "";
public static int WINDOW_HEIGHT = 600;
public static int WINDOW_WIDTH = 800;
public static String CURRENT_ENCRYPT = ".";
public static String CURRENT_DECRYPT = ".";
public static String LAST_FILE_SELECTED = ".";
public static void load()
{
try
{
ConfigParser.parse((_instance), PATH);
}
catch (IOException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e)
{
DebugUtil.getLogger().error("Failed to load configuration file.", e);
}
}
public static void save(String key, String var)
{
try
{
Properties props = new Properties();
props.load(new FileInputStream(PATH));
props.setProperty(key, var);
FileOutputStream output = new FileOutputStream(PATH);
props.store(output, "Saved settings");
output.close();
ConfigWindow.load();
}
catch (Exception props)
{
// empty catch block
}
}
}

View File

@ -0,0 +1,238 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.data;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.l2jmobius.L2ClientDat;
import com.l2jmobius.clientcryptor.DatFile;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.config.ConfigDebug;
import com.l2jmobius.util.ByteReader;
import com.l2jmobius.util.ByteWriter;
import com.l2jmobius.util.Util;
import com.l2jmobius.xml.ParamNode;
public class GameDataName
{
private static final byte[] endFileBytes = new byte[]
{
12,
83,
97,
102,
101,
80,
97,
99,
107,
97,
103,
101,
0
};
private final Map<Integer, String> names = new HashMap<>();
private final Map<String, Integer> nameHash = new HashMap<>();
private final Map<String, Integer> nameLowHash = new HashMap<>();
private void load(File currentFile, DatCrypter decCrypter) throws Exception
{
names.clear();
nameHash.clear();
nameLowHash.clear();
if (decCrypter.isEncrypt())
{
File file = new File(currentFile.getParent(), "L2GameDataName.txt");
if (file.exists())
{
List<String> list = Files.readAllLines(file.toPath());
for (int i = 0; i < list.size(); ++i)
{
String str = list.get(i);
Map<String, String> map = Util.stringToMap(str);
String name = map.get("name");
name = name.substring(1, name.length() - 1);
names.put(i, name);
nameHash.put(name, i);
nameLowHash.put(name.toLowerCase(), i);
}
L2ClientDat.addLogConsole("GameDataName: Load " + names.size() + " count.", true);
}
}
else
{
File file = new File(currentFile.getParent(), "L2GameDataName.dat");
FileInputStream fis = new FileInputStream(file);
if (fis.available() < 28)
{
L2ClientDat.addLogConsole(file.getName() + " The file is too small.", true);
fis.close();
return;
}
byte[] head = new byte[28];
fis.read(head);
fis.close();
String header = new String(head, "UTF-16LE");
if (!header.startsWith("Lineage2Ver"))
{
L2ClientDat.addLogConsole("GameDataName: File " + file.getName() + " not encrypted. Skip decrypt.", true);
return;
}
if (Integer.valueOf(header.substring(11)).intValue() != decCrypter.getCode())
{
L2ClientDat.addLogConsole("GameDataName: File " + file.getName() + " encrypted code: " + header + ". Skip decrypt.", true);
return;
}
L2ClientDat.addLogConsole("Unpacking [" + file.getName() + "]", true);
DatFile dat = new DatFile(file.getAbsolutePath());
dat.decrypt(decCrypter);
ByteBuffer buff = dat.getBuff();
int size = ByteReader.readUInt(buff);
for (int i = 0; i < size; ++i)
{
String name = ByteReader.readUtfString(buff, false);
if (name.contains("[") || name.contains("]"))
{
L2ClientDat.addLogConsole("GameDataName: Error index: " + i + " string: " + name, true);
}
names.put(i, name);
nameHash.put(name, i);
nameLowHash.put(name.toLowerCase(), i);
}
L2ClientDat.addLogConsole("GameDataName: Load " + names.size() + " count.", true);
}
}
public String getString(File currentFile, DatCrypter crypter, int index) throws Exception
{
String val;
if (names.isEmpty())
{
load(currentFile, crypter);
}
if (!names.containsKey(index))
{
L2ClientDat.addLogConsole("GameDataName: Not found index: " + index, true);
}
if ((val = names.getOrDefault(index, String.valueOf(index))).isEmpty())
{
L2ClientDat.addLogConsole("GameDataName: String name Empty!!! file: " + currentFile.getName(), true);
}
return "[" + val + "]";
}
public synchronized int getId(File currentFile, DatCrypter crypter, ParamNode node, String str) throws Exception
{
String low;
if (!str.startsWith("[") || !str.endsWith("]"))
{
L2ClientDat.addLogConsole("GameDataName: String name not brackets!!! file: " + currentFile.getName() + " str: " + str + " node: " + node, true);
}
if ((str = str.substring(1, str.length() - 1)).isEmpty())
{
L2ClientDat.addLogConsole("GameDataName: String name Empty!!! file: " + currentFile.getName() + " node: " + node, true);
return -1;
}
if (nameLowHash.isEmpty())
{
load(currentFile, crypter);
}
if (nameLowHash.containsKey(low = str.toLowerCase()))
{
return nameLowHash.get(low);
}
int newIndex = nameLowHash.size();
nameLowHash.put(low, newIndex);
nameHash.put(str, nameHash.size());
return newIndex;
}
public void checkAndUpdate(String currentDir, DatCrypter crypter) throws Exception
{
if (!nameHash.isEmpty())
{
HashSet<String> setList = new HashSet<>();
TreeMap<Integer, String> sortedMap = new TreeMap<>();
for (String key : nameHash.keySet())
{
sortedMap.put(nameHash.get(key), key);
if (setList.add(key.toLowerCase()))
{
continue;
}
L2ClientDat.addLogConsole("GameDataName: name " + key + " conflicted.", true);
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write((byte[]) ByteWriter.writeInt(sortedMap.size()).array());
for (String key : sortedMap.values())
{
baos.write((byte[]) ByteWriter.writeUtfString(key, false).array());
}
byte[] bytes = baos.toByteArray();
byte[] resultBytes = new byte[bytes.length + endFileBytes.length];
System.arraycopy(bytes, 0, resultBytes, 0, bytes.length);
System.arraycopy(endFileBytes, 0, resultBytes, bytes.length, endFileBytes.length);
String file = currentDir + "/L2GameDataName.dat";
if (ConfigDebug.ENCRYPT)
{
DatFile.encrypt(resultBytes, file, crypter);
}
else
{
FileOutputStream os = new FileOutputStream(file, false);
os.write(resultBytes);
os.close();
}
L2ClientDat.addLogConsole("GameDataName: packed " + sortedMap.size() + " count.", true);
names.clear();
nameHash.clear();
nameLowHash.clear();
}
}
public void clear()
{
names.clear();
nameHash.clear();
nameLowHash.clear();
}
public static GameDataName getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
static final GameDataName _instance = new GameDataName();
private SingletonHolder()
{
}
}
}

View File

@ -0,0 +1,299 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.forms;
import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTextArea;
public class JPopupTextArea extends JTextArea
{
static final String COPY = "Copy (Ctrl + C)";
static final String CUT = "Cut (Ctrl + X)";
static final String PASTE = "Paste (Ctrl + V)";
static final String DELETE = "Delete";
static final String SELECTALL = "Select all (Ctrl + A)";
static final String LINE = "Line (Ctrl + G)";
static final String FIND = "Find (Ctrl + F)";
Vector<Integer> lineLength = new Vector<>();
public JPopupTextArea()
{
addPopupMenu();
}
private void addPopupMenu()
{
JPopupMenu menu = new JPopupMenu();
JMenuItem copyItem = new JMenuItem();
copyItem.setAction(getActionMap().get("copy-to-clipboard"));
copyItem.setText(COPY);
JMenuItem cutItem = new JMenuItem();
cutItem.setAction(getActionMap().get("cut-to-clipboard"));
cutItem.setText(CUT);
JMenuItem pasteItem = new JMenuItem();
pasteItem.setAction(getActionMap().get("paste-from-clipboard"));
pasteItem.setText(PASTE);
JMenuItem deleteItem = new JMenuItem();
deleteItem.setAction(getActionMap().get("delete-previous"));
deleteItem.setText(DELETE);
JMenuItem selectAllItem = new JMenuItem();
selectAllItem.setAction(getActionMap().get("select-all"));
selectAllItem.setText(SELECTALL);
JMenuItem selectLine = new JMenuItem();
selectLine.addActionListener(e -> goToLine());
selectLine.setText(LINE);
JMenuItem selectFind = new JMenuItem();
selectFind.addActionListener(e -> searchString());
selectFind.setText(FIND);
menu.add(copyItem);
menu.add(cutItem);
menu.add(pasteItem);
menu.add(deleteItem);
menu.add(new JSeparator());
menu.add(selectAllItem);
menu.add(selectLine);
menu.add(selectFind);
this.add(menu);
addMouseListener(new PopupTriggerMouseListener(menu, this));
addKeyListener(new KeyListen());
}
void goToLine()
{
String lineno;
boolean fnd;
int no = 0;
do
{
block8:
{
fnd = true;
lineno = JOptionPane.showInputDialog("Line number:");
try
{
no = Integer.parseInt(lineno);
}
catch (Exception exp)
{
if (lineno == null)
{
break block8;
}
JOptionPane.showMessageDialog(new Frame(), "Enter a valid line number", "Error", 0);
fnd = false;
}
}
if ((no > 0) || (lineno == null))
{
continue;
}
JOptionPane.showMessageDialog(new Frame(), "Enter a valid line number", "Error", 0);
fnd = false;
}
while (!fnd);
if (lineno != null)
{
getLinePosition();
if ((no - 1) >= lineLength.size())
{
JOptionPane.showMessageDialog(new Frame(), "Line number does not exist", "Error", 0);
}
else
{
try
{
this.requestFocus();
setCaretPosition(lineLength.elementAt(no - 1));
}
catch (Exception exp)
{
JOptionPane.showMessageDialog(new Frame(), "Bad position", "Error", 0);
}
}
}
}
void searchString()
{
String lineno = JOptionPane.showInputDialog("Search string: ");
if ((lineno == null) || lineno.isEmpty())
{
JOptionPane.showMessageDialog(new Frame(), "Enter a empty string", "Error", 0);
return;
}
if (!lineno.isEmpty())
{
try
{
this.requestFocus();
String editorText = this.getText();
String searchValue = lineno;
int start = editorText.indexOf(searchValue, getSelectionEnd());
if (start != -1)
{
setCaretPosition(start);
moveCaretPosition(start + searchValue.length());
getCaret().setSelectionVisible(true);
}
}
catch (Exception exp)
{
JOptionPane.showMessageDialog(new Frame(), "Bad position", "Error", 0);
}
}
}
private void getLinePosition()
{
lineLength = new Vector<>();
String txt = this.getText();
int width = getWidth();
StringTokenizer st = new StringTokenizer(txt, "\n ", true);
String str = " ";
int len = 0;
lineLength.addElement(new Integer(0));
while (st.hasMoreTokens())
{
String token = st.nextToken();
int w = getGraphics().getFontMetrics(getGraphics().getFont()).stringWidth(str + token);
if ((w > width) || (token.charAt(0) == '\n'))
{
len += str.length();
if (token.charAt(0) == '\n')
{
lineLength.addElement(new Integer(len));
}
else
{
lineLength.addElement(new Integer(len - 1));
}
str = token;
continue;
}
str = str + token;
}
}
private class KeyListen implements KeyListener
{
private boolean controlDown;
private boolean gDown;
private boolean fDown;
KeyListen()
{
}
@Override
public void keyTyped(KeyEvent e)
{
}
@Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == 17)
{
controlDown = true;
}
else if (e.getKeyCode() == 71)
{
gDown = true;
}
else if (e.getKeyCode() == 70)
{
fDown = true;
}
if (controlDown)
{
if (gDown)
{
controlDown = false;
gDown = false;
goToLine();
}
else if (fDown)
{
controlDown = false;
fDown = false;
searchString();
}
}
}
@Override
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == 17)
{
controlDown = false;
}
else if (e.getKeyCode() == 71)
{
gDown = false;
}
else if (e.getKeyCode() == 70)
{
fDown = false;
}
}
}
private static class PopupTriggerMouseListener extends MouseAdapter
{
private final JPopupMenu popup;
private final JComponent component;
public PopupTriggerMouseListener(JPopupMenu popup, JComponent component)
{
this.popup = popup;
this.component = component;
}
private void showMenuIfPopupTrigger(MouseEvent e)
{
if (e.isPopupTrigger())
{
popup.show(component, e.getX() + 3, e.getY() + 3);
}
}
@Override
public void mousePressed(MouseEvent e)
{
showMenuIfPopupTrigger(e);
}
@Override
public void mouseReleased(MouseEvent e)
{
showMenuIfPopupTrigger(e);
}
}
}

View File

@ -0,0 +1,24 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.listeners;
public interface FormatListener
{
public String decode(String var1);
public String encode(String var1);
}

View File

@ -0,0 +1,188 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.util;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class ByteReader
{
private static Charset defaultCharset = Charset.forName("cp1252");
private static Charset utf16leCharset = Charset.forName("utf-16le");
public static char readChar(ByteBuffer buffer)
{
return (char) buffer.get();
}
public static int readUByte(ByteBuffer buffer)
{
return buffer.get() & 255;
}
public static int readInt(ByteBuffer buffer)
{
return Integer.reverseBytes(buffer.getInt());
}
public static int readUInt(ByteBuffer buffer)
{
return ByteReader.readInt(buffer);
}
public static short readShort(ByteBuffer buffer)
{
return Short.reverseBytes(buffer.getShort());
}
public static double readDouble(ByteBuffer buffer)
{
return Double.longBitsToDouble(Long.reverseBytes(buffer.getLong()));
}
public static long readLong(ByteBuffer buffer)
{
return Long.reverseBytes(buffer.getLong());
}
public static float readFloat(ByteBuffer buffer)
{
return Float.intBitsToFloat(Integer.reverseBytes(buffer.getInt()));
}
public static int readCompactInt(ByteBuffer input) throws IOException
{
int output = 0;
boolean signed = false;
for (int i = 0; i < 5; ++i)
{
int x = input.get() & 255;
if (x < 0)
{
throw new EOFException();
}
if (i == 0)
{
if ((x & 128) > 0)
{
signed = true;
}
output |= x & 63;
if ((x & 64) != 0)
{
continue;
}
break;
}
if (i == 4)
{
output |= (x & 31) << 27;
continue;
}
output |= (x & 127) << (6 + ((i - 1) * 7));
if ((x & 128) == 0)
{
break;
}
}
if (signed)
{
output *= -1;
}
return output;
}
public static String readRGB(ByteBuffer buffer)
{
String g;
String b;
String r = Integer.toHexString(buffer.get() & 255).toUpperCase();
if (r.length() < 2)
{
r = "0" + r;
}
if ((g = Integer.toHexString(buffer.get() & 255).toUpperCase()).length() < 2)
{
g = "0" + g;
}
if ((b = Integer.toHexString(buffer.get() & 255).toUpperCase()).length() < 2)
{
b = "0" + b;
}
return r + g + b;
}
public static String readRGBA(ByteBuffer buffer)
{
String a = Integer.toHexString(buffer.get() & 255).toUpperCase();
if (a.length() < 2)
{
a = "0" + a;
}
return a + ByteReader.readRGB(buffer);
}
public static String readUtfString(ByteBuffer buffer, boolean isRaw) throws Exception
{
int size = ByteReader.readInt(buffer);
if (size <= 0)
{
return "";
}
if (size > 1000000)
{
throw new Exception("To much data.");
}
byte[] bytes = new byte[size];
try
{
for (int i = 0; i < size; i += 2)
{
bytes[i + 1] = buffer.get();
bytes[i] = buffer.get();
}
}
catch (Exception e)
{
e.printStackTrace();
}
return ByteReader.checkAndReplaceNewLine(isRaw, new String(new String(bytes, "Unicode").getBytes("UTF-8"), "UTF-8"));
}
public static String readString(ByteBuffer input, boolean isRaw) throws IOException
{
int len = ByteReader.readCompactInt(input);
if (len == 0)
{
return "";
}
byte[] bytes = new byte[len > 0 ? len : -2 * len];
input.get(bytes);
return ByteReader.checkAndReplaceNewLine(isRaw, new String(bytes, 0, bytes.length - (len > 0 ? 1 : 2), len > 0 ? defaultCharset : utf16leCharset).intern());
}
private static String checkAndReplaceNewLine(boolean isRaw, String str)
{
if (!isRaw && str.contains("\r\n"))
{
str = str.replace("\r\n", "\\r\\n");
}
return str;
}
}

View File

@ -0,0 +1,211 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.util;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
public class ByteWriter
{
private static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
private static Charset defaultCharset = Charset.forName("ascii");
private static Charset utf16leCharset = Charset.forName("utf-16le");
public static Buffer writeChar(byte value)
{
ByteBuffer buffer = ByteBuffer.allocate(1).order(BYTE_ORDER);
buffer.put(value);
return buffer;
}
public static Buffer writeCompactInt(int count)
{
byte[] b = ByteWriter.compactIntToByteArray(count);
ByteBuffer buffer = ByteBuffer.allocate(b.length).order(BYTE_ORDER);
buffer.put(b);
return buffer;
}
public static Buffer writeByte(byte value)
{
ByteBuffer buffer = ByteBuffer.allocate(1).order(BYTE_ORDER);
buffer.put(value);
return buffer;
}
public static Buffer writeUByte(short value)
{
ByteBuffer buffer = ByteBuffer.allocate(1).order(BYTE_ORDER);
buffer.put((byte) value);
return buffer;
}
public static Buffer writeInt(int value)
{
ByteBuffer buffer = ByteBuffer.allocate(4).order(BYTE_ORDER);
buffer.putInt(value);
return buffer;
}
public static Buffer writeUInt(int value)
{
ByteBuffer buffer = ByteBuffer.allocate(4).order(BYTE_ORDER);
buffer.putInt(value);
return buffer;
}
public static Buffer writeShort(short value)
{
ByteBuffer buffer = ByteBuffer.allocate(2).order(BYTE_ORDER);
buffer.putShort(value);
return buffer;
}
public static Buffer writeUShort(int value)
{
ByteBuffer buffer = ByteBuffer.allocate(2).order(BYTE_ORDER);
buffer.put((byte) (value & 255));
buffer.put((byte) ((value & 65280) >> 8));
return buffer;
}
public static Buffer writeRGB(String rgb)
{
ByteBuffer buffer = ByteBuffer.allocate(3).order(BYTE_ORDER);
buffer.put((byte) Integer.parseInt(rgb.substring(0, 2), 16));
buffer.put((byte) Integer.parseInt(rgb.substring(2, 4), 16));
buffer.put((byte) Integer.parseInt(rgb.substring(4, 6), 16));
return buffer;
}
public static Buffer writeRGBA(String rgba)
{
ByteBuffer buffer = ByteBuffer.allocate(4).order(BYTE_ORDER);
buffer.put((byte[]) ByteWriter.writeRGB(rgba.substring(0, 6)).array());
buffer.put((byte) Integer.parseInt(rgba.substring(6, 8), 16));
return buffer;
}
public static Buffer writeUtfString(String str, boolean isRaw)
{
int size = str.length();
if (size <= 0)
{
return ByteBuffer.allocate(4).order(BYTE_ORDER).putInt(0);
}
if (!isRaw)
{
str = ByteWriter.checkAndReplaceNewLine(str);
size = str.length();
}
ByteBuffer buffer = ByteBuffer.allocate((size * 2) + 4).order(BYTE_ORDER);
buffer.putInt(size * 2);
for (int i = 0; i < size; ++i)
{
buffer.putChar(str.charAt(i));
}
return buffer;
}
public static Buffer writeString(String s, boolean isRaw)
{
if ((s == null) || s.isEmpty())
{
return ByteWriter.writeCompactInt(0);
}
if (!isRaw)
{
s = ByteWriter.checkAndReplaceNewLine(s);
}
s = s + '\u0000';
boolean def = defaultCharset.newEncoder().canEncode(s);
byte[] bytes = s.getBytes(def ? defaultCharset : utf16leCharset);
byte[] bSize = ByteWriter.compactIntToByteArray(def ? bytes.length : (-bytes.length) / 2);
ByteBuffer buffer = ByteBuffer.allocate(bytes.length + bSize.length).order(BYTE_ORDER);
buffer.put(bSize);
buffer.put(bytes);
return buffer;
}
public static Buffer writeDouble(double value)
{
ByteBuffer buffer = ByteBuffer.allocate(8).order(BYTE_ORDER);
buffer.putDouble(value);
return buffer;
}
public static Buffer writeFloat(float value)
{
ByteBuffer buffer = ByteBuffer.allocate(4).order(BYTE_ORDER);
buffer.putFloat(value);
return buffer;
}
public static Buffer writeLong(long value)
{
ByteBuffer buffer = ByteBuffer.allocate(8).order(BYTE_ORDER);
buffer.putLong(value);
return buffer;
}
private static byte[] compactIntToByteArray(int v)
{
boolean negative = v < 0;
v = Math.abs(v);
int[] bytes = new int[]
{
v & 63,
(v >> 6) & 127,
(v >> 13) & 127,
(v >> 20) & 127,
(v >> 27) & 127
};
if (negative)
{
int[] arrn = bytes;
arrn[0] = arrn[0] | 128;
}
int size = 5;
for (int i = 4; (i > 0) && (bytes[i] == 0); --i)
{
--size;
}
byte[] res = new byte[size];
for (int i = 0; i < size; ++i)
{
if (i != (size - 1))
{
int[] arrn = bytes;
int n = i;
arrn[n] = arrn[n] | (i == 0 ? 64 : 128);
}
res[i] = (byte) bytes[i];
}
return res;
}
private static String checkAndReplaceNewLine(String str)
{
if (str.contains("\\r\\n"))
{
str = str.replace("\\r\\n", "\r\n");
}
return str;
}
}

View File

@ -0,0 +1,85 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;
import com.l2jmobius.config.ConfigDebug;
import com.l2jmobius.xml.Variant;
public class DebugUtil
{
private static Logger _log = LogManager.getLogger(DebugUtil.class);
public static void debug(String message)
{
if (ConfigDebug.DAT_DEBUG_MSG)
{
_log.info(message);
}
}
public static void debugPos(int pos, String name, Variant val)
{
if (ConfigDebug.DAT_DEBUG_POS)
{
_log.info("pos: " + pos + " " + name + ": " + val);
if ((ConfigDebug.DAT_DEBUG_POS_LIMIT != 0) && (pos > ConfigDebug.DAT_DEBUG_POS_LIMIT))
{
System.exit(0);
}
}
}
public static void save(ByteBuffer buffer, File path)
{
if (ConfigDebug.SAVE_DECODE)
{
try
{
String unpackDirPath = path.getParent() + "/" + "!decrypted";
File decryptedDir = new File(unpackDirPath);
decryptedDir.mkdir();
File file = new File(decryptedDir + "/" + path.getName());
FileOutputStream fos = new FileOutputStream(file);
fos.write(buffer.array());
fos.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
public static Logger getLogger()
{
return _log;
}
static
{
DOMConfigurator.configure("./config/log4j.xml");
}
}

View File

@ -0,0 +1,338 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.util;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class Util
{
public static void printBytes(String name, byte[] buffer)
{
StringBuilder builder = new StringBuilder(buffer.length);
builder.append(name).append(": [");
for (byte b : buffer)
{
builder.append(b).append(" ");
}
builder.append("]");
DebugUtil.getLogger().info(builder.toString());
}
public static boolean compareBuffers(byte[] b1, byte[] b2)
{
if ((b1 == null) || (b2 == null) || (b1.length != b2.length))
{
return false;
}
for (int i = 0; i < b1.length; ++i)
{
if (b1[i] == b2[i])
{
continue;
}
return false;
}
return true;
}
private static String printData(byte[] data, int len)
{
int a;
int charpoint;
byte t1;
StringBuilder result = new StringBuilder();
int counter = 0;
for (int i = 0; i < len; ++i)
{
if ((counter % 16) == 0)
{
result.append(Util.fillHex(i, 4) + ": ");
}
result.append(Util.fillHex(data[i] & 255, 2) + " ");
if (++counter != 16)
{
continue;
}
result.append(" ");
charpoint = i - 15;
for (a = 0; a < 16; ++a)
{
if (((t1 = data[charpoint++]) > 31) && (t1 < 128))
{
result.append((char) t1);
continue;
}
result.append('.');
}
result.append("\n");
counter = 0;
}
int rest = data.length % 16;
if (rest > 0)
{
for (int i = 0; i < (17 - rest); ++i)
{
result.append(" ");
}
charpoint = data.length - rest;
for (a = 0; a < rest; ++a)
{
if (((t1 = data[charpoint++]) > 31) && (t1 < 128))
{
result.append((char) t1);
continue;
}
result.append('.');
}
result.append("\n");
}
return result.toString();
}
private static String fillHex(int data, int digits)
{
String number = Integer.toHexString(data);
for (int i = number.length(); i < digits; ++i)
{
number = "0" + number;
}
return number;
}
public static String printData(byte[] blop)
{
return Util.printData(blop, blop.length);
}
public static List<File> loadFiles(String dir, String prefix)
{
ArrayList<File> list = new ArrayList<>();
File folder = new File(dir);
for (File listOfFile : folder.listFiles())
{
if (listOfFile.isFile())
{
if (!listOfFile.getName().endsWith(prefix))
{
continue;
}
list.add(listOfFile);
continue;
}
if (!listOfFile.isDirectory())
{
continue;
}
File folder2 = new File(dir + listOfFile.getName() + "/");
for (File aListOfFiles2 : folder2.listFiles())
{
if (!aListOfFiles2.getName().endsWith(prefix))
{
continue;
}
list.add(aListOfFiles2);
}
}
return list;
}
public static String[] getDirsNames(String dir, String prefix)
{
ArrayList<String> list = new ArrayList<>();
File folder = new File(dir);
File[] listOfFiles = folder.listFiles();
if (listOfFiles == null)
{
return null;
}
for (File listOfFile : listOfFiles)
{
if (!listOfFile.isDirectory() || !listOfFile.getName().endsWith(prefix))
{
continue;
}
list.add(listOfFile.getName());
}
String[] text = new String[list.size()];
int i = 0;
Iterator<String> iterator = list.iterator();
while (iterator.hasNext())
{
text[i] = iterator.next();
++i;
}
return text;
}
public static String[] getFilesNames(String dir, String prefix)
{
ArrayList<String> list = new ArrayList<>();
File folder = new File(dir);
File[] listOfFiles = folder.listFiles();
if (listOfFiles == null)
{
return null;
}
for (File listOfFile : listOfFiles)
{
if (!listOfFile.isFile() || !listOfFile.getName().endsWith(prefix))
{
continue;
}
list.add(listOfFile.getName().replace(prefix, ""));
}
String[] text = new String[list.size()];
int i = 0;
Iterator<String> iterator = list.iterator();
while (iterator.hasNext())
{
text[i] = iterator.next();
++i;
}
return text;
}
public static String printData(ByteBuffer buf)
{
byte[] data = new byte[buf.remaining()];
buf.get(data);
String hex = Util.printData(data, data.length);
buf.position(buf.position() - data.length);
return hex;
}
public static List<String> splitList(String s)
{
if (s.startsWith("{"))
{
s = s.substring(1, s.length() - 1);
}
ArrayList<String> res = new ArrayList<>();
StringBuffer buff = new StringBuffer();
int level = 0;
for (char part : s.toCharArray())
{
if ((part == '{') || (part == '['))
{
++level;
}
else if ((part == '}') || (part == ']'))
{
--level;
}
else if ((part == ';') && (level == 0))
{
res.add(buff.toString());
buff = new StringBuffer();
continue;
}
buff.append(part);
}
res.add(buff.toString());
return res;
}
public static Map<String, String> stringToMap(String id)
{
LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (String str : id.split("\t"))
{
if (!str.contains("="))
{
continue;
}
int index = str.indexOf("=");
String key = str.substring(0, index);
String val = str.substring(index + 1, str.length());
map.put(key, val);
}
return map;
}
public static String mapToString(Map<String, String> map)
{
StringBuilder builder = new StringBuilder();
for (String key : map.keySet())
{
builder.append(key).append("=").append(map.get(key)).append("\t");
}
return builder.toString();
}
@SuppressWarnings(
{
"rawtypes",
"unchecked"
})
public static void compileJavaClass(String sourceFile)
{
try
{
DiagnosticCollector diagnostics = new DiagnosticCollector();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<? extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjectsFromFiles(Util.loadFiles(sourceFile, ".java"));
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnit);
if (!task.call().booleanValue())
{
for (Object diagnostic : diagnostics.getDiagnostics())
{
final Diagnostic d = (Diagnostic) diagnostic;
System.out.format("Error on line %d in %s%n", d.getLineNumber(), ((JavaFileObject) d.getSource()).toUri());
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static Object loadJavaClass(String className, String path)
{
try
{
@SuppressWarnings("resource")
URLClassLoader classLoader = new URLClassLoader(new URL[]
{
new File(path).toURI().toURL()
});
Class<?> loadedClass = classLoader.loadClass(className);
return loadedClass.newInstance();
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
}

View File

@ -0,0 +1,148 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.clientcryptor.crypt.BlowFishDatCrypter;
import com.l2jmobius.clientcryptor.crypt.DESDatCrypter;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.clientcryptor.crypt.RSADatCrypter;
import com.l2jmobius.clientcryptor.crypt.XorDatCrypter;
import com.l2jmobius.util.DebugUtil;
public class CryptVersionParser
{
private static Map<String, DatCrypter> encryptKeys = new LinkedHashMap<>();
private static Map<String, DatCrypter> decryptKeys = new LinkedHashMap<>();
private static CryptVersionParser _parser = new CryptVersionParser();
public static CryptVersionParser getInstance()
{
return _parser;
}
private CryptVersionParser()
{
CryptVersionParser.parseCryptVersion();
}
private static void parseCryptVersion()
{
File def = new File("./config/cryptVersion.xml");
if (def.exists())
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setIgnoringElementContentWhitespace(true);
factory.setIgnoringComments(true);
try
{
Document doc = factory.newDocumentBuilder().parse(def);
Node defsNode = doc.getFirstChild();
while (defsNode != null)
{
if (defsNode.getNodeName().equals("keys"))
{
for (Node defNode = defsNode.getFirstChild(); defNode != null; defNode = defNode.getNextSibling())
{
if (!defNode.getNodeName().equals("key"))
{
continue;
}
String name = defNode.getAttributes().getNamedItem("name").getNodeValue();
String type = defNode.getAttributes().getNamedItem("type").getNodeValue().toLowerCase();
int code = Integer.parseInt(defNode.getAttributes().getNamedItem("code").getNodeValue().toLowerCase());
boolean isDecrypt = Boolean.parseBoolean(defNode.getAttributes().getNamedItem("decrypt").getNodeValue());
boolean useStructure = Boolean.parseBoolean(defNode.getAttributes().getNamedItem("useStructure").getNodeValue());
String extension = defNode.getAttributes().getNamedItem("extension").getNodeValue();
DatCrypter dat = null;
switch (type)
{
case "rsa":
{
String modulus = defNode.getAttributes().getNamedItem("modulus").getNodeValue();
String exp = defNode.getAttributes().getNamedItem("exp").getNodeValue();
dat = new RSADatCrypter(code, modulus, exp, isDecrypt);
break;
}
case "xor":
{
dat = new XorDatCrypter(code, Integer.parseInt(defNode.getAttributes().getNamedItem("key").getNodeValue()), isDecrypt);
break;
}
case "blowfish":
{
dat = new BlowFishDatCrypter(code, defNode.getAttributes().getNamedItem("key").getNodeValue(), isDecrypt);
break;
}
case "des":
{
dat = new DESDatCrypter(code, defNode.getAttributes().getNamedItem("key").getNodeValue(), isDecrypt);
}
}
if (dat == null)
{
continue;
}
dat.addFileExtension(extension);
dat.setUseStructure(useStructure);
if (isDecrypt)
{
decryptKeys.put(name, dat);
continue;
}
encryptKeys.put(name, dat);
}
}
defsNode = doc.getNextSibling();
}
}
catch (Exception e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
}
}
}
public Map<String, DatCrypter> getEncryptKey()
{
return encryptKeys;
}
public Map<String, DatCrypter> getDecryptKeys()
{
return decryptKeys;
}
public DatCrypter getEncryptKey(String s)
{
return encryptKeys.get(s);
}
public DatCrypter getDecryptKey(String s)
{
return decryptKeys.get(s);
}
}

View File

@ -0,0 +1,84 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
import java.util.List;
import com.l2jmobius.listeners.FormatListener;
public class Descriptor
{
private final String _alias;
private final String _filePattern;
private final List<ParamNode> _nodes;
private boolean _isRawData;
private boolean _isSafePackage;
private FormatListener _format;
Descriptor(String alias, String filePattern, List<ParamNode> nodes)
{
_alias = alias;
_filePattern = filePattern;
_nodes = nodes;
_isRawData = false;
}
void setIsRawData(boolean value)
{
_isRawData = value;
}
public void setIsSafePackage(boolean value)
{
_isSafePackage = value;
}
boolean isRawData()
{
return _isRawData;
}
public boolean isSafePackage()
{
return _isSafePackage;
}
public String getAlias()
{
return _alias;
}
public String getFilePattern()
{
return _filePattern;
}
List<ParamNode> getNodes()
{
return _nodes;
}
public void setFormat(FormatListener format)
{
_format = format;
}
public FormatListener getFormat()
{
return _format;
}
}

View File

@ -0,0 +1,53 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
public class DescriptorLink
{
private final String dir;
private final String namePattern;
private final String linkFile;
private final String linkVersion;
public DescriptorLink(String dir, String namePattern, String linkFile, String linkVersion)
{
this.dir = dir;
this.namePattern = namePattern;
this.linkFile = linkFile;
this.linkVersion = linkVersion;
}
public String getFilePattern()
{
return dir;
}
public String getNamePattern()
{
return namePattern;
}
public String getLinkFile()
{
return linkFile;
}
public String getLinkVersion()
{
return linkVersion;
}
}

View File

@ -0,0 +1,368 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import com.l2jmobius.listeners.FormatListener;
import com.l2jmobius.util.DebugUtil;
import com.l2jmobius.util.Util;
import com.l2jmobius.xml.exceptions.CycleArgumentException;
public class DescriptorParser
{
private static DescriptorParser _parser = new DescriptorParser();
private final Map<String, Map<String, Descriptor>> _descriptors = new HashMap<>();
private final Map<String, List<ParamNode>> _definitions = new HashMap<>();
private final Map<String, List<DescriptorLink>> _links = new HashMap<>();
private DescriptorParser()
{
load();
}
public static DescriptorParser getInstance()
{
return _parser;
}
private void load()
{
parseDefinitions();
Util.loadFiles("./structure/", ".xml").forEach(this::parseDescriptor);
}
private void parseDefinitions()
{
File def = new File("./!definitions.xml");
if (def.exists())
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setIgnoringElementContentWhitespace(true);
factory.setIgnoringComments(true);
try
{
Document doc = factory.newDocumentBuilder().parse(def);
Node defsNode = doc.getFirstChild();
while (defsNode != null)
{
if (defsNode.getNodeName().equals("definitions"))
{
for (Node defNode = defsNode.getFirstChild(); defNode != null; defNode = defNode.getNextSibling())
{
if (!defNode.getNodeName().equals("definition"))
{
continue;
}
String defName = defNode.getAttributes().getNamedItem("name").getNodeValue();
List<ParamNode> nodes = parseNodes(defNode, true, new HashSet<String>(), "definitions->" + defName);
_definitions.put(defName, nodes);
}
}
defsNode = doc.getNextSibling();
}
}
catch (Exception e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
}
}
}
private void parseDescriptor(File file)
{
if (!file.exists())
{
DebugUtil.debug("File " + file.getName() + " not found.");
}
try
{
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setIgnoringElementContentWhitespace(true);
factory.setIgnoringComments(true);
Document doc = factory.newDocumentBuilder().parse(file);
Node fileNode0 = doc.getFirstChild();
while (fileNode0 != null)
{
if (fileNode0.getNodeName().equalsIgnoreCase("list"))
{
for (Node fileNode = fileNode0.getFirstChild(); fileNode != null; fileNode = fileNode.getNextSibling())
{
String dir;
Object obj;
String namePattern;
Map<String, Descriptor> versions;
if (fileNode.getNodeName().equalsIgnoreCase("link"))
{
dir = file.getName().substring(0, file.getName().length() - 4);
namePattern = fileNode.getAttributes().getNamedItem("pattern").getNodeValue();
String linkFile = fileNode.getAttributes().getNamedItem("file").getNodeValue();
String linkVersion = fileNode.getAttributes().getNamedItem("version").getNodeValue();
List<DescriptorLink> list = _links.get(dir);
if (list == null)
{
list = new ArrayList<>();
}
list.add(new DescriptorLink(dir, namePattern, linkFile, linkVersion));
_links.put(dir, list);
continue;
}
if (!fileNode.getNodeName().equalsIgnoreCase("file"))
{
continue;
}
dir = file.getName().substring(0, file.getName().length() - 4);
namePattern = fileNode.getAttributes().getNamedItem("pattern").getNodeValue();
boolean isRawData = parseBoolNode(fileNode, "isRaw", false);
boolean isSafePackage = parseBoolNode(fileNode, "isSafePackage", false);
String formatName = parseStringNode(fileNode, "format", null);
DebugUtil.debug("Boot of parsing file: " + namePattern);
List<ParamNode> nodes = parseNodes(fileNode, false, new HashSet<String>(), dir + "->" + namePattern);
Descriptor desc = new Descriptor(file.getName(), namePattern, nodes);
desc.setIsRawData(isRawData);
desc.setIsSafePackage(isSafePackage);
if ((formatName != null) && ((obj = Util.loadJavaClass(formatName, "./structure/format/")) instanceof FormatListener))
{
desc.setFormat((FormatListener) obj);
}
if ((versions = _descriptors.get(dir)) == null)
{
versions = new HashMap<>();
}
versions.put(namePattern, desc);
_descriptors.put(dir, versions);
DebugUtil.debug("End of parsing file: " + namePattern);
}
}
fileNode0 = doc.getNextSibling();
}
}
catch (Exception e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
}
}
private List<ParamNode> parseNodes(Node fileNode, boolean isHideName, Set<String> names, String fileName) throws Exception
{
HashMap<String, Integer> defsCounter = new HashMap<>();
LinkedList<ParamNode> nodes = new LinkedList<>();
for (Node node = fileNode.getFirstChild(); node != null; node = node.getNextSibling())
{
ParamNode beginNode;
String nodeName = node.getNodeName();
if (nodeName.equals("#text"))
{
continue;
}
boolean isHide = isHideName || parseBoolNode(node, "hidden", true);
if (node.getAttributes().getNamedItem("name") == null)
{
DebugUtil.getLogger().warn("Node name == null, fileName: " + fileName);
continue;
}
String entityName = node.getAttributes().getNamedItem("name").getNodeValue();
if (nodeName.equalsIgnoreCase("node"))
{
String type = node.getAttributes().getNamedItem("reader").getNodeValue();
if (_definitions.containsKey(type))
{
if (!defsCounter.containsKey(type))
{
defsCounter.put(type, 1);
}
else
{
defsCounter.put(type, defsCounter.get(type) + 1);
}
List<ParamNode> defNodes = _definitions.get(type);
for (ParamNode defNode : defNodes)
{
ParamNode copied = defNode.copy();
copied.setName(entityName);
if (isHide)
{
copied.setHidden();
}
nodes.add(copied);
}
}
else
{
ParamNode dataNode = new ParamNode(entityName, ParamNodeType.VARIABLE, ParamType.valueOf(type));
if (isHide)
{
dataNode.setHidden();
}
nodes.add(dataNode);
DebugUtil.debug("Found node: " + dataNode.getName());
}
if (names.contains(entityName))
{
DebugUtil.getLogger().warn("Node name duplicated [" + entityName + "] fileName: " + fileName);
}
names.add(entityName);
continue;
}
if (nodeName.equalsIgnoreCase("for"))
{
String iteratorName = entityName;
boolean skipWriteSize = parseBoolNode(node, "skipWriteSize", false);
int size = -1;
if (node.getAttributes().getNamedItem("size") != null)
{
String sizeStr = node.getAttributes().getNamedItem("size").getNodeValue();
if (sizeStr.startsWith("#"))
{
iteratorName = sizeStr.substring(1);
}
else
{
size = Integer.parseInt(sizeStr);
}
}
if (size == 0)
{
DebugUtil.getLogger().warn("Size of cycle [" + iteratorName + "] was set to zero. Deprecated cycle?");
}
DebugUtil.debug("Found cycle for variable: " + entityName);
ParamNode beginNode2 = new ParamNode(entityName, ParamNodeType.FOR, null);
if (size >= 0)
{
beginNode2.setSize(size);
}
if (isHide)
{
beginNode2.setHidden();
}
beginNode2.setSkipWriteSize(skipWriteSize);
beginNode2.addSubNodes(parseNodes(node, false, names, fileName));
beginNode2.setCycleName(iteratorName);
nodes.add(beginNode2);
boolean iteratorFound = false;
for (ParamNode n : nodes)
{
if (!iteratorName.equals(n.getName()))
{
continue;
}
n.setIterator();
iteratorFound = true;
break;
}
if (iteratorFound || (size >= 0))
{
continue;
}
throw new CycleArgumentException("Invalid argument [" + iteratorName + "] for [cycle]");
}
if (nodeName.equalsIgnoreCase("wrapper"))
{
beginNode = new ParamNode(entityName, ParamNodeType.WRAPPER, null);
beginNode.addSubNodes(parseNodes(node, true, names, fileName));
nodes.add(beginNode);
DebugUtil.debug("Found [wrapper] data " + entityName);
continue;
}
if (nodeName.equalsIgnoreCase("write"))
{
beginNode = new ParamNode(entityName, ParamNodeType.CONSTANT, ParamType.STRING);
beginNode.setHidden();
nodes.add(beginNode);
DebugUtil.debug("Found [constant] data: " + entityName);
continue;
}
if (!nodeName.equalsIgnoreCase("if"))
{
continue;
}
String paramName = node.getAttributes().getNamedItem("param").getNodeValue();
String vsl = node.getAttributes().getNamedItem("val").getNodeValue();
if (!paramName.startsWith("#"))
{
throw new Exception("Invalid argument [" + entityName + "] for [if]");
}
paramName = paramName.substring(1);
ParamNode beginNode3 = new ParamNode(entityName, ParamNodeType.IF, null);
beginNode3.setParamIf(paramName);
beginNode3.setValIf(vsl);
beginNode3.addSubNodes(parseNodes(node, false, names, fileName));
nodes.add(beginNode3);
DebugUtil.debug("Found [if] data: " + entityName);
}
return nodes;
}
public Descriptor findDescriptorForFile(String dir, String fileName)
{
List<DescriptorLink> listDes = _links.get(dir);
if (listDes == null)
{
return null;
}
for (DescriptorLink desc : listDes)
{
Map<String, Descriptor> versions;
if (!fileName.toLowerCase().matches(desc.getNamePattern().toLowerCase()) || !_descriptors.containsKey(desc.getLinkFile()) || !(versions = _descriptors.get(desc.getLinkFile())).containsKey(desc.getLinkVersion()))
{
continue;
}
return versions.get(desc.getLinkVersion());
}
return null;
}
private boolean parseBoolNode(Node node, String name, boolean def)
{
if (node.getAttributes() == null)
{
return def;
}
if (node.getAttributes().getNamedItem(name) == null)
{
return def;
}
return node.getAttributes().getNamedItem(name).getNodeValue().equalsIgnoreCase("true");
}
private String parseStringNode(Node node, String name, String def)
{
if (node.getAttributes() == null)
{
return def;
}
if (node.getAttributes().getNamedItem(name) == null)
{
return def;
}
return node.getAttributes().getNamedItem(name).getNodeValue();
}
}

View File

@ -0,0 +1,395 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
import java.io.File;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.l2jmobius.L2ClientDat;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.config.ConfigDebug;
import com.l2jmobius.data.GameDataName;
import com.l2jmobius.util.ByteReader;
import com.l2jmobius.util.DebugUtil;
public class DescriptorReader
{
private static DescriptorReader _instance = new DescriptorReader();
final String eq = "=";
final String tab = "\t";
final String nl = System.getProperty("line.separator");
final String semi = ";";
final String lb = "[";
final String rb = "]";
public static DescriptorReader getInstance()
{
return _instance;
}
public String parseData(File currentFile, DatCrypter crypter, Descriptor desc, ByteBuffer data) throws Exception
{
String stringData;
if (desc.isRawData())
{
StringBuilder builder = new StringBuilder();
readVariables(currentFile, crypter, desc.getNodes().get(0), new HashMap<String, Variant>(), data, builder, true);
stringData = builder.toString();
}
else
{
stringData = this.parseData(currentFile, crypter, data, null, desc.getNodes(), 1, new HashMap<String, Variant>(), false, 0).trim();
}
if (desc.getFormat() != null)
{
stringData = desc.getFormat().decode(stringData);
}
int pos = desc.isSafePackage() ? data.position() + 13 : data.position();
if (data.limit() > pos)
{
L2ClientDat.addLogConsole("Unpacked not full " + data.position() + "/" + data.limit() + " diff: " + (data.limit() - pos), true);
}
return stringData;
}
private String parseData(File currentFile, DatCrypter crypter, ByteBuffer data, ParamNode lastNode, List<ParamNode> nodes, int cycleSize, Map<String, Variant> vars, boolean isNameHidden, int cycleNameLevel) throws Exception
{
if (cycleSize > 0)
{
StringBuilder out = new StringBuilder();
for (int i = 0; i < cycleSize; ++i)
{
boolean isAddCycleName = !isNameHidden && (lastNode != null) && lastNode.getEntityType().isCycle();
if (isAddCycleName)
{
for (int k = 0; k < cycleNameLevel; ++k)
{
out.append("\t");
}
out.append(lastNode.getName().concat("_begin"));
++cycleNameLevel;
}
int nodeSize = nodes.size();
int nNode = 0;
for (ParamNode n : nodes)
{
if (n.isIterator())
{
continue;
}
++nNode;
}
if (isNameHidden && (nNode > 1))
{
out.append("{");
}
for (int j = 0; j < nodeSize; ++j)
{
ParamNode node = nodes.get(j);
if (node.getEntityType().isIf())
{
Variant var = vars.get(node.getParamIf());
if ((var == null) || !var.toString().equalsIgnoreCase(node.getValIf()))
{
continue;
}
out.append(this.parseData(currentFile, crypter, data, null, node.getSubNodes(), 1, vars, isNameHidden, cycleNameLevel));
continue;
}
if (!(node.isIterator() || node.getEntityType().isConstant() || isNameHidden || (!node.getEntityType().isWrapper() && !node.isNameHidden())))
{
out.append("\t").append(node.getName()).append("=");
}
if (node.getEntityType().isWrapper())
{
out.append(this.parseData(currentFile, crypter, data, null, node.getSubNodes(), 1, vars, true, cycleNameLevel));
}
else if (node.getEntityType().isCycle())
{
int size;
if (node.getSize() >= 0)
{
size = node.getSize();
}
else
{
Variant var = vars.get(node.getCycleName());
if (var.isInt())
{
size = var.getInt();
}
else if (var.isShort())
{
size = var.getShort();
}
else
{
throw new Exception("Wrong cycle variable format for cycle: " + node.getName() + " iterator: " + node.getCycleName());
}
}
if (node.isNameHidden())
{
out.append("{");
}
out.append(this.parseData(currentFile, crypter, data, node, node.getSubNodes(), size, vars, node.isNameHidden(), cycleNameLevel));
if (node.isNameHidden())
{
out.append("}");
}
}
else if (node.getEntityType().isConstant())
{
out.append(node.getName().replace("\\t", "\t").replace("\\r\\n", "\r\n"));
}
else if (node.getEntityType().isVariable())
{
readVariables(currentFile, crypter, node, vars, data, out, false);
}
if (!node.isIterator() && !node.getEntityType().isConstant() && isNameHidden && (j != (nodeSize - 1)))
{
out.append(";");
}
if (!vars.containsKey(node.getName()))
{
continue;
}
DebugUtil.debugPos(data.position(), node.getName(), vars.get(node.getName()));
}
if (isNameHidden)
{
if (nNode > 1)
{
out.append("}");
}
if (i < (cycleSize - 1))
{
out.append(";");
}
}
if (!isAddCycleName)
{
continue;
}
if (out.charAt(out.length() - 1) != '\n')
{
out.append("\t");
}
out.append(lastNode.getName()).append("_end\r\n");
--cycleNameLevel;
}
return out.toString();
}
return "";
}
private void readVariables(File currentFile, DatCrypter crypter, ParamNode node, Map<String, Variant> vars, ByteBuffer data, StringBuilder out, boolean isRaw) throws Exception
{
switch (node.getType())
{
case UCHAR:
{
short value = (byte) ByteReader.readChar(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Short.class));
break;
}
case UBYTE:
{
int value = ByteReader.readUByte(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Integer.class));
break;
}
case SHORT:
{
short value = ByteReader.readShort(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Short.class));
break;
}
case USHORT:
{
int value = ByteReader.readShort(data) & 65535;
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Integer.class));
break;
}
case UINT:
{
int value = ByteReader.readUInt(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Integer.class));
break;
}
case INT:
{
int value = ByteReader.readInt(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Integer.class));
break;
}
case CNTR:
{
int value = ByteReader.readCompactInt(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Integer.class));
break;
}
case UNICODE:
{
String str = ByteReader.readUtfString(data, isRaw);
if (isRaw)
{
out.append(str);
break;
}
if (!node.isIterator())
{
out.append("[").append(str).append("]");
}
vars.put(node.getName(), new Variant(str, String.class));
break;
}
case ASCF:
{
String str = ByteReader.readString(data, isRaw);
if (isRaw)
{
out.append(str);
break;
}
if (!node.isIterator())
{
out.append("[").append(str).append("]");
}
vars.put(node.getName(), new Variant(str, String.class));
break;
}
case DOUBLE:
{
double value = ByteReader.readDouble(data);
if (!node.isIterator())
{
out.append(new BigDecimal(Double.toString(value)).toPlainString());
}
vars.put(node.getName(), new Variant(value, Double.class));
break;
}
case FLOAT:
{
float value = ByteReader.readFloat(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(Float.valueOf(value), Float.class));
break;
}
case LONG:
{
long value = ByteReader.readLong(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, Long.class));
break;
}
case RGBA:
{
String value = ByteReader.readRGBA(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, String.class));
break;
}
case RGB:
{
String value = ByteReader.readRGB(data);
if (!node.isIterator())
{
out.append(value);
}
vars.put(node.getName(), new Variant(value, String.class));
break;
}
case HEX:
{
int value = ByteReader.readUByte(data);
if (!node.isIterator())
{
String hex = Integer.toHexString(value).toUpperCase();
if (hex.length() == 1)
{
hex = "0" + hex;
}
out.append(hex);
}
vars.put(node.getName(), new Variant(value, Integer.class));
break;
}
case MAP_INT:
{
int index = ByteReader.readUInt(data);
if (ConfigDebug.DAT_REPLACEMENT_NAMES)
{
String paramName = GameDataName.getInstance().getString(currentFile, crypter, index);
if (!node.isIterator())
{
out.append(paramName);
}
vars.put(node.getName(), new Variant(paramName, String.class));
break;
}
if (!node.isIterator())
{
out.append(index);
}
vars.put(node.getName(), new Variant(index, Integer.class));
break;
}
}
}
}

View File

@ -0,0 +1,362 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import com.l2jmobius.clientcryptor.crypt.DatCrypter;
import com.l2jmobius.config.ConfigDebug;
import com.l2jmobius.data.GameDataName;
import com.l2jmobius.util.ByteWriter;
import com.l2jmobius.util.DebugUtil;
import com.l2jmobius.util.Util;
import com.l2jmobius.xml.exceptions.CycleArgumentException;
import com.l2jmobius.xml.exceptions.PackDataException;
public class DescriptorWriter
{
private static final byte[] endFileBytes = new byte[]
{
12,
83,
97,
102,
101,
80,
97,
99,
107,
97,
103,
101,
0
};
private static final DescriptorWriter _instance = new DescriptorWriter();
private static final Logger _log = LogManager.getLogger(DescriptorWriter.class);
public static DescriptorWriter getInstance()
{
return _instance;
}
public static byte[] parseData(File currentFile, DatCrypter crypter, Descriptor desc, String data) throws Exception
{
ByteArrayOutputStream stream = new ByteArrayOutputStream(data.length() / 2);
if (desc.getFormat() != null)
{
data = desc.getFormat().encode(data);
}
if (desc.isRawData())
{
Buffer result = DescriptorWriter.parseNodeValue(currentFile, crypter, data, desc.getNodes().get(0), true);
if (result != null)
{
stream.write((byte[]) result.array());
}
else
{
_log.error("Failed to parse raw data.");
}
}
else
{
ByteBuffer buffer = ByteBuffer.allocateDirect(data.length() * 2);
String lines = data.replace("\r\n", "\t");
HashMap<ParamNode, Integer> counters = new HashMap<>();
DescriptorWriter.packData(currentFile, crypter, buffer, lines, counters, new HashMap<String, String>(), new HashMap<ParamNode, String>(), desc.getNodes());
try
{
buffer.flip();
stream.write(ByteBuffer.allocate(buffer.limit()).put(buffer).array());
}
catch (IOException e)
{
DebugUtil.getLogger().error(e.getMessage(), e);
}
}
if (desc.isSafePackage())
{
stream.write(endFileBytes);
}
return stream.toByteArray();
}
private static void packData(File currentFile, DatCrypter crypter, ByteBuffer buffer, String lines, Map<ParamNode, Integer> counters, Map<String, String> paramMap, Map<ParamNode, String> mapData, List<ParamNode> nodes) throws Exception
{
for (ParamNode node : nodes)
{
try
{
String param;
if (node.isIterator())
{
counters.put(node, buffer.position());
continue;
}
if (node.getEntityType().isCycle())
{
if (!node.isNameHidden())
{
Pattern pattern = Pattern.compile("\\b" + node.getName().concat("_begin\\b(.*?)\\b").concat(node.getName()).concat("_end\\b"), 32);
Matcher m = pattern.matcher(lines);
ArrayList<String> list = new ArrayList<>();
while (m.find())
{
list.add(m.group(1));
}
DescriptorWriter.writeSize(currentFile, crypter, buffer, counters, node, list.size());
for (String str : list)
{
paramMap.putAll(Util.stringToMap(str));
DescriptorWriter.packData(currentFile, crypter, buffer, str, counters, paramMap, mapData, node.getSubNodes());
}
continue;
}
param = DescriptorWriter.getDataString(node, node.getName(), paramMap, mapData);
if (param == null)
{
throw new Exception("Error getDataString == null node: " + node.getName());
}
if (param.isEmpty() || param.equals("{}"))
{
DescriptorWriter.writeSize(currentFile, crypter, buffer, counters, node, 0);
continue;
}
List<String> subParams = Util.splitList(param);
int cycleSize = subParams.size();
if ((node.getSize() > 0) && (node.getSize() != cycleSize))
{
throw new Exception("Wrong static cycle count for cycle: " + node.getName() + " size: " + subParams.size() + " params: " + Arrays.toString(subParams.toArray()));
}
DescriptorWriter.writeSize(currentFile, crypter, buffer, counters, node, cycleSize);
int nPramNode = 0;
int nCycleNode = 0;
for (ParamNode n : node.getSubNodes())
{
if (n.getEntityType().isCycle())
{
++nCycleNode;
continue;
}
if (n.isIterator())
{
continue;
}
++nPramNode;
}
for (String subParam : subParams)
{
int paramIndex = 0;
List<String> sub2Params = (nPramNode > 0) || (nCycleNode > 1) ? Util.splitList(subParam) : Collections.singletonList(subParam);
for (ParamNode n : node.getSubNodes())
{
if (n.isIterator() || n.getEntityType().isConstant())
{
continue;
}
if (paramIndex >= sub2Params.size())
{
throw new Exception("Wrong cycle param count for cycle: " + node.getName() + " paramIndex: " + paramIndex + " params: " + Arrays.toString(sub2Params.toArray()));
}
mapData.put(n, sub2Params.get(paramIndex++));
}
DescriptorWriter.packData(currentFile, crypter, buffer, lines, counters, paramMap, mapData, node.getSubNodes());
}
continue;
}
if (node.getEntityType().isWrapper())
{
List<String> subParams = Util.splitList(DescriptorWriter.getDataString(node, node.getName(), paramMap, mapData));
int paramIndex = 0;
for (ParamNode n : node.getSubNodes())
{
if (n.isIterator() || n.getEntityType().isConstant())
{
continue;
}
mapData.put(n, subParams.get(paramIndex++));
}
DescriptorWriter.packData(currentFile, crypter, buffer, lines, counters, paramMap, mapData, node.getSubNodes());
continue;
}
if (node.getEntityType().isVariable())
{
buffer.put((byte[]) DescriptorWriter.parseNodeValue(currentFile, crypter, DescriptorWriter.getDataString(node, node.getName(), paramMap, mapData), node, false).array());
continue;
}
if (!node.getEntityType().isIf() || ((param = DescriptorWriter.getDataString(node, node.getParamIf(), paramMap, mapData)) == null) || !node.getValIf().equalsIgnoreCase(param))
{
continue;
}
DescriptorWriter.packData(currentFile, crypter, buffer, lines, counters, paramMap, mapData, node.getSubNodes());
}
catch (Exception e)
{
throw new PackDataException(e + "\r\n-node: " + node + "\r\n\tparam: " + paramMap.get(node.getName()));
}
}
}
private static Buffer parseNodeValue(File currentFile, DatCrypter crypter, String data, ParamNode node, boolean isRaw)
{
ParamType nodeType = node.getType();
if (nodeType == null)
{
_log.error("Incorrect node type for node " + node);
return null;
}
try
{
switch (nodeType)
{
case UCHAR:
{
return ByteWriter.writeChar(Byte.parseByte(data));
}
case CNTR:
{
return ByteWriter.writeCompactInt(Integer.parseInt(data));
}
case UBYTE:
{
return ByteWriter.writeUByte(Short.parseShort(data));
}
case SHORT:
{
return ByteWriter.writeShort(Short.parseShort(data));
}
case USHORT:
{
return ByteWriter.writeUShort(Integer.parseInt(data));
}
case UINT:
case INT:
{
return ByteWriter.writeInt(Integer.parseInt(data));
}
case UNICODE:
{
return ByteWriter.writeUtfString(isRaw ? data : data.substring(1, data.length() - 1), isRaw);
}
case ASCF:
{
return ByteWriter.writeString(isRaw ? data : data.substring(1, data.length() - 1), isRaw);
}
case DOUBLE:
{
return ByteWriter.writeDouble(Double.parseDouble(data));
}
case FLOAT:
{
return ByteWriter.writeFloat(Float.parseFloat(data));
}
case LONG:
{
return ByteWriter.writeLong(Long.parseLong(data));
}
case RGBA:
{
return ByteWriter.writeRGBA(data);
}
case RGB:
{
return ByteWriter.writeRGB(data);
}
case HEX:
{
return ByteWriter.writeByte((byte) (Integer.parseInt(data, 16) & 255));
}
case MAP_INT:
{
if (ConfigDebug.DAT_REPLACEMENT_NAMES)
{
return ByteWriter.writeInt(GameDataName.getInstance().getId(currentFile, crypter, node, data));
}
return ByteWriter.writeInt(Integer.parseInt(data));
}
}
DebugUtil.getLogger().error("Unsupported primitive type " + (nodeType));
}
catch (Exception e)
{
_log.error("Failed to parse value for node " + node + " data: " + data, e);
}
return null;
}
private static String getDataString(ParamNode node, String name, Map<String, String> paramMap, Map<ParamNode, String> mapData)
{
if ((mapData != null) && mapData.containsKey(node))
{
return mapData.get(node);
}
return paramMap.get(name);
}
private static void writeSize(File currentFile, DatCrypter crypter, ByteBuffer buffer, Map<ParamNode, Integer> counters, ParamNode node, int cycleSize) throws CycleArgumentException
{
if (!node.isSkipWriteSize() && (node.getSize() < 0))
{
ParamNode iterator = node.getTmpIterator();
if (iterator == null)
{
counters.keySet().stream().filter(n -> n.getName().equals(node.getCycleName())).forEach(node::setTmpIterator);
iterator = node.getTmpIterator();
if (iterator == null)
{
throw new CycleArgumentException("Invalid argument [" + node.getName() + "] for cycle");
}
}
Buffer buff = DescriptorWriter.parseNodeValue(currentFile, crypter, String.valueOf(cycleSize), iterator, false);
int pos = counters.get(iterator);
if (pos >= 0)
{
int rem = buffer.position();
if (pos == rem)
{
buffer.put((byte[]) buff.array());
}
else
{
byte[] arrayNext = new byte[rem - pos];
buffer.position(pos);
buffer.get(arrayNext);
buffer.position(pos);
buffer.put((byte[]) buff.array());
buffer.put(arrayNext);
}
counters.remove(iterator);
}
}
}
}

View File

@ -0,0 +1,189 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
import java.util.ArrayList;
import java.util.List;
public class ParamNode
{
private String _name;
private int _size = -1;
private boolean _hidden = false;
private String _cycleName;
private final ParamNodeType _entityType;
private final ParamType _type;
private List<ParamNode> _sub;
private boolean _isIterator;
private ParamNode _tmpIterator;
private boolean _skipWriteSize;
private String _paramIf;
private String _valIf;
ParamNode(String name, ParamNodeType entityType, ParamType type)
{
_name = name;
_entityType = entityType;
_type = type;
}
public void setName(String name)
{
_name = name;
}
public void setSize(int size)
{
_size = size;
}
public int getSize()
{
return _size;
}
void setIterator()
{
_isIterator = true;
}
void setHidden()
{
_hidden = true;
}
boolean isIterator()
{
return _isIterator && (_size < 0);
}
boolean isNameHidden()
{
return _hidden;
}
public String getName()
{
return _name;
}
ParamNodeType getEntityType()
{
return _entityType;
}
public ParamType getType()
{
return _type;
}
synchronized ParamNode copy()
{
ParamNode node = new ParamNode(getName(), getEntityType(), getType());
if (isNameHidden())
{
node.setHidden();
}
if (isIterator())
{
node.setIterator();
}
node.setSkipWriteSize(isSkipWriteSize());
node.setCycleName(getCycleName());
if (getSubNodes() != null)
{
ArrayList<ParamNode> list = new ArrayList<>();
for (ParamNode n : getSubNodes())
{
ParamNode copyN = n.copy();
list.add(copyN);
}
node.addSubNodes(list);
}
return node;
}
synchronized void addSubNodes(List<ParamNode> n)
{
if (_sub == null)
{
_sub = new ArrayList<>();
}
_sub.addAll(n);
}
List<ParamNode> getSubNodes()
{
return _sub;
}
@Override
public String toString()
{
return _name + "[" + (_entityType) + "][" + _cycleName + "][" + (_type) + "]";
}
ParamNode getTmpIterator()
{
return _tmpIterator;
}
void setTmpIterator(ParamNode tmpIterator)
{
_tmpIterator = tmpIterator;
}
boolean isSkipWriteSize()
{
return _skipWriteSize;
}
void setSkipWriteSize(boolean skipWrite)
{
_skipWriteSize = skipWrite;
}
void setParamIf(String paramIf)
{
_paramIf = paramIf;
}
void setValIf(String valIf)
{
_valIf = valIf;
}
String getParamIf()
{
return _paramIf;
}
String getValIf()
{
return _valIf;
}
String getCycleName()
{
return _cycleName;
}
void setCycleName(String cycleName)
{
_cycleName = cycleName;
}
}

View File

@ -0,0 +1,55 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
enum ParamNodeType
{
FOR,
WRAPPER,
CONSTANT,
VARIABLE,
IF;
private ParamNodeType()
{
}
boolean isCycle()
{
return this == FOR;
}
public boolean isWrapper()
{
return this == WRAPPER;
}
boolean isConstant()
{
return this == CONSTANT;
}
boolean isVariable()
{
return this == VARIABLE;
}
boolean isIf()
{
return this == IF;
}
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
enum ParamType
{
UCHAR,
CNTR,
UBYTE,
USHORT,
SHORT,
UINT,
STRING,
INT,
UNICODE,
ASCF,
DOUBLE,
FLOAT,
LONG,
RGBA,
RGB,
HEX,
MAP_INT;
private ParamType()
{
}
}

View File

@ -0,0 +1,80 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml;
public class Variant
{
private final Object _value;
private final Class<?> _type;
Variant(Object value, Class<?> type)
{
_value = type.cast(value);
_type = type;
}
public final boolean isInt()
{
return _type == Integer.class;
}
public final boolean isShort()
{
return _type == Short.class;
}
public final boolean isFloat()
{
return _type == Float.class;
}
public final boolean isDouble()
{
return _type == Double.class;
}
public final int getInt()
{
return (Integer) _value;
}
public final short getShort()
{
return (Short) _value;
}
public final float getFloat()
{
return ((Float) _value).floatValue();
}
public double getDouble()
{
return (Double) _value;
}
public long getLong()
{
return (Long) _value;
}
@Override
public final String toString()
{
return String.valueOf(_value);
}
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml.exceptions;
public class CycleArgumentException extends Exception
{
public CycleArgumentException(String message)
{
super(message);
}
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.xml.exceptions;
public class PackDataException extends Exception
{
public PackDataException(String message)
{
super(message);
}
}