/// <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> /// Action performed when Pack file is hit /// </summary> /// <param name="param"></param> private async void PackFileAction(object param) { if (SelectedFilter < 0) { UserInput.ShowMessage("USER_SELECT_PACK_TYPE"); return; } QueueActionType = ActionType.Pack; if (!IsItemReady()) { return; } FilesActionVm.Instance.CcFiles++; SetItemState(State.Packing | State.LongAction); //await Task.Delay(1000); //this.SetItemState(State.Ready); //WorkingListVM.Instance.ProcessQueueUponActionFinalization(this); //return; HashMismatchFiles.Clear(); ErrorList.Clear(); _successCounter = 0; var filesInDir = IOHelper.GetAllFilesFromDir(String.Format("{0}{1}", MainWindowVm.Instance.SelectedWorkingProfile.UnpackDirectory, DisplayName)); var indexItems = new List <IndexItem>(); string indexFilePath = String.Format("{0}{1}", MainWindowVm.Instance.SelectedWorkingProfile.WorkingDirectory, String.Format("{0}{1}", DisplayName, MainWindowVm.Instance.SelectedWorkingProfile.IndexExtension)); string packFilePath = String.Format("{0}{1}", MainWindowVm.Instance.SelectedWorkingProfile.WorkingDirectory, String.Format("{0}{1}", DisplayName, MainWindowVm.Instance.SelectedWorkingProfile.PackExtension)); if (File.Exists(packFilePath)) { // File already packed, but will be overwritten WindowLog.Warning("FILE_ALREADY_PACKED_BUT_OVER", Filename); File.Delete(packFilePath); } if (File.Exists(indexFilePath)) { File.Delete(EterHelper.ReplaceWithEpkExt(indexFilePath)); } await Task.Run(() => { int counter = 0; foreach (var file in filesInDir) { //int type = -1; //string fileExtension = Path.GetExtension(file.FullName); //if (SelectedFilter.RawExtensions != null) //{ // foreach (var rawExt in SelectedFilter.RawExtensions) // if (rawExt.ToLower() == fileExtension.ToLower()) // type = 0; // foreach (var lzoExt in SelectedFilter.LzoExtensions) // if (lzoExt.ToLower() == fileExtension.ToLower()) // type = 1; // foreach (var xteaExt in SelectedFilter.XteaExtensions) // if (xteaExt.ToLower() == fileExtension.ToLower()) // type = 2; //} //if (type == -1) // type = SelectedFilter.NotIncludedExtensionsType; string toReplaceStr = String.Format("{0}{1}", MainWindowVm.Instance.SelectedWorkingProfile.UnpackDirectory, DisplayName).Replace("/", "\\"); string fileName = file.FullName.Substring(file.FullName.IndexOf(toReplaceStr) + toReplaceStr.Length + 1); indexItems.Add(new IndexItem( counter, fileName, null, 0, 0, null, 0, SelectedFilter, DisplayName)); counter++; } double lastProgressValue = 0; try { EterFilesDal.BuildIndexAndPackFiles( indexItems, packFilePath, String.Format("{0}{1}\\", MainWindowVm.Instance.SelectedWorkingProfile.UnpackDirectory, DisplayName), MainWindowVm.Instance.SelectedWorkingProfile.IndexKey, MainWindowVm.Instance.SelectedWorkingProfile.PackKey, (error) => { if (error != null) { ErrorList.Add(error); } }, (result, progress) => { if (result == 0) { _successCounter++; } if (((progress - lastProgressValue) >= 5)) { ActionProgress = progress; lastProgressValue = progress; } }, () => SetItemState(State.CriticalError)); } catch (OutOfMemoryException ex) { WindowLog.Warning("FILE_TOO_BIG", DisplayName); SetItemState(State.CriticalError); } catch (EterPackFileNotFoundException ex) { WindowLog.Error("ETER_EPK_FILE_NOT_FOUND", DisplayName); SetItemState(State.CriticalError); } catch (FileNotFoundException ex) { WindowLog.Error("FILE_NOT_FOUND", DisplayName, new object[] { ex.FileName }); SetItemState(State.CriticalError); } catch (UnauthorizedAccessException ex) { WindowLog.Error("COULD_NOT_ACCESS_FILE", DisplayName, DisplayName); SetItemState(State.CriticalError); } catch (System.IO.IOException ex) { WindowLog.Error("ERROR_WITH_CUSTOM_MSG", DisplayName, ex.Message); SetItemState(State.CriticalError); } }); SetItemState(ErrorList.Count > 0 ? State.ReadyWithErrors : State.Ready); ActionProgress = 100; WindowLog.Information("ETER_PACK_RESULT", DisplayName, new object[] { _successCounter, HashMismatchFiles.Count, ErrorList.Count }); AfterLongAction(param); }
/// <summary> /// Action performed when Unpack file is hit /// </summary> /// <param name="param"></param> private async void UnpackFileAction(object param) { QueueActionType = ActionType.Unpack; if (!IsItemReady()) { return; } FilesActionVm.Instance.CcFiles++; SetItemState(State.Unpacking | State.LongAction); await Task.Run(() => { // Set state to unpacking SetItemState(State.Unpacking); // Clear lists HashMismatchFiles.Clear(); ErrorList.Clear(); // Reset counters _successCounter = 0; double lastProgressValue = 0; try { // Unpack file EterFilesDal.UnpackFile( new FileInfo(EterHelper.ReplaceWithEpkExt(Path.Combine(MainWindowVm.Instance.SelectedWorkingProfile.WorkingDirectory, Filename))), MainWindowVm.Instance.SelectedWorkingProfile.UnpackDirectory, MainWindowVm.Instance.SelectedWorkingProfile.IndexKey, MainWindowVm.Instance.SelectedWorkingProfile.PackKey, (operationResult, globalProgress) => { if ((globalProgress - lastProgressValue) >= 5) { ActionProgress = globalProgress; lastProgressValue = globalProgress; } if (operationResult == 0) { _successCounter++; } }, (error, hash) => { if (error != null) { ErrorList.Add(error); } if (!String.IsNullOrWhiteSpace(hash)) { HashMismatchFiles.Add(hash); } }); } catch (ErrorReadingIndexException ex) { WindowLog.Error("ETER_WRONG_INDEX_KEY", DisplayName); SetItemState(State.CriticalError); } catch (OutOfMemoryException ex) { WindowLog.Warning("FILE_TOO_BIG", DisplayName); SetItemState(State.CriticalError); } catch (EterPackFileNotFoundException ex) { WindowLog.Error("ETER_EPK_FILE_NOT_FOUND", DisplayName); SetItemState(State.CriticalError); } catch (FileNotFoundException ex) { WindowLog.Error("FILE_NOT_FOUND", DisplayName, new object[] { ex.FileName }); SetItemState(State.CriticalError); } catch (UnauthorizedAccessException ex) { WindowLog.Error("COULD_NOT_ACCESS_FILE", DisplayName, DisplayName); SetItemState(State.CriticalError); } catch (System.IO.IOException ex) { WindowLog.Error("ERROR_WITH_CUSTOM_MSG", DisplayName, ex.Message); SetItemState(State.CriticalError); } }); // If any file produced an error, set state accordingly if (ErrorList.Count > 0 && ItemState != State.CriticalError) { SetItemState(State.ReadyWithErrors); } else if (ErrorList.Count == 0 && ItemState != State.CriticalError) { SetItemState(State.Ready); } // Make sure progress bar is at 100% ActionProgress = 100; // Logging stuff if (ItemState != State.CriticalError) { // Get all failed items int failedCount = ErrorList.Count; // Were any unnamed files present? var item = ErrorList.FirstOrDefault(x => x.ErrorMotive.Contains("(no name)")); // If so, add it if (item != null) { failedCount += Convert.ToInt32(item.Arg) - 1; } WindowLog.Information("ETER_UNPACK_RESULT", DisplayName, _successCounter, HashMismatchFiles.Count, failedCount); } AfterLongAction(param); }
/// <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; } }