示例#1
0
        /// <summary>
        /// Updates both the index and pack file sizes
        /// </summary>
        public void UpdateFileSizes()
        {
            // Lazy way of doing it...
            try
            {
                var s = String.Format("{0}{1}", MainWindowVm.Instance.SelectedWorkingProfile.WorkingDirectory,
                                      EterHelper.ReplaceWithEixExt(Filename));
                SizeOfIndexFile = new FileInfo(s).Length;
            }
            catch (Exception)
            {
                SizeOfIndexFile = 0;
            }

            try
            {
                SizeOfPackFile = new FileInfo(String.Format("{0}{1}", MainWindowVm.Instance.SelectedWorkingProfile.WorkingDirectory,
                                                            EterHelper.ReplaceWithEpkExt(Filename))).Length;
            }
            catch (Exception)
            {
                SizeOfIndexFile = 0;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="packFile"></param>
        /// <param name="saveFilesPath"></param>
        /// <param name="indexKey"></param>
        /// <param name="packKey"></param>
        /// <param name="progressCallback"></param>
        /// <param name="fileLoggingCallback"></param>
        public static void UnpackFile(
            FileInfo packFile,
            string saveFilesPath,
            byte[] indexKey,
            byte[] packKey,
            Action<int, int> progressCallback,
            Action<ErrorItem, string> fileLoggingCallback)
        {
            if (!File.Exists(packFile.FullName))
            {
                throw new EterPackFileNotFoundException();
            }

            // Reads the index file
            List<IndexItem> workingList = ReadIndexFile(EterHelper.ReplaceWithEixExt(packFile.FullName), indexKey, StringHelpers.TrimExtension(packFile.Name));

            if (workingList == null)
                return;

            // Check userData paths
            Directory.CreateDirectory(saveFilesPath);

            // File result counters
            double totalFilesCounter = 0;
            int unnamedFilesCounter = 0;
            int operationResult = 0;

            foreach (IndexItem item in workingList)
            {
                if (item.Size < 16 && item.PackType > 0)
                {
                    //WindowLog.LogOutputMessage(Log.LogId.FILE_SIZE_TOO_SMALL, args: new object[] { item.Filename, item.Size }); TODO
                    continue;
                }

                DrivePointManager.CheckIfContainsDrivePoint(item.Filename);

                if (String.IsNullOrWhiteSpace(item.Filename))
                {
                    unnamedFilesCounter++;
                    continue;
                }

                switch (item.PackType)
                {
                    // Raw format
                    case 0:
                        operationResult = ProcessFileType0(
                            packFile.FullName,
                            String.Format("{0}{1}/", saveFilesPath, StringHelpers.TrimExtension(packFile.Name)),
                            item,
                            fileLoggingCallback);
                        break;
                    // LZO compressed file
                    case 1:
                        operationResult = ProcessFileType1(
                            packFile.FullName,
                            String.Format("{0}{1}/", saveFilesPath, StringHelpers.TrimExtension(packFile.Name)),
                            item,
                            fileLoggingCallback);
                        break;
                    // XTEA encrypted and LZO compressed file
                    case 2:
                        operationResult = ProcessFileType2(
                            packFile.FullName,
                            String.Format("{0}{1}/", saveFilesPath, StringHelpers.TrimExtension(packFile.Name)),
                            item,
                            packKey,
                            fileLoggingCallback);
                        break;
                    // Panama encryption
                    case 3:
                        operationResult = ProcessFileType3(
                            packFile.FullName,
                            String.Format("{0}{1}/", saveFilesPath, StringHelpers.TrimExtension(packFile.Name)),
                            item,
                            packKey,
                            fileLoggingCallback);
                        break;
                    default:
                        fileLoggingCallback(
                            new ErrorItem(item.Filename, String.Format("Type {0} is not yet supported.", item.PackType)),
                            null);
                        break;
                }

                totalFilesCounter += 1.0;

                // Update progress
                double actionProgress = (totalFilesCounter / workingList.Count * 100.0);
                progressCallback(operationResult, (int)actionProgress);
            }

            // If unnamed files were found, log it
            if (unnamedFilesCounter > 0)
                fileLoggingCallback(
                    new ErrorItem("UNDEFINED", unnamedFilesCounter + " files were ignored due to incomplete header info (no name)", unnamedFilesCounter),
                    null);
        }
        /// <summary>
        /// Buils EIX and EPK files
        /// </summary>
        /// <param name="list">Deserialized list</param>
        /// <param name="packFilePath">Path to EPK file, including the name</param>
        /// <param name="unpackedFilesPath">Path to unpacked files</param>
        /// <param name="indexKey">Index XTEA key</param>
        /// <param name="packKey">Pack XTEA key</param>
        /// <param name="errorLogCallBack"></param>
        /// <param name="progressCallback">Callback if progress updates are needed</param>
        /// <returns></returns>
        public static bool BuildIndexAndPackFiles(
            List<IndexItem> list,
            string packFilePath,
            string unpackedFilesPath,
            byte[] indexKey,
            byte[] packKey,
            Action<ErrorItem> errorLogCallBack,
            Action<int, int> progressCallback,
            Action fatalErrorCallback)
        {
            using (var fStream = new MemoryStream())
            {
                packFilePath = packFilePath.Replace("\\", "/");
                // Check if directory exists
                string directoryPath = packFilePath.Substring(0, packFilePath.LastIndexOf("/"));
                Directory.CreateDirectory(directoryPath);

                // Create stream to EPK file
                var epkStream = new FileStream(EterHelper.ReplaceWithEpkExt(packFilePath), FileMode.Create);

                // Size from header for each file
                int decompressedSize = 0;
                int compressedSize = 0;
                uint encryptedSize = 0;

                // File counter to index files
                int indexCount = 0;

                // Progress variables
                double actionProgress = 0;

                // FileOffset holder (EPK stream's length)
                int fileOffset = 0;

                // Write first header to EIX file
                fStream.Write(ConstantsBase.EterFourCc, 0, ConstantsBase.EterFourCc.Length);
                fStream.Write(BitConverter.GetBytes(2), 0, 4);
                fStream.Write(BitConverter.GetBytes(list.Count), 0, 4);

                try
                {
                    foreach (IndexItem item in list)
                    {
                        // Loop through items
                        var fileName = new byte[161];
                        var fileNameCrc = new byte[4];

                        //Index item's structure (totalizing 192 bytes)
                        #region Byte Holders

                        var fileIndex = new byte[4];
                        var realDataSize = new byte[4];
                        var dataSize = new byte[4];
                        var dataCrc = new byte[4];
                        var dataOffset = new byte[4];
                        var padding3B = new byte[3];

                        #endregion

                        // Set all backslashs to forwards slashes
                        item.Filename = item.Filename.Replace('\\', '/');

                        // Get raw data
                        byte[] rawData = IOHelper.ReadFile(unpackedFilesPath + item.Filename);

                        // Header sizees
                        int encryptedFileSize = 0;
                        int compressedFileSize = 0;
                        int decompressedFileSize = 0;

                        // Set real data & decompressed size to raw data's lenth
                        realDataSize = BitConverter.GetBytes(rawData.Length);
                        decompressedFileSize = rawData.Length;

                        // Set fileoffset to actual stream's length
                        fileOffset = (int)epkStream.Length;

                        #region File Type Processing

                        // Switch through the 3 possible cases
                        switch (item.PackType)
                        {
                            case 0:
                                // Write data to EPK stream
                                epkStream.Write(rawData, 0, rawData.Length);

                                // Set data size equal to raw data since no compression nor encrypted occured
                                dataSize = BitConverter.GetBytes(rawData.Length);
                                break;
                            case 1:
                            case 2:
                                // Compress data
                                byte[] compressedDataBuffer = LzoHelper.CompressData(rawData.Length, rawData);

                                // Create buffer to hold header + compressedData
                                byte[] compressedDataWithHeaderBuffer = new byte[compressedDataBuffer.Length + 4];

                                // Copy header and compressedData to previously created buffer
                                Array.Copy(ConstantsBase.LzoFourCc, 0, compressedDataWithHeaderBuffer, 0, 4);
                                Array.Copy(compressedDataBuffer, 0, compressedDataWithHeaderBuffer, 4, compressedDataBuffer.Length);

                                // Set dataSize to compressedSize (since it assumes it's type 1)
                                dataSize = BitConverter.GetBytes(compressedDataWithHeaderBuffer.Length + 16);

                                // Set compressedSize
                                compressedFileSize = compressedDataBuffer.Length;

                                // If type 2
                                if (item.PackType == 2)
                                {
                                    // Get encrypted size (ALWAYS the upper multiple)
                                    encryptedFileSize = GetUpperMultiple(compressedDataWithHeaderBuffer.Length);

                                    // Resize data to fit encryptedSize
                                    Array.Resize(ref compressedDataWithHeaderBuffer, encryptedFileSize);

                                    // Encrypt Data
                                    Xtea.Encrypt2(ref compressedDataWithHeaderBuffer, packKey);

                                    // Set dataSize to encryptedData + header
                                    dataSize = BitConverter.GetBytes(compressedDataWithHeaderBuffer.Length + 16);
                                }

                                // Write header of file to EPK stream
                                epkStream.Write(ConstantsBase.LzoFourCc, 0, 4);
                                epkStream.Write(BitConverter.GetBytes(encryptedFileSize), 0, 4);
                                epkStream.Write(BitConverter.GetBytes(compressedFileSize), 0, 4);
                                epkStream.Write(BitConverter.GetBytes(decompressedFileSize), 0, 4);

                                // Write actual data
                                epkStream.Write(compressedDataWithHeaderBuffer, 0, compressedDataWithHeaderBuffer.Length);
                                break;
                        }

                        #endregion

                        #region Building index file

                        // Check if string replacment is needed
                        string virtualPathFile = DrivePointManager.InsertDrivePoints(item.Filename);

                        // Populate byte[] with data
                        fileIndex = BitConverter.GetBytes(item.Index);
                        byte[] fileNameTemp = Encoding.Default.GetBytes(virtualPathFile);
                        fileNameCrc = CrcHelper.GetCrc32HashFromMemoryToByteArray(Encoding.Default.GetBytes(virtualPathFile));
                        realDataSize = (realDataSize == null) ? BitConverter.GetBytes(item.DiskSize) : realDataSize;
                        dataSize = (dataSize == null) ? BitConverter.GetBytes(item.Size) : dataSize;
                        dataCrc = CrcHelper.GetCrc32HashToByteArray(unpackedFilesPath + item.Filename);
                        dataOffset = BitConverter.GetBytes(fileOffset);
                        var compressedType = (byte)item.PackType;

                        // Check if filename buffer is expectedSize
                        if (fileNameTemp.Length != 161)
                        {
                            Array.Copy(fileNameTemp, 0, fileName, 0, fileNameTemp.Length);
                        }

                        // Write data to EIX's stream
                        fStream.Write(fileIndex, 0, fileIndex.Length);
                        fStream.Write(fileName, 0, 161);
                        fStream.Write(padding3B, 0, padding3B.Length);
                        fStream.Write(fileNameCrc.Reverse().ToArray(), 0, fileNameCrc.Length);
                        fStream.Write(realDataSize, 0, realDataSize.Length);
                        fStream.Write(dataSize, 0, dataSize.Length);
                        fStream.Write(dataCrc.Reverse().ToArray(), 0, dataCrc.Length);
                        fStream.Write(dataOffset, 0, dataOffset.Length);
                        fStream.WriteByte(compressedType);
                        fStream.Write(padding3B, 0, padding3B.Length);

                        indexCount++;
                        #endregion

                        // Update progress
                        actionProgress = (indexCount / (double)list.Count * 100.0);
                        progressCallback(0, (int)actionProgress);
                    }
                }
                catch (Exception ex)
                {
                    //WindowLog.LogExceptioToFile(ex.ToString()); TODO
                    fatalErrorCallback();
                    throw;
                }

                // Assign current stream's lenght to decmopressedSize
                decompressedSize = (int)fStream.Length;

                // Buffer to hold compressedData
                byte[] compressedData = LzoHelper.CompressData(decompressedSize, fStream.ToArray());

                // Buffer with compressedData + MCOZ header
                byte[] compressedDataWithHeader = new byte[compressedData.Length + 4];

                // Copy Header to buffer
                Array.Copy(ConstantsBase.LzoFourCc, 0, compressedDataWithHeader, 0, 4);

                // Copy data to buffer
                Array.Copy(compressedData, 0, compressedDataWithHeader, 4, compressedData.Length);

                // Save compressedSize
                compressedSize = compressedData.Length;

                // Save encryptedSize (round to upper multiple)
                encryptedSize = (uint)GetUpperMultiple(compressedSize + 4);

                // Resize array to fit new size
                Array.Resize(ref compressedDataWithHeader, (int)encryptedSize);

                // Encrypt data
                Xtea.Encrypt2(ref compressedDataWithHeader, indexKey);

                // Create buffer to hold final data + header
                var outputFileBuffer = new byte[compressedDataWithHeader.Length + 16];

                // Copy header to buffer
                Array.Copy(ConstantsBase.LzoFourCc, 0, outputFileBuffer, 0, 4);
                Array.Copy(BitConverter.GetBytes(encryptedSize), 0, outputFileBuffer, 4, 4);
                Array.Copy(BitConverter.GetBytes(compressedSize), 0, outputFileBuffer, 8, 4);
                Array.Copy(BitConverter.GetBytes(decompressedSize), 0, outputFileBuffer, 12, 4);

                // Copy data to buffer
                Array.Copy(compressedDataWithHeader, 0, outputFileBuffer, 16, compressedDataWithHeader.Length);

                // Close stream
                epkStream.Close();
                epkStream.Dispose();

                // Save file
                File.WriteAllBytes(EterHelper.ReplaceWithEixExt(packFilePath), outputFileBuffer);

                return true;
            }
        }