ケータイ少女 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 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)相关推荐
- 思考并实现以下程序功能:实现一个抢红包的程序 java
抢红包的程序 java 思考并实现以下程序功能:实现一个抢红包的程序. 可参考模拟微信抢红包的过程:假如当前红包是x元,参与抢红包的有y人,按时间先后顺序保证y人正好抢完x元红包,其中每人抢的红包数值 ...
- 在web前端调用后台java程序(java类)的方式
在web前端调用后台java程序(java类)的方式: 首先静态html标签是无法直接调用java程序的,但是可以通过imput button按钮点击,onclick事件调用一个js函数,用这个js函 ...
- _如何在各种Linux发行版中安装zip压缩与解压缩程序
请关注本头条号,每天坚持更新原创干货技术文章. 如需学习视频,请在微信搜索公众号"智传网优"直接开始自助视频学习 1. 前言 本文主要讲解如何在Linux系统上安装zip压缩与解压 ...
- 微信读书登陆界面java_(JAVA后端)微信小程序-毕设级项目搭建-微信阅读小程序(内含源码,微信小程序+java逻辑后台+vue管理系统)~不求完美,实现就好...
转载地址:(JAVA后端)微信小程序-毕设级项目搭建-微信阅读小程序(内含源码,微信小程序+java逻辑后台+vue管理系统)~不求完美,实现就好 转载请注明出处 一.环境搭建 相关环境软件:JDK1 ...
- Java处理敲击键盘事件 Etch-A-Sketch玩具实现 光标画笔画图程序 Java核心技术
Java处理敲击键盘事件 Etch-A-Sketch玩具实现 光标画笔画图程序 Java核心技术 source code: package com.sunnyykn.chapter08; import ...
- 微信运动步数:小程序+Java后端,源码可下载
微信运动步数:小程序+Java后端 更多资源:www.jeeweixin.com 功能说明: 1.获取和展示用户的微信运动步数,计算卡路里: 2.用户打开小程序即可实现步数打卡入库: 3.通过日历展示 ...
- 微信小程序开发-微信支付之免密支付(自动扣费)一 小程序+java接口
微信小程序开发-微信支付之免密支付(自动扣费)一 小程序+java接口 链接: 点击进入
- java基础应用程序超市收银_超市收银程序(JAVA课程设计 2011)
<超市收银程序(JAVA课程设计 2011)>由会员分享,可在线阅读,更多相关<超市收银程序(JAVA课程设计 2011)(15页珍藏版)>请在人人文库网上搜索. 1.软零件研 ...
- java 小程序 多线程_《多线程练习—买票小程序——Java第十四周》
/* (程序头部注释开始) * 程序的版权和版本声明部分 * Copyright (c) 2011, 烟台大学计算机学院学生 * All rights reserved. * 文件名称: < ...
最新文章
- The Pediatric Cancer Genome Project 儿童癌症基因组计划
- 一根棉签解决身上各种酸痛,立马感觉无比舒畅!
- 王式安概率论与数理统计基础课手写笔记-第一章概率与事件-第二章随机变量及其分布
- matlab图像显示时间,请问怎么把样点数变成时间显示在图像了里
- powershell 遍历json_使用PowerShell处理JSON字符串
- UnitTest in .NET(Part 5)
- anaconda中更改python版本
- 关于如何写代码和学习代码
- NB-IOT平台之电信平台FOTA 升级记录
- FireMonkey 跨平台框架下的图片缩放和 JPEG 编码
- 会考计算机考试模拟软件,计算机会考考试模拟(范文).doc
- 斐波那契堆python实现——Fibonacci Heaps
- ERStudio逆向工程生成ER模型
- java 随机生成头像,ASP实现头像图像随机变换
- dhtml(灯火通明类似的词语)
- ssm基于Vue的共享单车app系统
- a = a + 1, a++, ++a ,a+=1区别
- HIT-哈工大数据结构-作业2(C++)
- win10内网穿透实现远程桌面连接
- 经典神经网络模型整理