嗯,这个也是快一年前写的了。当时在澄空看到有人想要解手机少女的文件,就随便写了个程序来处理这个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 archiveSECINFO nameSecInfo; // 1st of SECINFO in archiveSECDATA nameSecData; // 1st of SECDATA in archive, contents of which are filenames in UNICODESECINFO indexSecInfo; // 2nd of SECINFO in archiveSECDATA 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();    }}

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

  1. 思考并实现以下程序功能:实现一个抢红包的程序 java

    抢红包的程序 java 思考并实现以下程序功能:实现一个抢红包的程序. 可参考模拟微信抢红包的过程:假如当前红包是x元,参与抢红包的有y人,按时间先后顺序保证y人正好抢完x元红包,其中每人抢的红包数值 ...

  2. 在web前端调用后台java程序(java类)的方式

    在web前端调用后台java程序(java类)的方式: 首先静态html标签是无法直接调用java程序的,但是可以通过imput button按钮点击,onclick事件调用一个js函数,用这个js函 ...

  3. _如何在各种Linux发行版中安装zip压缩与解压缩程序

    请关注本头条号,每天坚持更新原创干货技术文章. 如需学习视频,请在微信搜索公众号"智传网优"直接开始自助视频学习 1. 前言 本文主要讲解如何在Linux系统上安装zip压缩与解压 ...

  4. 微信读书登陆界面java_(JAVA后端)微信小程序-毕设级项目搭建-微信阅读小程序(内含源码,微信小程序+java逻辑后台+vue管理系统)~不求完美,实现就好...

    转载地址:(JAVA后端)微信小程序-毕设级项目搭建-微信阅读小程序(内含源码,微信小程序+java逻辑后台+vue管理系统)~不求完美,实现就好 转载请注明出处 一.环境搭建 相关环境软件:JDK1 ...

  5. Java处理敲击键盘事件 Etch-A-Sketch玩具实现 光标画笔画图程序 Java核心技术

    Java处理敲击键盘事件 Etch-A-Sketch玩具实现 光标画笔画图程序 Java核心技术 source code: package com.sunnyykn.chapter08; import ...

  6. 微信运动步数:小程序+Java后端,源码可下载

    微信运动步数:小程序+Java后端 更多资源:www.jeeweixin.com 功能说明: 1.获取和展示用户的微信运动步数,计算卡路里: 2.用户打开小程序即可实现步数打卡入库: 3.通过日历展示 ...

  7. 微信小程序开发-微信支付之免密支付(自动扣费)一 小程序+java接口

    微信小程序开发-微信支付之免密支付(自动扣费)一 小程序+java接口 链接: 点击进入

  8. java基础应用程序超市收银_超市收银程序(JAVA课程设计 2011)

    <超市收银程序(JAVA课程设计 2011)>由会员分享,可在线阅读,更多相关<超市收银程序(JAVA课程设计 2011)(15页珍藏版)>请在人人文库网上搜索. 1.软零件研 ...

  9. java 小程序 多线程_《多线程练习—买票小程序——Java第十四周》

    /* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2011, 烟台大学计算机学院学生 * All rights reserved. * 文件名称:    < ...

最新文章

  1. The Pediatric Cancer Genome Project   儿童癌症基因组计划
  2. 一根棉签解决身上各种酸痛,立马感觉无比舒畅!
  3. 王式安概率论与数理统计基础课手写笔记-第一章概率与事件-第二章随机变量及其分布
  4. matlab图像显示时间,请问怎么把样点数变成时间显示在图像了里
  5. powershell 遍历json_使用PowerShell处理JSON字符串
  6. UnitTest in .NET(Part 5)
  7. anaconda中更改python版本
  8. 关于如何写代码和学习代码
  9. NB-IOT平台之电信平台FOTA 升级记录
  10. FireMonkey 跨平台框架下的图片缩放和 JPEG 编码
  11. 会考计算机考试模拟软件,计算机会考考试模拟(范文).doc
  12. 斐波那契堆python实现——Fibonacci Heaps
  13. ERStudio逆向工程生成ER模型
  14. java 随机生成头像,ASP实现头像图像随机变换
  15. dhtml(灯火通明类似的词语)
  16. ssm基于Vue的共享单车app系统
  17. a = a + 1, a++, ++a ,a+=1区别
  18. HIT-哈工大数据结构-作业2(C++)
  19. win10内网穿透实现远程桌面连接
  20. 经典神经网络模型整理

热门文章

  1. virt-p2v工具的使用记录
  2. Web应用设置Context Path的方式
  3. JS中获取contextPath的方法
  4. html5 翻牌效果,css3实现图片翻牌特效
  5. SQL distinct用法
  6. Android 蓝牙 A2DP基础概念、A2DP音频流的建立及传输流程、A2DP播放暂停音乐命令交互过程分析 - 史上最全分析
  7. 信息学奥赛C++语言:旅游费用
  8. 1、什么是Internet
  9. 一个IBM人的离职泪:伟大公司,SB老板,苦逼员工
  10. python 常用第三方包镜像安装