- 浏览: 3047865 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
呼,这个也是一年多之前写的了……当时茶茶说起有没有办法处理BMW里的dat文件,然后我写了这程序,然后我们就真的开工了。真神奇,呵呵。
当时发给茶茶的聊天记录:
嘛,当时也没仔细考察。这BMW很明显用的不是YaneSDK.NET而是更早的版本,是YaneSDK2还是YaneSDK3来着,我又忘了。不管了。
然后这个程序的使用方法:
接下来是源代码。跟前面两帖(这里和这里)一样,因为用了BlowfishJ的BinConverter.java,所以以下代码以LGPL许可证发布。
不过说真的,这连续3帖里的代码都是当时太贪图写起来方便而弄得乱七八糟的……结构太糟糕了。或许还是应该把一些结构自己的IO给封装起来的。叹气。
Archiver.java:
IndexEntry.java:
BinConverter.java:
当时发给茶茶的聊天记录:
引用
http://www.ahcomic.com/bbs/thread-163398-1-1.html
这是前段时间正好写的关于Fatal/Fake的其中一个archive的解析.BMW的情况看起来跟Fatal/Fake差不多.可以参考下,拿里面的代码来修改.
http://yanesdkdotnet.sourceforge.jp/
如果我没猜错的话,BMW用的是这个引擎(或相关,yane2k1).但是这不重要,我们不一定需要知道原本的引擎是什么样的.
========================================================================================================================
头8个字节,地址0x00-0x07,属于file signature,读进来验证下是不是等于yanepkDx就可以了.
接着是一个DWORD(也就是32位整型,4个byte),那是文件个数,顺序是little-endian.0x2D意味着这个archive里有是45个文件.
然后后面接着的所谓的index.你会看到每个index的entry的长度都是固定的,长度为268(=0x10C).其中前面是留给"以\0结束的字符串"的空间,这里当然就是文件名啦.后面有3个little-endian的DWORD,分别是文件的地址/大小/大小.
提到index的每个entry里末尾的那3个DWORD,在我手上的这个文件里,后两个表示大小的值总是一样的.这意味着其中一个是原始大小而另一个是压缩后大小,只是现在我这文件的内容没有被压缩过而已.你或许会在另外几个dat文件里看到这两个表示大小的值会不一样.那么比较小的那个就是压缩后的大小.
index后就是文件内容了.把它们分离出来就行."分离"也就是把数据一个一个字节写到一个新文件里就可以了.反正我们也不追求什么速度啊空间啊效率什么的.你在index里得到了3个有用的值(这里我们只用到两个,因为不知道哪个"大小"是指压缩的.假设地址是offset,大小是origFileSize),那么Java就用RandomAccessFile设置文件指针到offset,然后读出origFileSize个字节,按照原本的文件名写出去就可以了...简单吧.
以后要打包回来的时候,只要反过来做一次事情就行...
这是前段时间正好写的关于Fatal/Fake的其中一个archive的解析.BMW的情况看起来跟Fatal/Fake差不多.可以参考下,拿里面的代码来修改.
http://yanesdkdotnet.sourceforge.jp/
如果我没猜错的话,BMW用的是这个引擎(或相关,yane2k1).但是这不重要,我们不一定需要知道原本的引擎是什么样的.
========================================================================================================================
头8个字节,地址0x00-0x07,属于file signature,读进来验证下是不是等于yanepkDx就可以了.
接着是一个DWORD(也就是32位整型,4个byte),那是文件个数,顺序是little-endian.0x2D意味着这个archive里有是45个文件.
然后后面接着的所谓的index.你会看到每个index的entry的长度都是固定的,长度为268(=0x10C).其中前面是留给"以\0结束的字符串"的空间,这里当然就是文件名啦.后面有3个little-endian的DWORD,分别是文件的地址/大小/大小.
提到index的每个entry里末尾的那3个DWORD,在我手上的这个文件里,后两个表示大小的值总是一样的.这意味着其中一个是原始大小而另一个是压缩后大小,只是现在我这文件的内容没有被压缩过而已.你或许会在另外几个dat文件里看到这两个表示大小的值会不一样.那么比较小的那个就是压缩后的大小.
index后就是文件内容了.把它们分离出来就行."分离"也就是把数据一个一个字节写到一个新文件里就可以了.反正我们也不追求什么速度啊空间啊效率什么的.你在index里得到了3个有用的值(这里我们只用到两个,因为不知道哪个"大小"是指压缩的.假设地址是offset,大小是origFileSize),那么Java就用RandomAccessFile设置文件指针到offset,然后读出origFileSize个字节,按照原本的文件名写出去就可以了...简单吧.
以后要打包回来的时候,只要反过来做一次事情就行...
嘛,当时也没仔细考察。这BMW很明显用的不是YaneSDK.NET而是更早的版本,是YaneSDK2还是YaneSDK3来着,我又忘了。不管了。
然后这个程序的使用方法:
引用
Usage: java Archiver [option] filename
options:
[e]xtract
[a]rchive
============================================
example:
Suppose data1.dat is in the same directory where this program is.
to extract files to current directory:
Suppose the files to be packed are in "data1" directory:
options:
[e]xtract
[a]rchive
============================================
example:
Suppose data1.dat is in the same directory where this program is.
to extract files to current directory:
java Archiver e data1.dat
Suppose the files to be packed are in "data1" directory:
java Archiver a data1\
接下来是源代码。跟前面两帖(这里和这里)一样,因为用了BlowfishJ的BinConverter.java,所以以下代码以LGPL许可证发布。
不过说真的,这连续3帖里的代码都是当时太贪图写起来方便而弄得乱七八糟的……结构太糟糕了。或许还是应该把一些结构自己的IO给封装起来的。叹气。
Archiver.java:
/* * @(#)Archiver.java 2007/03/23 * Written by rednaxela / FX */ import java.io.*; import java.util.*; /** * Demonstrating archive operations on *.dat files as seen in BattleMoonWars. */ public class Archiver { static final byte[] SIGNATURE = { (byte)'y', (byte)'a', (byte)'n', (byte)'e', (byte)'p', (byte)'k', (byte)'D', (byte)'x', }; static final int ENTRY_LENGTH = 268; static final int MAX_FILENAME_LENGTH = 256; static final int OFFSET_OFS = 256; static final int LENGTH_OFS = 260; static final int COMPRESSED_LENGTH_OFS = 264; // ?? static final String USAGE = "Usage: java Archiver [option] filename\n" + "options:\n" + "[e]xtract\n" + "[a]rchive"; /** * the application entry point * @param args (command line) parameters */ public static void main(String args[]) throws Exception { // check command line arguments if (args.length != 2) error(USAGE); if ("e".equals(args[0].trim())) { // extract files from archive String filename = args[1].trim(); if (filename.length() == 0) error("2nd argument not exist."); extractFiles(filename); } else if ("a".equals(args[0].trim())) { // pack files into archive String dirname = args[1].trim(); if (dirname.length() == 0) error("2nd argument not exist."); packFiles(dirname); } else error(USAGE); } private static void extractFiles(String filename) throws Exception { // open source archive File arc = new File(filename); if (!arc.exists()) error("Archive " + filename + " doesn't exist"); long contentOfs = 0L; IndexEntry[] indexEntries = null; FileInputStream fis = new FileInputStream(arc); DataInputStream dis = new DataInputStream(fis); // match archive SIGNATURE byte[] sig = new byte[8]; dis.read(sig); if (!Arrays.equals(SIGNATURE, sig)) error("Archive file not supported."); // get file count int fileCount = reverseEndian(dis.readInt()); System.out.println("Files in archive: " + fileCount); indexEntries = new IndexEntry[fileCount]; // read index entries byte[] entryBuffer = new byte[ENTRY_LENGTH]; for (int i = 0; i < fileCount; ++i) { IndexEntry entry = new IndexEntry(); dis.read(entryBuffer); ByteArrayInputStream bais = new ByteArrayInputStream(entryBuffer); String name = readCString(bais); entry.setFilename(name); System.err.print("File \"" + entry.getFilename() + "\" : "); int offset = BinConverter.byteArrayToIntLE(entryBuffer, OFFSET_OFS); entry.setOffset(offset); System.err.print(" at relative offset 0x" + Integer.toHexString(entry.getOffset()).toUpperCase()); int length = BinConverter.byteArrayToIntLE(entryBuffer, LENGTH_OFS); entry.setLength(length); System.err.print(" size: 0x" + Integer.toHexString(entry.getLength()).toUpperCase()); int compressedLength = BinConverter.byteArrayToIntLE(entryBuffer, COMPRESSED_LENGTH_OFS); entry.setCompressedLength(compressedLength); System.err.println(" csize: 0x" + Integer.toHexString(entry.getCompressedLength()).toUpperCase()); if (length > compressedLength) { System.err.println("possibly compressed"); System.exit(1); } else if (length < compressedLength) { System.err.println("possibly compressed, got length/compressedLength wrong..."); System.exit(1); } indexEntries[i] = entry; } // extract files for (IndexEntry entry : indexEntries) { // data correctness check - this support ordered file archive only. // to support out-of-order archives, use RandomAccessFile instead. if (fis.getChannel().position() != entry.getOffset()) error("Bad file content order at " + entry.getFilename() + " 0x" + Integer.toHexString((int)(fis.getChannel().position() - contentOfs))); File outfile = new File("./" + entry.getFilename()); File parentDir = outfile.getParentFile(); if (!parentDir.exists()) parentDir.mkdirs(); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(outfile), 0x7FFFFF); int remainder = 0; // keep track of the amount of remaining bytes int length = entry.getLength(); while (fis.available() != 0 && remainder != length) { bos.write(fis.read()); ++remainder; } bos.flush(); bos.close(); } fis.close(); } private static void packFiles(String dirname) throws Exception { // open source directory File dir = new File(dirname); if (!dir.exists()) error("Directory " + dirname + " doesn't exist"); else if (!dir.isDirectory()) error(dirname + " is not a valid directory."); // use a ArrayList to store the index entries ArrayList<IndexEntry> indexEntries = new ArrayList<IndexEntry>(); // make up index data buildIndex(dir, indexEntries, ""); // calculate the offset values for each file record int runningOfs = SIGNATURE.length + 4 + ENTRY_LENGTH * indexEntries.size(); for (IndexEntry entry : indexEntries) { entry.setOffset(runningOfs); runningOfs += entry.getLength(); } // write out archive SIGNATURE and file count DataOutputStream dos = new DataOutputStream( new BufferedOutputStream( new FileOutputStream(dir.getName() + ".dat"))); dos.write(SIGNATURE); dos.writeInt(reverseEndian(indexEntries.size())); // write out the file index for (IndexEntry entry : indexEntries) { System.err.println("Adding file " + entry.getFilename() + " size: 0x" + Integer.toHexString(entry.getLength()).toUpperCase() + " at relative offset: 0x" + Integer.toHexString(entry.getOffset()).toUpperCase()); int zeroCount = MAX_FILENAME_LENGTH - entry.getFilename().length(); dos.write(entry.getFilename().getBytes()); for (int i = 0; i < zeroCount; ++i) dos.write(0); dos.writeInt(reverseEndian(entry.getOffset())); dos.writeInt(reverseEndian(entry.getLength())); dos.writeInt(reverseEndian(entry.getLength())); // NOTE! Does NOT support compressed files at current time being } // write out each file's content for(IndexEntry entry : indexEntries) { BufferedInputStream bis = new BufferedInputStream(new FileInputStream(dir.getAbsolutePath() + "\\" + entry.getFilename())); while (bis.available() != 0) dos.writeByte(bis.read()); bis.close(); } dos.flush(); dos.close(); } private static int reverseEndian(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 int reverseEndian(short s) { byte[] bytes = new byte[2]; bytes[0] = (byte)(s >>> 8 ); bytes[1] = (byte) s; int i = 0; i |= (bytes[1] << 8 ) & 0x000ff00; i |= bytes[0] & 0x00000ff; return i; } private static void error(String cause) { System.err.println("Error " + cause); System.exit(1); } private static String readCString(InputStream in) throws IOException { ArrayList<Byte> str = new ArrayList<Byte>(); byte current = (byte)in.read(); while (current != (byte)0x00) { str.add(current); current = (byte)in.read(); } byte[] temp = new byte[str.size()]; for (int i = 0; i < temp.length; ++i) { temp[i] = str.get(i); } return new String(temp); } private static void buildIndex(File dir, ArrayList<IndexEntry> index, String parent) { // list the designated directory File[] files = dir.listFiles(); // traverse dir for (int i = 0; i < files.length; ++i) { if (files[i].isDirectory()) { // current file is a directory // assumes that subdir depth doesn't exceed one buildIndex(files[i], index, parent+files[i].getName()+"\\"); } else { // current file is a normal file IndexEntry entry = new IndexEntry(); entry.setFilename(parent+files[i].getName()); entry.setLength((int)files[i].length()); index.add(entry); } } } }
IndexEntry.java:
/* * @(#)IndexEntry.java 2007/03/23 * Written by rednaxela / FX */ public class IndexEntry { private String filename; private int length; private int offset; private int compressedLength; // TODO not yet implemented - CHECK! /** * @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 compressedLength */ public int getCompressedLength() { return compressedLength; } /** * @param offset the compressedLength to set */ public void setCompressedLength(int compressedLength) { this.compressedLength = compressedLength; } }
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(); } }
- BMW_Archiver_20080817.zip (27.2 KB)
- 下载次数: 3
评论
2 楼
RednaxelaFX
2008-04-08
嗯嗯,如果再有什么类似的东西需要写的话说不定会用Ruby。但是重构就算了……
这可是write-and-throwaway的代码啊……
这可是write-and-throwaway的代码啊……
1 楼
poweryoung
2008-04-08
今天帖好多...
来,用ruby重构吧...
来,用ruby重构吧...
发表评论
-
IDA Pro Free笔记
2013-05-19 08:59 0IDA把数据都存哪里了? Windows 7 D:\tem ... -
加密算法收集
2011-01-23 16:10 0加密算法,OTP http://en.wikipedia.or ... -
BattleMoonWars 归档解压/压缩程序(砍掉重炼版)
2010-05-03 21:40 2277以前写过BattleMoonWars的 ... -
Quartett!文本插入程序
2008-06-21 20:38 1569年初写的Quartett!的文本 ... -
ケータイ少女 script.arc的解压缩程序 (Java)
2008-04-08 14:02 4646嗯,这个也是快一年前写的了。当时在澄空看到有人想要解手机少女的 ... -
桃華月憚体験版的解压缩程序 (Java)
2008-04-08 13:38 3108这是差不多一年前写的程序了……有人说想看于是发出来。 当时也是 ... -
Quartett!的文本提取程序
2008-03-05 23:31 1871诶,之前写了这个程序 ... -
Fortune Arterial Tools
2008-02-28 13:34 2442using System; using System.IO; ... -
さくらシュトラッセ literal record
2008-01-28 14:53 1801脚本在scenario.sc里。无 ... -
Borland的库的一个小特征?
2008-01-06 00:22 2503最近弄了下KAMIPANI相关 ... -
[脚本分析] 从Quartett!的脚本得到资源列表
2007-12-21 14:00 1768听汉公的说明,看来LittleWitch所使用的FFD Sys ... -
[脚本分析] Quartett!的二进制脚本分析
2007-12-20 22:17 3317我前两天在NetOA方面确 ...
相关推荐
"BattleMoonWars 归档解压/压缩程序(砍掉重炼版)"是一个专为游戏《BattleMoonWars》设计的文件压缩与解压缩工具。这个工具由开发者进行了优化和改良,以提供更高效、更稳定的文件管理服务。在源码层面进行了重构,...
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
Unity3d 3D模型描边代码 懒人直接上代码
原文链接:https://alading.blog.csdn.net/article/details/141710476 包含功能: 经理管理:负责经理信息维护与权限分配,确保管理层操作的安全性和高效性。 员工管理:管理员工信息,包括招聘、离职、考勤及权限设置,优化人力资源配置。 商品分类管理:对商品进行科学分类,便于商品检索与管理,提升顾客购物体验。 商品信息管理:维护商品详细信息,如名称、价格、描述等,确保信息准确无误。 商品入库管理:监控商品入库流程,记录库存变化,实现库存精准管理。 商品销售管理:处理销售事务,包括销售记录、退货处理,支持销售业绩分析。 缺货提醒管理:自动检测库存水平,及时发出缺货警告,保障商品供应连续性。 商品收银管理:处理交易结算,支持多种支付方式,确保收银过程快速准确。 供应商管理:维护供应商信息,评估合作效果,优化供应链,保证商品质量与供应稳定性。
MATLAB实现工业PCB电路板缺陷识别和检测【图像处理实战】项目详情请参见:https://handsome-man.blog.csdn.net/article/details/130493170 PCB板检测的大概流程如下:首先存储一个标准PCB板图像作为良好板材的参考标准,然后将待检测的PCB板图像进行处理,比较与标准PCB图像的差异,根据差异的情况来判断缺陷类型。 项目代码可顺利编译运行~
Jupyter-Notebook
1991-2021年中国科技统计年鉴-最新数据发布.zip
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
BMI270 驱动开发程序
Jupyter-Notebook
Jupyter-Notebook
Jupyter-Notebook
上市公司基本资料及行业分类(2020)+员工构成数据大全(2006-2020)-最新发布.zip
安慰剂检验代码与数据文章-最新研究成果.zip
原文链接:https://alading.blog.csdn.net/article/details/141678636 包含功能: 用户管理:负责系统用户的注册、登录、权限分配及个人信息管理。 供应商管理:维护供应商信息,包括添加、编辑、删除供应商资料及合作状态。 商品分类管理:创建、修改和删除商品分类,便于商品归类与查找。 商品信息管理:管理商品详情,如添加新品、编辑商品属性、价格及库存量。 采购入库管理:记录采购订单详情,处理入库操作,更新库存数量。 销售信息管理:记录销售订单,追踪销售数据,支持销售分析与报表生成。 赠品入库管理:管理赠品入库流程,记录赠品信息,关联促销活动。 退货入库管理:处理客户退货,记录退货详情,恢复库存数量。 其他入库管理:管理非采购、非赠品的入库情况,如调拨入库等。 购买订单管理:跟踪客户购买订单状态,从下单到发货的全流程管理。 赠送信息管理:管理赠品发放规则,记录赠送活动执行情况。 盘点信息管理:定期进行库存盘点,记录盘点结果,调整库存差异。
演示:https://blog.csdn.net/m0_54925305/article/details/143660348?spm=1001.2014.3001.5501
Jupyter-Notebook
shiahdifhiahfiqefiwhfi weifwijfiwqufiqweefijeq0jfe