mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-09-26 21:30:46 +00:00
Added: #2360 SOL file (Flash Local Shared Object - flash cookie) editor
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.sol;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf0.Amf0InputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf0.Amf0OutputStream;
|
||||
import com.jpexs.helpers.MemoryInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class FilePathTag extends Tag {
|
||||
|
||||
public static final int ID = 3;
|
||||
|
||||
public String filePath;
|
||||
|
||||
public FilePathTag(byte[] data, boolean forceWriteAsLong) {
|
||||
super(ID, "DefineFilePath", data, forceWriteAsLong);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readData() throws IOException {
|
||||
Amf0InputStream is = new Amf0InputStream(new MemoryInputStream(data));
|
||||
int filePathLen = is.readU16("filePath");
|
||||
filePath = new String(is.readBytes(filePathLen), "UTF-8");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(OutputStream os) throws IOException {
|
||||
Amf0OutputStream aos = new Amf0OutputStream(os);
|
||||
byte[] filePathData = filePath.getBytes("UTF-8");
|
||||
aos.writeU16(filePathData.length);
|
||||
aos.writeBytes(filePathData);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.sol;
|
||||
|
||||
import com.jpexs.decompiler.flash.amf.amf0.Amf0InputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf0.Amf0OutputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3InputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Amf3OutputStream;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.NoSerializerExistsException;
|
||||
import com.jpexs.decompiler.flash.amf.amf3.Traits;
|
||||
import com.jpexs.helpers.MemoryInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class LsoTag extends Tag {
|
||||
|
||||
public static final int ID = 2;
|
||||
|
||||
public String fileName;
|
||||
public int amfVersion;
|
||||
public Map<String, Object> amfValues = new LinkedHashMap<>();
|
||||
|
||||
public LsoTag(byte[] data, boolean forceWriteAsLong) {
|
||||
super(ID, "DefineLso", data, forceWriteAsLong);
|
||||
}
|
||||
|
||||
public LsoTag(String fileName, int amfVersion, Map<String, Object> amfValues) {
|
||||
super(ID, "DefineLso", new byte[0], true);
|
||||
this.fileName = fileName;
|
||||
this.amfVersion = amfVersion;
|
||||
this.amfValues = amfValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readData() throws IOException {
|
||||
Amf0InputStream is = new Amf0InputStream(new MemoryInputStream(data));
|
||||
|
||||
byte[] expectedSig = new byte[]{'T', 'C', 'S', 'O'};
|
||||
byte[] actualSig = is.readBytes(4);
|
||||
if (!Arrays.equals(actualSig, expectedSig)) {
|
||||
throw new IllegalArgumentException("Not a SOL file - invalid signature");
|
||||
}
|
||||
is.skip(6); //00 04 00 00 00 00
|
||||
int filenameLen = is.readU16("filenameLen");
|
||||
fileName = new String(is.readBytes(filenameLen), "UTF-8");
|
||||
|
||||
amfVersion = (int) is.readU32("amfVersion");
|
||||
if (amfVersion != 0 && amfVersion != 3) {
|
||||
throw new IllegalArgumentException("Unsupported AMF version");
|
||||
}
|
||||
byte[] amfData = is.readBytes(is.available());
|
||||
if (amfVersion == 0) {
|
||||
Amf0InputStream ais = new Amf0InputStream(new MemoryInputStream(amfData));
|
||||
List<Object> complexObjects = new ArrayList<>();
|
||||
while (ais.available() > 0) {
|
||||
String varName = ais.readUtf8("varName");
|
||||
try {
|
||||
Object varValue = ais.readValue("varValue");
|
||||
amfValues.put(varName, varValue);
|
||||
} catch (NoSerializerExistsException ex) {
|
||||
throw new IllegalArgumentException("Serializer for class " + ex.getClassName() + " not found");
|
||||
}
|
||||
ais.read(); //ending byte
|
||||
}
|
||||
ais.resolveMapReferences(amfValues);
|
||||
}
|
||||
|
||||
if (amfVersion == 3) {
|
||||
Amf3InputStream ais = new Amf3InputStream(new MemoryInputStream(amfData));
|
||||
List<Object> objectsTable = new ArrayList<>();
|
||||
List<Traits> traitsTable = new ArrayList<>();
|
||||
List<String> stringTable = new ArrayList<>();
|
||||
|
||||
while (ais.available() > 0) {
|
||||
String varName = ais.readUtf8Vr("varName", new ArrayList<>());
|
||||
try {
|
||||
Object varValue = ais.readValue("varValue", new HashMap<>(), objectsTable, traitsTable, stringTable);
|
||||
amfValues.put(varName, varValue);
|
||||
} catch (NoSerializerExistsException ex) {
|
||||
throw new IllegalArgumentException("Serializer for class " + ex.getClassName() + " not found");
|
||||
}
|
||||
ais.read(); //ending byte
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(OutputStream os) throws IOException {
|
||||
Amf0OutputStream aos = new Amf0OutputStream(os);
|
||||
aos.write(new byte[] {'T', 'C', 'S', 'O'});
|
||||
aos.write(new byte[] {0x00,0x04,0x00,0x00,0x00,0x00});
|
||||
byte[] fileNameData = fileName.getBytes("UTF-8");
|
||||
aos.writeU16(fileNameData.length);
|
||||
aos.write(fileNameData);
|
||||
aos.writeU32(amfVersion);
|
||||
|
||||
if (amfVersion == 0) {
|
||||
List<Object> complexObjects = new ArrayList<>();
|
||||
for(String key: amfValues.keySet()) {
|
||||
aos.writeUtf8(key);
|
||||
aos.writeValue(amfValues.get(key), complexObjects);
|
||||
aos.write(0);
|
||||
}
|
||||
}
|
||||
if (amfVersion == 3) {
|
||||
Amf3OutputStream a3os = new Amf3OutputStream(os);
|
||||
List<String> stringTable = new ArrayList<>();
|
||||
List<Traits> traitTable = new ArrayList<>();
|
||||
List<Object> objectTable = new ArrayList<>();
|
||||
for(String key: amfValues.keySet()) {
|
||||
try {
|
||||
a3os.writeUtf8Vr(key, new ArrayList<>()); //Intentionally not using string table
|
||||
a3os.writeValue(amfValues.get(key), new HashMap<>(), stringTable, traitTable, objectTable);
|
||||
} catch (NoSerializerExistsException ex) {
|
||||
throw new IllegalArgumentException("Serializer not found for class " + ex.getClassName());
|
||||
}
|
||||
aos.write(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.sol;
|
||||
|
||||
import com.jpexs.decompiler.flash.EndOfStreamException;
|
||||
import com.jpexs.decompiler.flash.amf.amf0.Amf0OutputStream;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf0.Amf0Exporter;
|
||||
import com.jpexs.decompiler.flash.exporters.amf.amf3.Amf3Exporter;
|
||||
import com.jpexs.decompiler.flash.importers.amf.amf0.Amf0Importer;
|
||||
import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3Importer;
|
||||
import com.jpexs.decompiler.flash.importers.amf.amf3.Amf3ParseException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class SolFile {
|
||||
|
||||
private DataInputStream is;
|
||||
|
||||
private long pos = 0;
|
||||
|
||||
private List<Tag> tags = new ArrayList<>();
|
||||
|
||||
public SolFile(InputStream is) throws IOException {
|
||||
this.is = new DataInputStream(is);
|
||||
readTags();
|
||||
}
|
||||
|
||||
public SolFile(String fileName, int amfVersion, Map<String, Object> amfValues) {
|
||||
tags.add(new LsoTag(fileName, amfVersion, amfValues));
|
||||
}
|
||||
|
||||
public void writeTo(OutputStream os) throws IOException {
|
||||
for (Tag t: tags) {
|
||||
writeTag(os, t);
|
||||
}
|
||||
}
|
||||
|
||||
private void readTags() throws IOException {
|
||||
while (is.available() > 0) {
|
||||
Tag t = readTag();
|
||||
t.readData();
|
||||
tags.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
private int readInternal() throws IOException {
|
||||
int ret = is.read();
|
||||
if (ret == -1) {
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
pos++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private int readUI16() throws IOException {
|
||||
int b1 = readInternal();
|
||||
int b2 = readInternal();
|
||||
int ret = (b1 << 8) + b2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private long readUI32() throws IOException {
|
||||
int b1 = readInternal();
|
||||
int b2 = readInternal();
|
||||
int b3 = readInternal();
|
||||
int b4 = readInternal();
|
||||
|
||||
return ((b1 << 24) + (b2 << 16) + (b3 << 8) + b4) & 0xffffffff;
|
||||
}
|
||||
|
||||
private byte[] readBytes(int count) throws IOException {
|
||||
byte[] ret = new byte[count];
|
||||
try {
|
||||
is.readFully(ret);
|
||||
} catch (EOFException eof) {
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
pos += count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private void skipBytes(int count) throws IOException {
|
||||
is.skip(count);
|
||||
pos += count;
|
||||
}
|
||||
|
||||
private void writeTag(OutputStream os, Tag t) throws IOException{
|
||||
Amf0OutputStream aos = new Amf0OutputStream(os);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
t.writeData(baos);
|
||||
int contentLength = baos.size();
|
||||
if (contentLength > 0x3F || t.forceWriteAsLong) {
|
||||
aos.writeU16((t.getTagType() << 6) | 0x3F);
|
||||
aos.writeU32(baos.size());
|
||||
} else {
|
||||
aos.writeU16((t.getTagType() << 6) | contentLength);
|
||||
}
|
||||
aos.writeBytes(baos.toByteArray());
|
||||
}
|
||||
|
||||
private Tag readTag() throws IOException {
|
||||
long headerPos = pos;
|
||||
int tagTypeAndLength = readUI16();
|
||||
int contentLength = tagTypeAndLength & 0x3F;
|
||||
boolean writeAsLong = false;
|
||||
if (contentLength == 0x3F) {
|
||||
contentLength = (int) readUI32();
|
||||
writeAsLong = true;
|
||||
}
|
||||
int tagType = tagTypeAndLength >> 6;
|
||||
byte[] data = readBytes(contentLength);
|
||||
|
||||
switch (tagType) {
|
||||
case LsoTag.ID:
|
||||
return new LsoTag(data, writeAsLong);
|
||||
case FilePathTag.ID:
|
||||
return new FilePathTag(data, writeAsLong);
|
||||
default:
|
||||
return new UnknownTag(tagType, data, writeAsLong);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
private LsoTag getLsoTag() throws IOException {
|
||||
for (Tag t : getTags()) {
|
||||
if (t instanceof LsoTag) {
|
||||
return (LsoTag) t;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getAmfVersion() throws IOException {
|
||||
LsoTag lsoTag = getLsoTag();
|
||||
if (lsoTag == null) {
|
||||
return -1;
|
||||
}
|
||||
return lsoTag.amfVersion;
|
||||
}
|
||||
|
||||
public String getFileName() throws IOException {
|
||||
LsoTag lsoTag = getLsoTag();
|
||||
if (lsoTag == null) {
|
||||
return null;
|
||||
}
|
||||
return lsoTag.fileName;
|
||||
}
|
||||
|
||||
public Map<String, Object> getAmfValues() throws IOException {
|
||||
LsoTag lsoTag = getLsoTag();
|
||||
if (lsoTag == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return lsoTag.amfValues;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws FileNotFoundException, IOException {
|
||||
SolFile sol0 = new SolFile(new FileInputStream("testdata/sharedobjects/data/amf0test.sol"));
|
||||
sol0.writeTo(new FileOutputStream("testdata/sharedobjects/data/out/amf0test.sol"));
|
||||
for (Tag t : sol0.getTags()) {
|
||||
if (t instanceof LsoTag) {
|
||||
LsoTag lt = (LsoTag) t;
|
||||
String amf0string = Amf0Exporter.amfMapToString(lt.amfValues, 0, "\r\n");
|
||||
Amf0Importer importer = new Amf0Importer();
|
||||
try {
|
||||
Map<String, Object> imported = importer.stringToAmfMap(amf0string);
|
||||
String amf0stringNew = Amf0Exporter.amfMapToString(imported, 0, "\r\n");
|
||||
System.err.println("same0 = " + amf0stringNew.equals(amf0string));
|
||||
} catch (Amf3ParseException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SolFile sol3 = new SolFile(new FileInputStream("testdata/sharedobjects/data/amf3test.sol"));
|
||||
sol3.writeTo(new FileOutputStream("testdata/sharedobjects/data/out/amf3test.sol"));
|
||||
for (Tag t : sol3.getTags()) {
|
||||
if (t instanceof LsoTag) {
|
||||
LsoTag lt = (LsoTag) t;
|
||||
String amf3string = Amf3Exporter.amfMapToString(lt.amfValues, " ", "\r\n", 0);
|
||||
Amf3Importer importer = new Amf3Importer();
|
||||
try {
|
||||
Map<String, Object> imported = importer.stringToAmfMap(amf3string);
|
||||
String amf3stringNew = Amf3Exporter.amfMapToString(imported, " ", "\r\n", 0);;
|
||||
System.err.println("same3 = " + amf3stringNew.equals(amf3string));
|
||||
} catch (Amf3ParseException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.sol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* SOL file tag.
|
||||
* @author JPEXS
|
||||
*/
|
||||
public abstract class Tag {
|
||||
protected boolean forceWriteAsLong;
|
||||
private final int tagType;
|
||||
private final String tagName;
|
||||
protected byte[] data;
|
||||
|
||||
public Tag(int tagType, String tagName, byte[] data, boolean forceWriteAsLong) {
|
||||
this.tagType = tagType;
|
||||
this.tagName = tagName;
|
||||
this.data = data;
|
||||
this.forceWriteAsLong = forceWriteAsLong;
|
||||
}
|
||||
|
||||
public abstract void readData() throws IOException;
|
||||
|
||||
public abstract void writeData(OutputStream os) throws IOException;
|
||||
|
||||
public int getTagType() {
|
||||
return tagType;
|
||||
}
|
||||
|
||||
public boolean isForceWriteAsLong() {
|
||||
return forceWriteAsLong;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2010-2024 JPEXS, All rights reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 3.0 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.sol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class UnknownTag extends Tag {
|
||||
|
||||
public UnknownTag(int tagType, byte[] data, boolean forceWriteAsLong) {
|
||||
super(tagType, "Unknown(" + tagType + ")", data, forceWriteAsLong);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readData() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(OutputStream os) throws IOException {
|
||||
os.write(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* Reading Local Shared Objects.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.sol;
|
||||
Reference in New Issue
Block a user