home *** CD-ROM | disk | FTP | other *** search
/ Xentax forum attachments archive / xentax.7z / 4427 / aisp_memo-20090510.7z / hed_dat3_src / src / HedFile.java < prev    next >
Encoding:
Java Source  |  2009-04-21  |  17.6 KB  |  590 lines

  1. import java.io.*;
  2. import java.nio.*;
  3. import java.nio.channels.*;
  4.  
  5. /**
  6.  * hedÑ╒ÑíÑñÑδñ╚íóñ╜ñ∞ñ╦┤╪╧óñ╣ñδdatÑ╒ÑíÑñÑδñ≥┴α║εñ╣ñδ╝Ω├╩ñ≥─≤╢íñ╣ñδÑ»ÑΘÑ╣
  7.  */
  8. public class HedFile extends HedFileFormat {
  9.     // hedÑ╒ÑíÑñÑδñ╦ñ╧│╩╟╝ñ╡ñ∞ñ╩ññ╛≡╩≤
  10.     public File mFile; // hedÑ╒ÑíÑñÑδ
  11.     public byte mHedInitial; // hedÑ╒ÑíÑñÑδñ╬░┼╣µ╕░1
  12.     public byte mHedDiff; // hedÑ╒ÑíÑñÑδñ╬░┼╣µ╕░2
  13.     public File mDataDir;
  14.     public File mDecodedDir;
  15.  
  16.     private static final String CES = "UTF-16LE";
  17.     private static final String BACKUP_EXT = FileUtils.BACKUP_EXT;
  18.  
  19.     //----------------------------------------------------------------------
  20.  
  21.     /** Ñ│Ñ≤Ñ╣Ñ╚ÑΘÑ»Ñ┐ */
  22.     public HedFile(File dataDir, File decodedDir) {
  23.     mFile = null;
  24.     mDataDir = dataDir;
  25.     mDecodedDir = decodedDir;
  26.     }
  27.  
  28.     /** Ñ│Ñ≤Ñ╣Ñ╚ÑΘÑ»Ñ┐ */
  29.     public HedFile(File dataDir, File decodedDir, File hedFile) throws IOException {
  30.     this(dataDir, decodedDir);
  31.     load(hedFile);
  32.     }
  33.  
  34.     //----------------------------------------------------------------------
  35.     // hedÑ╒ÑíÑñÑδñ╬╞╔ñ▀╣■ñ▀┤╪╖╕
  36.  
  37.     /**
  38.      * hedÑ╒ÑíÑñÑδñ≥Ñφí╝Ñ╔ñ╣ñδ
  39.      */
  40.     public void load(File hedFile) throws IOException {
  41.     mFile = hedFile;
  42.  
  43.     byte[] header = readHeader();
  44.     getBytes(mSignature, header, 0);
  45.     mEncodedSize = getUint(header, 4);
  46.     mVersion = getUint(header, 8);
  47.  
  48.     byte[] payload = readPayload();
  49.     int pos = 0;
  50.     
  51.     pos = getBytes(mXxx1, payload, pos);
  52.     mDatInfoSize = getUint(payload, pos);
  53.     pos += 4;
  54.     mTemplate = new String(payload, pos + 1, payload[pos] * 2, CES);
  55.     pos += 1 + payload[pos] * 2;
  56.     mMaxDatIndex = getUint(payload, pos);
  57.     pos += 4;
  58.     mMaxDatSize = getUint(payload, pos);
  59.     pos += 4;
  60.  
  61.     pos = getBytes(mXxx2, payload, pos);
  62.     if(getUint(payload, pos) > 10000)
  63.         // ═≡┐⌠Ñ╟í╝Ñ┐ñ╧10kÑ╨ÑñÑ╚░╩╛σñ╦ñ╧ñ╩ñΘñ╩ñññ╚▓╛─Ω
  64.         throw new RuntimeException("random data too large");
  65.     mDatRand = new byte[getUint(payload, pos)];
  66.     pos = getBytes(mDatRand, payload, pos + 4);
  67.     pos = getBytes(mXxx3, payload, pos);
  68.  
  69.     mMemberInfoSize = getUint(payload, pos);
  70.     pos += 4;
  71.     mNrMembers = getUint(payload, pos);
  72.     pos += 4;
  73.     for(;;) {
  74.         pos = loadMemberInfo(pos, payload);
  75.         if(pos >= payload.length)
  76.         break;
  77.     }
  78.     }
  79.  
  80.     /**
  81.      * hedñ╬Ñ╪Ñ├Ñ└(░┼╣µ▓╜ñ╖ñ╞ñ╩ññ└Φ╞¼╔⌠╩¼)ñ≥╞└ñδ
  82.      */
  83.     public byte[] readHeader() throws IOException {
  84.     FileChannel srcChan = new FileInputStream(mFile).getChannel();
  85.     ByteBuffer buf = ByteBuffer.allocate(12);
  86.     srcChan.read(buf);
  87.     srcChan.close();
  88.     return buf.array();
  89.     }
  90.  
  91.     /**
  92.      * hedñ╬Ñ┌ÑñÑφí╝Ñ╔(░┼╣µ▓╜ñ╖ñ╞ñóñδ╦▄┬╬╔⌠╩¼)ñ≥╞└ñδ
  93.      */
  94.     public byte[] readPayload() throws IOException {
  95.     FileChannel srcChan = new FileInputStream(mFile).getChannel();
  96.     ByteBuffer buf = ByteBuffer.allocate((int)srcChan.size() - 12);
  97.     srcChan.read(buf, 12);
  98.     srcChan.close();
  99.  
  100.     byte[] data = buf.array();
  101.     buf = null;
  102.  
  103.     // ╕░ñ≥┐Σ─Ωñ╖ñ╞Ñ╟Ñ│í╝Ñ╔
  104.     guessKeys(data);
  105.     decodePayload(mHedInitial, mHedDiff, data);
  106.  
  107.     return data;
  108.     }
  109.  
  110.     private void guessKeys(byte[] data) {
  111.     int init, diff;
  112.     byte[] tmp = new byte[4];
  113.     for(init = 0; init < 256; init++) {
  114.         for(diff = 0; diff < 256; diff++) {
  115.         getBytes(tmp, data, 0);
  116.         decodePayload((byte)init, (byte)diff, tmp);
  117.         if(tmp[0] == 1 && tmp[1] == 0 && tmp[2] == 0 && tmp[3] == 0) {
  118.             mHedInitial = (byte)init;
  119.             mHedDiff = (byte)diff;
  120.             return;
  121.         }
  122.         }
  123.     }
  124.     throw new RuntimeException("cannot find scramble data!!!");
  125.     }
  126.  
  127.     private static void decodePayload(byte init, byte diff, byte[] data) {
  128.     byte additive = init;
  129.     for(int offset = 0; offset < data.length; offset++) {
  130.         byte old = data[offset];
  131.         data[offset] = (byte)(((offset + additive) ^ old) - diff);
  132.         additive = old;
  133.     }
  134.     }
  135.  
  136.     /**
  137.      * posÑ╨ÑñÑ╚╠▄ñ½ñΘ╗╧ñ▐ñδ═╫┴╟Ñ╒ÑíÑñÑδ╛≡╩≤ñ≥╞╔ñ▀╝ΦñΩíómMembersñ╦─╔▓├ñ╣ñδíú
  138.      * @return ╝íñ╬═╫┴╟Ñ╒ÑíÑñÑδ╛≡╩≤ñ╬░╠├╓
  139.      */
  140.     private int loadMemberInfo(int pos, byte[] data) {
  141.     HedMember member = new HedMember();
  142.     try {
  143.         member.mPath1 = new String(data, pos + 1, data[pos] * 2, CES);
  144.         pos += 1 + data[pos] * 2;
  145.         member.mPath2 = new String(data, pos + 1, data[pos] * 2, CES);
  146.         pos += 1 + data[pos] * 2;
  147.     } catch(UnsupportedEncodingException e) {
  148.         throw new RuntimeException(e);
  149.     }
  150.     member.mDatIndex = getUint(data, pos + 0);
  151.     member.mFileOffset = getUint(data, pos + 4);
  152.     member.mMemberSize = getUint(data, pos + 8);
  153.     member.mUpdatedTime = getUint(data, pos + 12);
  154.     member.mUnknown = getUint(data, pos + 16);
  155.     member.mFile = new File((member.mPath1 + member.mPath2).replace('\\', '/'));
  156.     member.mDatModified = false;
  157.     pos += 20;
  158.     mMembers.add(member);
  159.     loadedMember(member);
  160.     return pos;
  161.     }
  162.  
  163.     protected void loadedMember(HedMember member) {
  164.     // ÑßÑ≤Ñ╨╛≡╩≤ñ≥Ñφí╝Ñ╔ñ╖ñ┐ñ╚ñ¡ñ╦╕╞ñ╨ñ∞ñδÑßÑ╜Ñ├Ñ╔
  165.     }
  166.  
  167.     //----------------------------------------------------------------------
  168.     // hedÑ╒ÑíÑñÑδñ╬╜±ñ¡╣■ñ▀┤╪╖╕
  169.  
  170.     /**
  171.      * HedFileÑñÑ≤Ñ╣Ñ┐Ñ≤Ñ╣ñ≥╣╜├█ñ╖ñ╩ñ¬ñ╣.
  172.      */
  173.     private void refreshInstance(boolean forced) throws IOException {
  174.     int datFileOffset = 0; // ┤√┬╕Ñ╒ÑíÑñÑδñ¼└Ωñßñ╞ñññδ╬╬░Φñ╬╕σ├╝
  175.  
  176.     // Ñ╤Ñ╣1: Ñ╒ÑíÑñÑδѬÑ╒Ñ╗Ñ├Ñ╚░╩│░ñ╬Ñ╒Ñúí╝ÑδÑ╔ñ≥╣╣┐╖
  177.     mDatInfoSize = 1 + mTemplate.length() * 2 + 4 + 4;
  178.     mMemberInfoSize = 4;
  179.     mNrMembers = 0;
  180.     for(int datFileIndex = 0;; datFileIndex++) {
  181.         int nrRefs = 0; // ñ│ñ╬datÑ╒ÑíÑñÑδñ╟╜Φ═²ñ╖ñ┐┐⌠
  182.         for(int m = 0; m < mMembers.size(); m++) {
  183.         HedMember member = mMembers.get(m);
  184.         if(member.mDatIndex != datFileIndex)
  185.             continue;
  186.  
  187.         // HedFileFormat╛≡╩≤ñ≥╣╣┐╖
  188.         ++nrRefs;
  189.         ++mNrMembers;
  190.         mMemberInfoSize += (1 + member.mPath1.length() * 2)
  191.             + (1 + member.mPath2.length() * 2) + 4 * 5;
  192.  
  193.         // ÑßÑ≤Ñ╨ñ┤ñ╚ñ╬Ñ╟í╝Ñ┐ñ≥╣╣┐╖(mFileOffset░╩│░)
  194.         File ifile = new File(mDecodedDir, member.mFile.getPath());
  195.         if(forced
  196.            || ifile.length() != member.mMemberSize
  197.            || ifile.lastModified() / 1000 != member.mUpdatedTime) {
  198.             member.mDatModified = true;
  199.             member.mMemberSize = (int)ifile.length();
  200.             member.mUpdatedTime = (int)(ifile.lastModified() / 1000);
  201.         }
  202.  
  203.         // ┤√┬╕Ñ╒ÑíÑñÑδñ¼└Ωñßñδ╬╬░Φñ╬║╟╕σ├╝ñ≥╗╗╜╨
  204.         if(member.mDatModified == false
  205.            && datFileOffset < member.mFileOffset + member.mMemberSize)
  206.             datFileOffset = member.mFileOffset + member.mMemberSize;
  207.         }
  208.         if(nrRefs == 0)
  209.         break;
  210.         mMaxDatIndex = datFileIndex;
  211.     }
  212.     mEncodedSize = mXxx1.length + (4 + mDatInfoSize)
  213.         + mXxx2.length + (4 + mDatRand.length) + mXxx3.length
  214.         + (4 + mMemberInfoSize);
  215.     if(mNrMembers != mMembers.size())
  216.         throw new RuntimeException("dat index jumped");
  217.  
  218.     // Ñ╤Ñ╣2: mFileOffsetñ≥╣╣┐╖
  219.     for(int datFileIndex = 0; datFileIndex <= mMaxDatIndex; datFileIndex++) {
  220.         for(int m = 0; m < mMembers.size(); m++) {
  221.         HedMember member = mMembers.get(m);
  222.         if(member.mDatIndex != datFileIndex)
  223.             continue;
  224.  
  225.         if(member.mDatModified) {
  226.             member.mFileOffset = datFileOffset;
  227.             datFileOffset += member.mMemberSize;
  228.         }
  229.         //System.out.printf("%c %8d %8d %s\n", (member.mDatModified ? '#' : '='), member.mFileOffset, member.mMemberSize, member.mFile.getPath());
  230.         }
  231.     }
  232.     }
  233.  
  234.     private static void encodePayload(byte init, byte diff, byte[] data) {
  235.     byte prev = init;
  236.     for(int offset = 0; offset < data.length; offset++) {
  237.         data[offset] = (byte)((data[offset] + diff) ^ (offset + prev));
  238.         prev = data[offset];
  239.     }
  240.     }
  241.  
  242.     /**
  243.      * hedÑ╒ÑíÑñÑδñ≥╩▌┬╕ñ╣ñδ
  244.      */
  245.     private void save() throws IOException {
  246.     FileUtils.makeBackupFile(mFile);
  247.  
  248.     ByteBuffer buf;
  249.     FileChannel dstChan = new FileOutputStream(mFile).getChannel();
  250.  
  251.     // hedñ╬Ñ╪Ñ├Ñ└(░┼╣µ▓╜ñ╖ñ╞ñ╩ññ╔⌠╩¼)ñ≥╜±ñ»
  252.     buf = ByteBuffer.allocate(12);
  253.     byte[] header = buf.array();
  254.     putBytes(header, 0, mSignature);
  255.     putUint(header, 4, mEncodedSize);
  256.     putUint(header, 8, mVersion);
  257.     dstChan.write(buf);
  258.  
  259.     // hedñ╬Ñ┌ÑñÑφí╝Ñ╔(░┼╣µ▓╜ñ╖ñ╞ñóñδ╔⌠╩¼)ñ≥║ε└«ñ╣ñδ
  260.     buf = ByteBuffer.allocate(mEncodedSize);
  261.     byte[] payload = buf.array();
  262.     int pos = 0;
  263.  
  264.     pos = putBytes(payload, pos, mXxx1);
  265.  
  266.     pos = putUint(payload, pos, mDatInfoSize);
  267.     pos = putString(payload, pos, mTemplate);
  268.     pos = putUint(payload, pos, mMaxDatIndex);
  269.     pos = putUint(payload, pos, mMaxDatSize);
  270.  
  271.     pos = putBytes(payload, pos, mXxx2);
  272.     pos = putUint(payload, pos, mDatRand.length);
  273.     pos = putBytes(payload, pos, mDatRand);
  274.     pos = putBytes(payload, pos, mXxx3);
  275.  
  276.     pos = putUint(payload, pos, mMemberInfoSize);
  277.     pos = putUint(payload, pos, mNrMembers);
  278.     for(int i = 0; i < mMembers.size(); i++) {
  279.         HedMember member = mMembers.get(i);
  280.         pos = putString(payload, pos, member.mPath1);
  281.         pos = putString(payload, pos, member.mPath2);
  282.         pos = putUint(payload, pos, member.mDatIndex);
  283.         pos = putUint(payload, pos, member.mFileOffset);
  284.         pos = putUint(payload, pos, member.mMemberSize);
  285.         pos = putUint(payload, pos, member.mUpdatedTime);
  286.         pos = putUint(payload, pos, member.mUnknown);
  287.     }
  288.  
  289.     // ░┼╣µ▓╜ñ╖ñ╞╜±ñ¡╣■ñα
  290.     encodePayload(mHedInitial, mHedDiff, payload);
  291.     buf.rewind();
  292.     dstChan.write(buf);
  293.  
  294.     dstChan.close();
  295.     }
  296.  
  297.     //----------------------------------------------------------------------
  298.     // datÑ╒ÑíÑñÑδñ╬┼╕│½╜Φ═²
  299.  
  300.     /**
  301.      * hedÑ╒ÑíÑñÑδñ¼╗╪ñ╖ñ╞ñññδÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ≥datÑ╒ÑíÑñÑδ╖▓ñ½ñΘ┼╕│½ñ╣ñδ.
  302.      * ÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ¼┼╕│½ñ╡ñ∞ñ╞ñññ╩ñññ½íó┼╕│½ñ╡ñ∞ñ┐╕σñ╦╩╤╣╣ñ╡ñ∞ñ╞ñññδ╛∞╣τíó
  303.      * ╝┬║▌ñ╬ÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδ┼╕│½ñ≥╣╘ñ╩ñªíú
  304.      */
  305.     public void extractAll() throws IOException {
  306.     extractAll(false);
  307.     }
  308.  
  309.     /**
  310.      * hedÑ╒ÑíÑñÑδñ¼╗╪ñ╖ñ╞ñññδÑ╒ÑíÑñÑδñ≥ñ╣ñ┘ñ╞datÑ╒ÑíÑñÑδñ½ñΘ┼╕│½ñ╣ñδ
  311.      * @param forced    ÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ≥╢»└⌐┼╕│½ñ╣ñδñ╚ñ¡true
  312.      */
  313.     public void extractAll(boolean forced) throws IOException {
  314.     for(int datFileIndex = 0; datFileIndex <= mMaxDatIndex; datFileIndex++)
  315.         extractDatByIndex(datFileIndex, forced);
  316.     }
  317.  
  318.     /**
  319.      * datÑ╒ÑíÑñÑδñ╬ÑñÑ≤Ñ╟Ñ├Ñ»Ñ╣ñ≥╗╪─Ωñ╖íóñ╜ñ∞ñ╦┤▐ñ▐ñ∞ñδÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ≥
  320.      * ┼╕│½ñ╣ñδíú
  321.      */
  322.     private void extractDatByIndex(int datFileIndex, boolean forced) throws IOException {
  323.     // ╞■╬╧Ñ╒ÑíÑñÑδíª╞■╬╧Ñ╨Ñ├Ñ╒Ñíñ╬╜α╚≈
  324.     String datPath = String.format(mTemplate, datFileIndex);
  325.     File datFile = new File(mDataDir, datPath);
  326.     FileChannel srcChan = new FileInputStream(datFile).getChannel();
  327.     ByteBuffer ibuf = null;
  328.     try {
  329.         ibuf = srcChan.map(FileChannel.MapMode.READ_ONLY,
  330.                    0, srcChan.size());
  331.     } catch(Exception e) {
  332.         ibuf = ByteBuffer.allocate((int)srcChan.size());
  333.         srcChan.read(ibuf);
  334.     }
  335.  
  336.     // ╜╨╬╧
  337.     for(int m = 0; m < mMembers.size(); m++) {
  338.         HedMember member = mMembers.get(m);
  339.         if(member.mDatIndex != datFileIndex)
  340.         continue;
  341.  
  342.         // Ñ╒ÑíÑñÑδñ¼ñ╣ñ╟ñ╦┼╕│½║╤ñ▀ñ╟ñ½ñ─╩╤╣╣ñ╡ñ∞ñ╞ñ╩ñññ╩ñΘíó
  343.         // ñóñΘñ┐ñßñ╞┼╕│½ñ╧ñ╖ñ╩ññíú
  344.         File ofile = new File(mDecodedDir, member.mFile.getPath());
  345.         if(!forced && ofile.isFile()
  346.            && ofile.length() == member.mMemberSize
  347.            && ofile.lastModified() / 1000 == member.mUpdatedTime)
  348.         continue;
  349.  
  350.         // ╜╨╬╧Ñ╒ÑíÑñÑδíª╜╨╬╧Ñ╨Ñ├Ñ╒Ñíñ╬╜α╚≈
  351.         ofile.getParentFile().mkdirs();
  352.         ByteBuffer obuf = ByteBuffer.allocate(member.mMemberSize);
  353.  
  354.         // ╔ⁿ╣µ
  355.         int oIdx = 0, rIdx = 0;
  356.         ibuf.position(member.mFileOffset);
  357.         for(; oIdx < member.mMemberSize; oIdx++) {
  358.         obuf.put((byte)(ibuf.get() - mDatRand[rIdx++]));
  359.         if(rIdx >= mDatRand.length)
  360.             rIdx = 0;
  361.         }
  362.  
  363.         // ╝ΦñΩ╜╨ñ╖ñ┐├µ┐╚ñ≥Ñ╒ÑíÑñÑδñ╦╜±ñ¡╜╨ñ╖
  364.         FileChannel dstChan = new FileOutputStream(ofile).getChannel();
  365.         obuf.rewind();
  366.         dstChan.write(obuf);
  367.         dstChan.close();
  368.         ofile.setLastModified(member.mUpdatedTime * 1000L);
  369.         extractedMember(member);
  370.     }
  371.     srcChan.close();
  372.     }
  373.  
  374.     protected void extractedMember(HedMember member) {
  375.     // ÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ≥┼╕│½ñ╖ñ┐╗■ñ╦╕╞ñ╨ñ∞ñδÑßÑ╜Ñ├Ñ╔
  376.     }
  377.  
  378.     //----------------------------------------------------------------------
  379.     // datÑ╒ÑíÑñÑδñ╬│╩╟╝╜Φ═²
  380.  
  381.     public void storeAll() throws IOException {
  382.     storeAll(false);
  383.     }
  384.  
  385.     /**
  386.      * ÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδ╛≡╩≤ñ≥╣╣┐╖ñ╖ñ╞ÑñÑ≤Ñ╣Ñ┐Ñ≤Ñ╣ñ╬└░╣τñ≥╝ΦñΩ─╛ñ╖íó
  387.      * datÑ╒ÑíÑñÑδñ╚hedÑ╒ÑíÑñÑδñ≥╣╣┐╖ñ╣ñδ
  388.      */
  389.     public void storeAll(boolean forced) throws IOException {
  390.     refreshInstance(forced);
  391.     save();
  392.  
  393.     for(int datFileIndex = 0; datFileIndex <= mMaxDatIndex; datFileIndex++)
  394.         storeDatByIndex(datFileIndex);
  395.     }
  396.  
  397.     /**
  398.      * datÑ╒ÑíÑñÑδñ╬ÑñÑ≤Ñ╟Ñ├Ñ»Ñ╣ñ≥╗╪─Ωñ╖íóñ╜ñ∞ñ╦┤▐ñ▐ñ∞ñδÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ≥
  399.      * │╩╟╝ñ╣ñδíú
  400.      */
  401.     private void storeDatByIndex(int datFileIndex) throws IOException {
  402.     String datPath = String.format(mTemplate, datFileIndex);
  403.     File datFile = new File(mDataDir, datPath);
  404.     FileUtils.makeBackupFile(datFile);
  405.  
  406.     //FileChannel dstChan = new FileOutputStream(datFile).getChannel();
  407.     FileChannel dstChan = new RandomAccessFile(datFile, "rw").getChannel();
  408.     for(int m = 0; m < mMembers.size(); m++) {
  409.         HedMember member = mMembers.get(m);
  410.         if(member.mDatIndex != datFileIndex)
  411.         continue;
  412.         if(!member.mDatModified)
  413.         continue;
  414.  
  415.         File ifile = new File(mDecodedDir, member.mFile.getPath());
  416.         FileChannel srcChan = new FileInputStream(ifile).getChannel();
  417.         ByteBuffer buf = ByteBuffer.allocate((int)srcChan.size());
  418.         byte[] data = buf.array();
  419.         srcChan.read(buf);
  420.         srcChan.close();
  421.  
  422.         // ╔Σ╣µ▓╜
  423.         int iIdx = 0, rIdx = 0;
  424.         for(; iIdx < data.length; iIdx++) {
  425.         data[iIdx] = (byte)(data[iIdx] + mDatRand[rIdx++]);
  426.         if(rIdx >= mDatRand.length)
  427.             rIdx = 0;
  428.         }
  429.         buf.rewind();
  430.         dstChan.write(buf, member.mFileOffset);
  431.         storedMember(member);
  432.     }
  433.  
  434.     dstChan.close();
  435.     }
  436.  
  437.     protected void storedMember(HedMember member) {
  438.     // ÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ≥│╩╟╝ñ╖ñ┐ñ╚ñ¡ñ╦╕╞ñ╨ñ∞ñδÑßÑ╜Ñ├Ñ╔
  439.     }
  440.  
  441.     //----------------------------------------------------------------------
  442.     // Ñ»ÑΘÑ╣ÑßÑ╜Ñ├Ñ╔╖▓1
  443.  
  444.     private static int getBytes(byte[] dest, byte[] src, int offset) {
  445.     System.arraycopy(src, offset, dest, 0, dest.length);
  446.     return offset + dest.length;
  447.     }
  448.  
  449.     private static int getValue(byte b) {
  450.     return (b < 0) ? b + 256 : b;
  451.     }
  452.  
  453.     private static int getUint(byte[] data, int pos) {
  454.     return (getValue(data[pos + 0]) << 0)
  455.         | (getValue(data[pos + 1]) << 8)
  456.         | (getValue(data[pos + 2]) << 16)
  457.         | (getValue(data[pos + 3]) << 24);
  458.     }
  459.  
  460.     private static int putUint(byte[] data, int pos, int value) {
  461.     data[pos + 0] = (byte)(value >> 0);
  462.     data[pos + 1] = (byte)(value >> 8);
  463.     data[pos + 2] = (byte)(value >> 16);
  464.     data[pos + 3] = (byte)(value >> 24);
  465.     return pos + 4;
  466.     }
  467.  
  468.     private static int putBytes(byte[] dest, int pos, byte[] src) {
  469.     System.arraycopy(src, 0, dest, pos, src.length);
  470.     return pos + src.length;
  471.     }
  472.  
  473.     private static int putString(byte[] data, int pos, String s) {
  474.     data[pos + 0] = (byte)s.length();
  475.     try {
  476.         return putBytes(data, pos + 1, s.getBytes(CES));
  477.     } catch(UnsupportedEncodingException e) {
  478.         throw new RuntimeException("invalid CES " + CES);
  479.     }
  480.     }
  481.  
  482.     //----------------------------------------------------------------------
  483.     // ├╓┤╣╜Φ═²
  484.  
  485.     private static final FileFilter PLAIN_FILE_FILTER = new FileFilter() {
  486.         public boolean accept(File file) {
  487.         return file.isFile();
  488.         }};
  489.         
  490.  
  491.     public static String getSubdirById(String type, String id) {
  492.     if(type.equals("chara"))
  493.         // "0123456" -> "chara/0/12345"
  494.         return "chara/" + id.substring(0, 1) + "/" + id.substring(1, 6);
  495.     else if(type.equals("item"))
  496.         // "01234567" -> "item/0/12/34567"
  497.         return "item/" + id.substring(0, 1) + "/" + id.substring(1, 3) + "/" + id.substring(3, 8);
  498.     else if(type.equals("field"))
  499.         return "world/field/" + id;
  500.     else
  501.         throw new RuntimeException("unknown type: " + type);
  502.     }
  503.  
  504.     /**
  505.      * Ñ¡ÑπÑΘÑ»Ñ┐Ñ╟í╝Ñ┐ñ╬├╓ñ¡┤╣ñ¿ñ≥╣╘ñ╩ñª
  506.      * @param virtId    ├╓ñ¡┤╣ñ¿┬╨╛▌ñ╬Ñ╟í╝Ñ┐ñ╬ID
  507.      * @param realId    ╗▓╛╚ñ╡ñ∞ñδÑ╟í╝Ñ┐ñ╬ID
  508.      */
  509.     public void replaceChara(String virtId, String realId) throws IOException {
  510.     String[] subdirs = {
  511.         "anim", "attr", "face", "model", "tex"
  512.     };
  513.  
  514.     if(virtId.equals(realId))
  515.         return;
  516.  
  517.     for(int i = 0; i < subdirs.length; i++)
  518.         genericReplace(mDecodedDir + "/"
  519.                + getSubdirById("chara", virtId) + "/" + subdirs[i],
  520.                mDecodedDir + "/"
  521.                + getSubdirById("chara", realId) + "/" + subdirs[i],
  522.                virtId.substring(0, 6),
  523.                realId.substring(0, 6));
  524.     }
  525.  
  526.     public void replaceItem(String virtId, String realId) throws IOException {
  527.     if(virtId.equals(realId))
  528.         return;
  529.  
  530.     genericReplace(mDecodedDir + "/"
  531.                + getSubdirById("item", virtId) + "/attr",
  532.                mDecodedDir + "/"
  533.                + getSubdirById("item", realId) + "/attr",
  534.                virtId, realId);
  535.     }
  536.  
  537.     private void genericReplace(String virtDir, String realDir, String vPrefix, String rPrefix) throws IOException {
  538.     File[] files;
  539.  
  540.     // ├╓ñ¡┤╣ñ¿└Φñ╬Ñ╒ÑíÑñÑδñ╬Ñ╨Ñ├Ñ»ÑóÑ├Ñ╫ñ≥║ε└«ñ╖íó╢⌡ñ╦ñ╣ñδ
  541.     files = new File(virtDir).listFiles(PLAIN_FILE_FILTER);
  542.     if(files != null)
  543.         for(int j = 0; j < files.length; j++) {
  544.         if(files[j].getName().endsWith(BACKUP_EXT))
  545.             continue; // Ñ╨Ñ├Ñ»ÑóÑ├Ñ╫Ñ╒ÑíÑñÑδñ╧└┌ñΩ╡═ñß┬╨╛▌│░
  546.         FileUtils.truncate(files[j]);
  547.         }
  548.  
  549.     // ╚∩╗▓╛╚Ñ╟í╝Ñ┐ñ╦ñóñδÑ╟í╝Ñ┐ñ≥íó╠╛┴░ñ≥╩╤ñ¿ñ─ñ─Ñ│Ñ╘í╝
  550.     files = new File(realDir).listFiles(PLAIN_FILE_FILTER);
  551.     if(files != null)
  552.         for(int j = 0; j < files.length; j++) {
  553.         if(files[j].getName().endsWith(BACKUP_EXT))
  554.             continue; // Ñ╨Ñ├Ñ»ÑóÑ├Ñ╫Ñ╒ÑíÑñÑδñ╧Ñ│Ñ╘í╝┬╨╛▌│░
  555.         String dstName = files[j].getName();
  556.         if(dstName.startsWith(rPrefix))
  557.             dstName = vPrefix + dstName.substring(vPrefix.length());
  558.         File dstFile = new File(virtDir, dstName);
  559.         dstFile.delete();
  560.         FileUtils.copyFile(files[j], dstFile, true);
  561.         replaced(files[j], dstFile);
  562.         }
  563.     }
  564.  
  565.     public void replaceField(String virtId, String realId) throws IOException {
  566.     String[] postfixes = {
  567.         ".vra", "_obj.vra"
  568.     };
  569.     String[] variants = {
  570.         "01", "02", "03", "04"
  571.     };
  572.     String virtDir = mDecodedDir + "/" + getSubdirById("field", virtId);
  573.     String realDir = mDecodedDir + "/" + getSubdirById("field", realId);
  574.     for(int p = 0; p < postfixes.length; p++) {
  575.         for(int v = 0; v < variants.length; v++) {
  576.         File vFile = new File(virtDir, virtId + "_" + variants[v] + postfixes[p]);
  577.         File rFile = new File(realDir, realId + "_01" + postfixes[p]);
  578.         FileUtils.makeBackupFile(vFile);
  579.         vFile.delete();
  580.         FileUtils.copyFile(rFile, vFile, true);
  581.         replaced(rFile, vFile);
  582.         }
  583.     }
  584.     }
  585.  
  586.     protected void replaced(File src, File dst) {
  587.     // ÑßÑ≤Ñ╨Ñ╒ÑíÑñÑδñ≥├╓ñ¡┤╣ñ¿ñ┐╗■ñ╦╕╞ñ╨ñ∞ñδÑßÑ╜Ñ├Ñ╔
  588.     }
  589. };
  590.