ケータイ少女 script.arc的解压缩程序 (Java)

嗯,这个也是快一年前写的了。当时在澄空看到有人想要解手机少女的文件,就随便写了个程序来处理这个script.arc与string.arc。另外几个arc文件用下面的程序处理不了,因为文件头里的信息没有被正确的处理。嘛,后来[url=http://plaza.rakuten.co.jp/asmodean]asmodean[/url]也放出了处理这个游戏的工具而且是用C++写的,我就没必要继续把这个程序做完。

也是由于使用了[url=http://www.hotpixel.net/software.html]BlowfishJ[/url]的BinConverter.java,所以以下代码以[url=http://www.gnu.org/copyleft/lesser.html]LGPL[/url]许可证发布。

Archiver.java:
/*
* @(#)Archiver.java 2007/05/15
* Written by rednaxela / FX
*/

import java.io.*;
import java.util.*;

/*

Data structures:

//--------------------------------
typedef struct tagHEADER {
DWORD id; // IDENTIFIER, BE AD CA A8, little-endian ("ARCW" xor 0xFF)
DWORD ver; // VERSION, locked to 0x00
DWORD fileSize; // total size of current archive
DWORD data_offset; // begin offset of file data
} HEADER; // 16 bytes

typedef struct tagSECINFO {
DWORD sectionLength; // sectionLength = len(SECINFO)(=0x0C) + len(SECDATA)
DWORD flags; // flags: NOT_MSK = 1, LZSS_MSK = 2
DWORD origSize; // original size of SECDATA after inflation
} SECINFO; // 12 bytes

typedef struct tagSECDATA {
BYTE data[SECINFO.sectionLength - 12];
// when SECINFO.flags = NOT_MSK | LZSS_MSK, the contents is both encrypted by NOT
// and compressed by LZSS.
} SECDATA;

struct INDEXBLOCK1 {

struct INDEXENTRY {
DWORD offset; // actualOfs = offset + totalHeaderLength - 0x0C
DWORD length; // length in archive
DWORD flags; // NOT_MSK = 1, LZSS_MSK = 2
DWORD origLen; // original file length
};

DWORD reserved1; // some sort of flag I guess
DWORD reserved2; // seems to be always zero
DWORD entryCount; // number of entries in this block
DWORD blockLength; // length of the remaining part of this block

DWORD nameOffset[entryCount]; // offset relative to nameSecData after decode and decompression
INDEXENTRY entry[entryCount]; // TOC entrys
};

struct INDEXBLOCK2 {
}; // structure is unknown


//--------------------------------

arc file:

HEADER header; // 1 header per archive
SECINFO nameSecInfo; // 1st of SECINFO in archive
SECDATA nameSecData; // 1st of SECDATA in archive, contents of which are filenames in UNICODE
SECINFO indexSecInfo; // 2nd of SECINFO in archive
SECDATA indexSecData; // 2nd of SECDATA in archive, contents of which indicates offset, length, flag info of file data.
bytes data[]; // remaining part of archive, which is file data

//--------------------------------

indexSecData after decode and decompression:

DWORD blockCount;
INDEXBLOCK1 block1;
// INDEXBLOCK2 block2; // this is where I've got trouble figuring out.

*/

/**
* Demonstrating archive operations on *.arc files as seen in Ketai-s.
*/
public class Archiver {

static final int IDENTIFIER = 0xA8BCADBE;
static final int VERSION = 0x00;
static final int NOT_MSK = 1;
static final int LZSS_MSK = 2;

static final String USAGE = "Usage: java Archiver [option] srcPath dstPath\n"
+ "options:\n"
+ "[l]ist\n"
+ "[e]xtract\n"
+ "[a]rchive";

static ArrayList<IndexEntry> index = null;
/**
* the application entry point
* @param args (command line) parameters
*/
public static void main(String[] args) throws Exception {

// check command line arguments
if (args.length != 3) error(USAGE);

if ("l".equals(args[0].trim())) { // extract files from archive

String srcPath = args[1].trim();
String dstPath = args[2].trim();
if (srcPath.length() == 0) error("2nd argument not exist.");
if (dstPath.length() == 0) error("3rd argument not exist.");

listFiles(srcPath, dstPath);

} else if ("e".equals(args[0].trim())) { // extract files from archive

String srcPath = args[1].trim();
String dstPath = args[2].trim();
if (srcPath.length() == 0) error("2nd argument not exist.");
if (dstPath.length() == 0) error("3rd argument not exist.");

extractFiles(srcPath, dstPath);

} else if ("a".equals(args[0].trim())) { // pack files into archive

String srcPath = args[1].trim();
String dstPath = args[2].trim();
if (srcPath.length() == 0) error("2nd argument not exist.");
if (dstPath.length() == 0) error("3rd argument not exist.");

packFiles(srcPath, dstPath);

} else error(USAGE);
}

private static void listFiles(String srcFile, String listFile) throws Exception {
// open source archive
File arc = new File(srcFile);
if (!arc.exists()) error("Archive " + srcFile + " doesn't exist");

long contentOfs = -1L;
FileInputStream fis = new FileInputStream(arc);
DataInputStream dis = new DataInputStream(fis);
index = new ArrayList<IndexEntry>();

// match archive IDENTIFIER
int id = flipEndian(dis.readInt());
if (IDENTIFIER != id) error("Archive file not supported. Unexpected identifier 0x" + Integer.toHexString(id).toUpperCase());

// match archive version
int ver = flipEndian(dis.readInt());
if (VERSION != ver) error("Archive file not supported. Unexpected version.");

// match file size
int fsize = flipEndian(dis.readInt());
if ((int)(arc.length()) != fsize) error("Archive file not supported. Unexpected file size.");

// probable checksum
int checksum = flipEndian(dis.readInt());
// if (checksum(arc) != checksum)) error("Archive file not supported. Unexpected checksum.");
System.err.println("CHECKSUM: 0x" + Integer.toHexString(checksum).toUpperCase());

// read index entries
byte[] nameBuf = readIndexBlock(fis);
byte[] infoBuf = readIndexBlock(fis);
parseIndex(nameBuf, infoBuf);

// extract files
int baseOfs = (int)(fis.getChannel().position()); // check this, might not be correct
PrintStream out = new PrintStream(new FileOutputStream(listFile));
System.setOut(out); // stdout redirection
for (IndexEntry entry : index) {
// print file info
printFileInfo(entry, baseOfs);
}
}

private static void extractFiles(String srcFile, String dstDir) throws Exception {
// open source archive
File arc = new File(srcFile);
if (!arc.exists()) error("Archive " + srcFile + " doesn't exist");

long contentOfs = -1L;
FileInputStream fis = new FileInputStream(arc);
DataInputStream dis = new DataInputStream(fis);
index = new ArrayList<IndexEntry>();

// match archive IDENTIFIER
int id = flipEndian(dis.readInt());
if (IDENTIFIER != id) error("Archive file not supported. Unexpected identifier 0x" + Integer.toHexString(id).toUpperCase());

// match archive version
int ver = flipEndian(dis.readInt());
if (VERSION != ver) error("Archive file not supported. Unexpected version.");

// match file size
int fsize = flipEndian(dis.readInt());
if ((int)(arc.length()) != fsize) error("Archive file not supported. Unexpected file size.");

// probable checksum
int checksum = flipEndian(dis.readInt());
// if (checksum(arc) != checksum)) error("Archive file not supported. Unexpected checksum.");
System.err.println("CHECKSUM: 0x" + Integer.toHexString(checksum).toUpperCase());

// read index entries
byte[] nameBuf = readIndexBlock(fis);
byte[] infoBuf = readIndexBlock(fis);
parseIndex(nameBuf, infoBuf);

// extract files
int baseOfs = (int)(fis.getChannel().position()); // check this, might not be correct
for (IndexEntry entry : index) {

// print file info
printFileInfo(entry, baseOfs);

// data correctness check - this support ordered file archive only
// to support out-of-order archives, use RandomAccessFile instead.
if ((int)(fis.getChannel().position()) != entry.getOffset() + baseOfs) {
// error("Bad file content order "
// + entry.getFilename() + " @ 0x"
// + Integer.toHexString((int)(fis.getChannel().position())));
System.err.println("Out-of-order. " + entry.getFilename() + " @ 0x"
+ Integer.toHexString((int)(fis.getChannel().position())));
fis.getChannel().position(entry.getOffset() + baseOfs);
}

// get buffer and read file
byte[] writeBuf = readBlock(fis, entry.getLength(), entry.getFlags(), entry.getOrigLength());
// write file
writeFile(writeBuf, entry.getFilename(), dstDir);
}
fis.close();
}

private static void packFiles(String srcDir, String dstFile) throws Exception {
// TODO
// not implemented yet
}

private static void parseIndex(byte[] nameBuf, byte[] infoBuf) throws IOException {
DataInputStream dis = new DataInputStream(
new ByteArrayInputStream(infoBuf));
int blockCount = flipEndian(dis.readInt());

// PROBLEM 1
// This is where my program doesn't go compatible with the image.arc
// and sound.arc files. Their blockCount is 2. The 1st block seem
// to be the same as string.arc and script.arc, but the 2nd block is severely
// obsfucated that I don't know what the program is doing.

// if (blockCount != 1) error("Archive not supported. BlockCount is " + blockCount);

for (int i = 0; i < blockCount; ++i) {
int reserved1 = flipEndian(dis.readInt()); // unknown usage
int reserved2 = flipEndian(dis.readInt()); // not used
int entryCount = flipEndian(dis.readInt()); // entry count in current block
int blockSize = flipEndian(dis.readInt()); // current block size

// System.err.println(Arrays.toString(nameBuf));
System.err.println("reserved1: " + reserved1);
System.err.println("reserved2: " + reserved2);
System.err.println("entry count: " + entryCount);
System.err.println("block size: " + blockSize);

// PROBLEM 2

// if (4 * 5 * entryCount != blockSize) error("Index error. Wrong block size.\n"
// + "Count=" + entryCount + " BlockSize=" + blockSize);

int[] nameOfs = new int[entryCount];
for (int j = 0; j < entryCount; ++j) {
nameOfs[j] = flipEndian(dis.readInt());
}

for (int j = 0; j < entryCount; ++j) {
IndexEntry entry = new IndexEntry();
entry.setFilename(readWString(nameBuf, nameOfs[j]));
entry.setOffset(flipEndian(dis.readInt()));
entry.setLength(flipEndian(dis.readInt()) - 12);
entry.setFlags(flipEndian(dis.readInt()));
entry.setOrigLength(flipEndian(dis.readInt()));

index.add(entry);
}
}
}

private static int flipEndian(int i) {
byte[] bytes = new byte[4];
bytes[0] = (byte)(i >>> 24);
bytes[1] = (byte)(i >>> 16);
bytes[2] = (byte)(i >>> 8);
bytes[3] = (byte) i;

i = bytes[3] << 24;
i |= (bytes[2] << 16) & 0x0ff0000;
i |= (bytes[1] << 8) & 0x000ff00;
i |= bytes[0] & 0x00000ff;

return i;
}

private static void invertBitsArray(byte[] arr) {
for (int i = 0; i < arr.length; ++i) {
arr[i] = (byte)~arr[i];
}
}
private static void error(String cause) {
System.err.println("Error " + cause);
System.exit(1);
}

private static void printFileInfo(IndexEntry entry, int baseOfs) {
System.out.print("File " + entry.getFilename());
System.out.print(" @ 0x" + Integer.toHexString(entry.getOffset() + baseOfs).toUpperCase());
System.out.print(" size: 0x" + Integer.toHexString(entry.getLength()).toUpperCase());
System.out.println(" origSize: 0x" + Integer.toHexString(entry.getOrigLength()).toUpperCase());
System.out.print("Masks: ");

boolean doPrint = (entry.getFlags() | NOT_MSK) != 0;
if (doPrint) System.out.print("NOT_MSK");
if ((entry.getFlags() | LZSS_MSK) != 0 && doPrint) System.out.print(" | ");
else System.out.println();
if ((entry.getFlags() | LZSS_MSK) != 0) System.out.println("LZSS_MSK");
}

private static byte[] readIndexBlock(InputStream in) throws IOException {
DataInputStream dis = new DataInputStream(in);

int len = flipEndian(dis.readInt()) - 12;
int flags = flipEndian(dis.readInt());
int origLen = flipEndian(dis.readInt());

return readBlock(in, len, flags, origLen);
}

private static byte[] readBlock(InputStream in, int len, int flags, int origLen) throws IOException {
byte[] buf = new byte[len];
in.read(buf);

if ((flags | NOT_MSK) != 0) invertBitsArray(buf);
if (((flags | LZSS_MSK) != 0) && (len < origLen)) buf = LZSS.decompress(buf, len, null, origLen);
// TODO - other flag status are not supported yet

return buf;
}

private static String readWString(byte[] buf, int pos) throws IOException {
int len = 0;
int i = pos;
while ((buf[i] | buf[i+1]) != 0) {
len += 2;
i += 2;
}

return new String(buf, pos, len, "UTF-16LE");
}

private static void writeFile(byte[] buf, String name, String dstDir) throws IOException {
if (dstDir == null) dstDir = "./output";
File outfile = new File(dstDir + "/" + name);
File parentDir = outfile.getParentFile();
if (!parentDir.exists()) parentDir.mkdirs();

FileOutputStream fos = new FileOutputStream(outfile);
BufferedOutputStream bos = new BufferedOutputStream(fos, 0x100000);
ByteArrayInputStream bais = new ByteArrayInputStream(buf);

int remainder = buf.length; // keep track of the amount of remaining bytes
byte[] bytes16 = new byte[16];
while (bais.available() != 0 && remainder / 16 != 0) {
bais.read(bytes16);
bos.write(bytes16);
remainder -= 16;
}

if (remainder != 0) {
byte[] remains = new byte[remainder];
bais.read(remains);
bos.write(remains);
}

bos.flush();
bos.close();
}
}


IndexEntry.java:
/*
* @(#)IndexEntry.java 2007/05/15
* Written by rednaxela / FX
*/

public class IndexEntry {

static final int ENTRY_LENGTH = 16;
static final int MAX_FILENAME_LENGTH = 256;
static final int OFFSET_OFS = 0;
static final int LENGTH_OFS = 4;
static final int FLAGS_OFS = 8;
static final int ORIGLENGTH_OFS = 12;

private String filename;
private int length;
private int offset;
private int flags; // NOT_MSK = 1, LZSS_MSK = 2
private int origLength;

/**
* @return the filename
*/
public String getFilename() {
return filename;
}

/**
* @param filename the filename to set
*/
public void setFilename(String filename) {
this.filename = filename;
}

/**
* @return the length
*/
public int getLength() {
return length;
}

/**
* @param length the length to set
*/
public void setLength(int length) {
this.length = length;
}

/**
* @return the offset
*/
public int getOffset() {
return offset;
}

/**
* @param offset the offset to set
*/
public void setOffset(int offset) {
this.offset = offset;
}

/**
* @return the offset
*/
public int getFlags() {
return offset;
}

/**
* @param offset the offset to set
*/
public void setFlags(int flags) {
this.flags = flags;
}

/**
* @return the compressedLength
*/
public int getOrigLength() {
return origLength;
}

/**
* @param offset the compressedLength to set
*/
public void setOrigLength(int origLength) {
this.origLength = origLength;
}
}


LZSS.java:
/*
* @(#)LZSS.java 2007/05/15
* Written by rednaxela / FX
*/

public class LZSS {

static final int WINDOW_LENGTH = 4096;

public static byte[] decompress(byte[] from, int compLen, byte[] to, int origLen) {
return decompress(from, compLen, to, 0, origLen);
}

public static byte[] decompress(byte[] from, int compLen, byte[] to, int pos, int origLen) {
if (to == null) to = new byte[origLen];

byte[] window = new byte[WINDOW_LENGTH];
int readOffset = 0;
int writeOffset = pos;
int marker = 0; // read marker, 8-bits, 1 for raw byte, 0 for back ref
int windowWriteOffset = 0x0FEE;
int windowReadOffset = 0;
int backRefLength = 0;
int current = 0;

while (readOffset != from.length) {
marker >>= 1;

if ((marker & 0x0100) == 0) {
current = from[readOffset++] & 0x0FF;
marker = 0x0FF00 | current;
}

if(readOffset == from.length) break;
if ((marker & 0x01) == 1) { // copy raw bytes
current = from[readOffset++] & 0x0FF;
to[writeOffset++] = (byte)current;
window[windowWriteOffset++] = (byte)current;
windowWriteOffset &= 0x0FFF;
} else { // copy from slide window
windowReadOffset = from[readOffset++] & 0x0FF;
if(readOffset == from.length) break;
current = from[readOffset++] & 0x0FF;
windowReadOffset |= (current & 0x0F0) << 4;
backRefLength = (current & 0x0F) + 2;
if (backRefLength < 0) continue;

int addOffset = 0;
while (addOffset <= backRefLength) {
int curOfs = (windowReadOffset + addOffset++) & 0x0FFF;
current = window[curOfs] & 0x0FF;
windowReadOffset &= 0x0FFF;
to[writeOffset++] = (byte)current;
window[windowWriteOffset++] = (byte)current;
windowWriteOffset &= 0x0FFF;
} // while
} // if-else
} // while

return to;
}
}


Binconverter.java:
/*
* @(#)BinConverter.java
*/

/**
* Some helper routines for data conversion, all data is treated in network
* byte order.
*/
public class BinConverter
{
/**
* Gets bytes from an array into an integer, in big-endian.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 32bit integer
*/
public final static int byteArrayToIntBE(
byte[] buf,
int ofs)
{
return (buf[ofs ] << 24)
| ((buf[ofs + 1] & 0x0ff) << 16)
| ((buf[ofs + 2] & 0x0ff) << 8)
| ( buf[ofs + 3] & 0x0ff);
}

/**
* Gets bytes from an array into an integer, in little-endian.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 32bit integer
*/
public final static int byteArrayToIntLE(
byte[] buf,
int ofs)
{
return (buf[ofs + 3] << 24)
| ((buf[ofs + 2] & 0x0ff) << 16)
| ((buf[ofs + 1] & 0x0ff) << 8)
| ( buf[ofs ] & 0x0ff);
}

///

/**
* Converts an integer to bytes in big-endian, which are put into an array.
* @param value the 32bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void intToByteArrayBE(
int value,
byte[] buf,
int ofs)
{
buf[ofs ] = (byte)((value >>> 24) & 0x0ff);
buf[ofs + 1] = (byte)((value >>> 16) & 0x0ff);
buf[ofs + 2] = (byte)((value >>> 8) & 0x0ff);
buf[ofs + 3] = (byte) value;
}

/**
* Converts an integer to bytes in little-endian, which are put into an array.
* @param value the 32bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void intToByteArrayLE(
int value,
byte[] buf,
int ofs)
{
buf[ofs + 3] = (byte)((value >>> 24) & 0x0ff);
buf[ofs + 2] = (byte)((value >>> 16) & 0x0ff);
buf[ofs + 1] = (byte)((value >>> 8) & 0x0ff);
buf[ofs ] = (byte) value;
}

///

/**
* Gets bytes from an array into a long.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 64bit integer
*/
public final static long byteArrayToLong(
byte[] buf,
int ofs)
{
// Looks more complex - but it is faster (at least on 32bit platforms).

return
((long)(( buf[ofs ] << 24) |
((buf[ofs + 1] & 0x0ff) << 16) |
((buf[ofs + 2] & 0x0ff) << 8) |
( buf[ofs + 3] & 0x0ff )) << 32) |
((long)(( buf[ofs + 4] << 24) |
((buf[ofs + 5] & 0x0ff) << 16) |
((buf[ofs + 6] & 0x0ff) << 8) |
( buf[ofs + 7] & 0x0ff )) & 0x0ffffffffL);
}

///

/**
* Converts a long to bytes, which are put into an array.
* @param value the 64bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void longToByteArray(
long value,
byte[] buf,
int ofs)
{
int tmp = (int)(value >>> 32);

buf[ofs ] = (byte) (tmp >>> 24);
buf[ofs + 1] = (byte)((tmp >>> 16) & 0x0ff);
buf[ofs + 2] = (byte)((tmp >>> 8) & 0x0ff);
buf[ofs + 3] = (byte) tmp;

tmp = (int)value;

buf[ofs + 4] = (byte) (tmp >>> 24);
buf[ofs + 5] = (byte)((tmp >>> 16) & 0x0ff);
buf[ofs + 6] = (byte)((tmp >>> 8) & 0x0ff);
buf[ofs + 7] = (byte) tmp;
}

///

/**
* Converts values from an integer array to a long.
* @param buf where to get the bytes
* @param ofs index from where to read the data
* @return the 64bit integer
*/
public final static long intArrayToLong(
int[] buf,
int ofs)
{
return (((long) buf[ofs ]) << 32) |
(((long) buf[ofs + 1]) & 0x0ffffffffL);
}

///

/**
* Converts a long to integers which are put into an array.
* @param value the 64bit integer to convert
* @param buf the target buf
* @param ofs where to place the bytes in the buf
*/
public final static void longToIntArray(
long value,
int[] buf,
int ofs)
{
buf[ofs ] = (int)(value >>> 32);
buf[ofs + 1] = (int) value;
}

///

/**
* Makes a long from two integers (treated unsigned).
* @param lo lower 32bits
* @param hi higher 32bits
* @return the built long
*/
public final static long makeLong(
int lo,
int hi)
{
return (((long) hi << 32) |
((long) lo & 0x00000000ffffffffL));
}

///

/**
* Gets the lower 32 bits of a long.
* @param val the long integer
* @return lower 32 bits
*/
public final static int longLo32(
long val)
{
return (int)val;
}

///

/**
* Gets the higher 32 bits of a long.
* @param val the long integer
* @return higher 32 bits
*/
public final static int longHi32(
long val)
{
return (int)(val >>> 32);
}

///

// our table for hex conversion
final static char[] HEXTAB =
{
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};

/**
* Converts a byte array to a hex string.
* @param data the byte array
* @return the hex string
*/
public final static String bytesToHexStr(
byte[] data)
{
return bytesToHexStr(data, 0, data.length);
}

///

/**
* Converts a byte array to a hex string.
* @param data the byte array
* @param ofs start index where to get the bytes
* @param len number of bytes to convert
* @return the hex string
*/
public final static String bytesToHexStr(
byte[] data,
int ofs,
int len)
{
int pos, c;
StringBuffer sbuf;


sbuf = new StringBuffer();
sbuf.setLength(len << 1);

pos = 0;
c = ofs + len;

while (ofs < c)
{
sbuf.setCharAt(pos++, HEXTAB[(data[ofs ] >> 4) & 0x0f]);
sbuf.setCharAt(pos++, HEXTAB[ data[ofs++] & 0x0f]);
}
return sbuf.toString();
}

///

/**
* Converts a hex string back into a byte array (invalid codes will be
* skipped).
* @param hex hex string
* @param data the target array
* @param srcofs from which character in the string the conversion should
* begin, remember that (nSrcPos modulo 2) should equals 0 normally
* @param dstofs to store the bytes from which position in the array
* @param len number of bytes to extract
* @return number of extracted bytes
*/
public final static int hexStrToBytes(
String hex,
byte[] data,
int srcofs,
int dstofs,
int len)
{
int i, j, strlen, avail_bytes, dstofs_bak;
byte abyte;
boolean convertOK;


// check for correct ranges
strlen = hex.length();

avail_bytes = (strlen - srcofs) >> 1;
if (avail_bytes < len)
{
len = avail_bytes;
}

int nOutputCapacity = data.length - dstofs;
if (len > nOutputCapacity)
{
len = nOutputCapacity;
}

// convert now

dstofs_bak = dstofs;

for (i = 0; i < len; i++)
{
abyte = 0;
convertOK = true;

for (j = 0; j < 2; j++)
{
abyte <<= 4;
char cActChar = hex.charAt(srcofs++);

if ((cActChar >= 'a') && (cActChar <= 'f'))
{
abyte |= (byte) (cActChar - 'a') + 10;
}
else
{
if ((cActChar >= '0') && (cActChar <= '9'))
{
abyte |= (byte) (cActChar - '0');
}
else
{
convertOK = false;
}
}
}
if (convertOK)
{
data[dstofs++] = abyte;
}
}

return (dstofs - dstofs_bak);
}

///

/**
* Converts a byte array into a Unicode string.
* @param data the byte array
* @param ofs where to begin the conversion
* @param len number of bytes to handle
* @return the string
*/
public final static String byteArrayToStr(
byte[] data,
int ofs,
int len)
{
int avail_capacity, sbuf_pos;
StringBuffer sbuf;


// we need two bytes for every character
len &= ~1;

// enough bytes in the buf?
avail_capacity = data.length - ofs;

if (avail_capacity < len)
{
len = avail_capacity;
}

sbuf = new StringBuffer();
sbuf.setLength(len >> 1);

sbuf_pos = 0;

while (0 < len)
{
sbuf.setCharAt(
sbuf_pos++,
(char)((data[ofs ] << 8)
| (data[ofs + 1] & 0x0ff)));
ofs += 2;
len -= 2;
}

return sbuf.toString();
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GAL游戏解包工具,含C++源码。 包含181个工具: alb2png amp2bmp ar2bugfix arkcmp2bmp brs2png d3dslide decrbrads decrddns2 decrkansa erisacvt ex1uparc ex4ag exafa exah3pac exakbdat exald exanepak exaos exaqugsp exar2 exarc2 exarc4 exarcw exatuworks exavk exbbbin exbelarc exbelldat_nosrc exbelldat1.02_nosrc exbkarc exbrdat_nosrc exbrpak exbsa exbszdat excandydat excatcab excdt excfarc exchpac excrxb exdaf2 exddp exdebopak exdieslib exdosnpac exdpk exdpmx exdxa exedendp5 exeiarc exescarc exfavbin exfigdat exfleurdat exfp3 exgce3 exgr2 exgsp4 exgxp exhappyend exhdcpak exhecdat exhud exhxp exiar exifdypf exiga exihkcap exisa exk5 exkactive exkhwag exkifint exkiss6dat exkizpak exkkesui exkleinpak exkoigdat exl4osa_nosrc exl5enj_nosrc exl6ren_nosrc exlac exlfa exlfar21 exlibiarc exlnk4 exlrlpk_nosrc exm2lib exmaiarc exmaotarc exmarupac_nosrc exmaspaz exmed exmespac exmhpac exmk2 exmnvdat exmoepack exmornpak exmpf2 exmpk exmpsaf exmugibin exmwpak exnfcel exnllpk_nosrc exnnkarc exnosdat_nosrc exnp4x exnpa exnpf exns2 exoozoarc expatbin expcf expdfpk expf2 expimg expkd expzdat exrlarc_nosrc exrrdat_nosrc exs4alf exscharc_nosrc exsec exsenarc_nosrc exsgnpa exshikidat exsholib exsteldat exsui2rom exszs extafarc extapak extarc extcd3 extensho extk2fpk extmmpck extricgf_nosrc extropak extskfdat exttd extttdpk extvkarc_nosrc exuni2 exunity exutsudat exvcpak exvff exvfs exwatfopck exwhaledat exwlcs exxusegd exyatpkg exykc exyox exzwarc_nosrc gax2png gyu2bmp junk_atled junk_monobeno kamiparadeck lpx2bmp mag2bmp miscbss misceri miscmja nimg2bmp pmchar2bmp ps2force480p spc2bmp sz2png tig2png tits2deck tox2deck toxtweak tpw2bmp_nosrc VisualMemory0.9.5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值