/// <summary> /// Enumerates all non-archive local files, BLT encodes them and adds them to the InstallFile /// </summary> /// <param name="maxDegreeOfParallelism"></param> /// <returns></returns> public void ExportFiles(int maxDegreeOfParallelism = 15) { var results = new ConcurrentBag <CASRecord>(); var block = new ActionBlock <string>(file => { // strip the local path and normalise var name = file[(file.IndexOf(BaseDirectory, Comparison) + BaseDirectory.Length)..].WoWNormalise(); // block table encode and export to the temp folder // then add appropiate tags var record = BlockTableEncoder.EncodeAndExport(file, Options.TempDirectory, name); record.Tags = TagGenerator.GetTags(file); if (!EncodingCache.ContainsEKey(record.EKey)) { EncodingCache.AddOrUpdate(record); } else { record.BLTEPath = ""; } results.Add(record); },
/// <summary> /// 设置输出数据 /// </summary> /// <param name="charStream"></param> /// <param name="encoding"></param> internal unsafe void SetBody(CharStream charStream, ref EncodingCache encoding) { if (charStream.Data.CurrentIndex == 0) { SetBody(); } else { freeBody(); int size = encoding.GetByteCountNotNull(charStream); AutoCSer.SubBuffer.Pool.GetBuffer(ref SubBuffer, size); if (SubBuffer.PoolBuffer.Pool == null) { encoding.WriteBytes(charStream, Body.Array = SubBuffer.Buffer); SubBuffer.Buffer = null; Type = ResponseType.ByteArray; } else { Body.Set(SubBuffer.Buffer, SubBuffer.StartIndex, size); encoding.WriteBytes(charStream, ref Body); Type = ResponseType.SubBuffer; } } }
/// <summary> /// Extracts a collection of files from an archive and BLTE encodes them /// </summary> /// <param name="mpq"></param> /// <param name="filenames"></param> /// <param name="maxDegreeOfParallelism"></param> /// <returns></returns> private async Task ExportFiles(MpqArchive mpq, IEnumerable <string> filenames, bool applyTags = false, int maxDegreeOfParallelism = 150) { var block = new ActionBlock <string>(file => { using var fs = mpq.OpenFile(file); // ignore PTCH files if (fs.Flags.HasFlag(MPQFileAttributes.PatchFile)) { return; } // patch has marked file for deletion so remove from filelist if (fs.Flags.HasFlag(MPQFileAttributes.DeleteMarker)) { FileList.TryRemove(file, out _); return; } if (fs.CanRead && fs.Length > 0) { var map = BlockTableEncoder.GetEMapFromExtension(file, fs.Length); if (!EncodingCache.TryGetRecord(MD5Hash.Parse(fs.GetMD5Hash()), file, out var record)) { record = BlockTableEncoder.EncodeAndExport(fs, map, Options.TempDirectory, file); EncodingCache.AddOrUpdate(record); } if (applyTags) { record.Tags = TagGenerator.GetTags(file, fs); } record.EBlock.EncodingMap = map; FileList.TryAdd(file, record); } }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism }); foreach (var file in filenames) { if (!FileList.ContainsKey(file)) { block.Post(file); } } block.Complete(); await block.Completion; }
/// <summary> /// 文件服务 /// </summary> protected FileServer() { ResponseEncoding = new EncodingCache(WebConfig.Encoding ?? AutoCSer.Config.Pub.Default.Encoding); if (ResponseEncoding.Encoding.CodePage == AutoCSer.Config.Pub.Default.Encoding.CodePage) { HtmlContentType = Http.ContentTypeAttribute.Html; JsContentType = Http.ContentTypeAttribute.Js; } else { HtmlContentType = EncodingCache.Ascii.GetBytesNotEmpty("text/html; charset=" + ResponseEncoding.Encoding.WebName); JsContentType = EncodingCache.Ascii.GetBytesNotEmpty("application/x-javascript; charset=" + ResponseEncoding.Encoding.WebName); } }
/// <summary> /// 设置输出数据 /// </summary> /// <param name="value"></param> /// <param name="isAscii"></param> /// <param name="encoding"></param> internal unsafe void SetBody(string value, bool isAscii, ref EncodingCache encoding) { if (value.Length == 0) { SetBody(); } else { freeBody(); if (isAscii && encoding.IsCompatibleAscii != 0) { int size = value.Length; AutoCSer.SubBuffer.Pool.GetBuffer(ref SubBuffer, size); fixed(char *textFixed = value) fixed(byte *bufferFixed = SubBuffer.GetFixedBuffer()) { if (SubBuffer.PoolBuffer.Pool == null) { Body.Array = SubBuffer.Buffer; AutoCSer.Extensions.StringExtension.WriteBytes(textFixed, size, bufferFixed); SubBuffer.Buffer = null; Type = ResponseType.ByteArray; } else { Body.Set(SubBuffer.Buffer, SubBuffer.StartIndex, size); AutoCSer.Extensions.StringExtension.WriteBytes(textFixed, size, bufferFixed + Body.Start); Type = ResponseType.SubBuffer; } } } else { int size = encoding.GetByteCountNotNull(value); AutoCSer.SubBuffer.Pool.GetBuffer(ref SubBuffer, size); if (SubBuffer.PoolBuffer.Pool == null) { encoding.WriteBytesNotEmpty(value, Body.Array = SubBuffer.Buffer); SubBuffer.Buffer = null; Type = ResponseType.ByteArray; } else { Body.Set(SubBuffer.Buffer, SubBuffer.StartIndex, size); encoding.WriteBytesNotEmpty(value, Body.Array, Body.Start); Type = ResponseType.SubBuffer; } } } }
/// <summary> /// 文件流写入器 /// </summary> /// <param name="fileName">文件全名</param> /// <param name="mode">打开方式</param> /// <param name="fileShare">共享访问方式</param> /// <param name="fileOption">附加选项</param> /// <param name="bufferSize">缓冲区字节大小</param> /// <param name="log">日志处理</param> /// <param name="encoding">文件编码</param> internal FileStreamWriter(string fileName, FileMode mode, FileShare fileShare, FileOptions fileOption, SubBuffer.Size bufferSize, ILog log, EncodingCache encoding) { if (string.IsNullOrEmpty(fileName)) { throw new ArgumentNullException("fileName is null"); } FileName = fileName; this.log = log; this.fileShare = fileShare; this.fileOption = fileOption; this.encoding = encoding; bufferPool = SubBuffer.Pool.GetPool(bufferSize); open(mode); }
/// <summary> /// Iterates all loose files within the data directory and BLT encodes them /// </summary> /// <param name="filenames"></param> public void EnumerateLooseDataFiles(IEnumerable <string> filenames) { if (!filenames.Any()) { return; } Log.WriteLine("Exporting loose Data files"); var block = new ActionBlock <string>(file => { var filename = GetInternalPath(file); var record = BlockTableEncoder.EncodeAndExport(file, Options.TempDirectory, filename); record.Tags = TagGenerator.GetTags(file); if (!EncodingCache.ContainsEKey(record.EKey)) { EncodingCache.AddOrUpdate(record); } else { record.BLTEPath = ""; } FileList.TryAdd(filename, record); }, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 150 }); foreach (var f in filenames) { block.Post(f); } block.Complete(); block.Completion.Wait(); }
/// <summary> /// Some alpha MPQs are hotswappable and only contain a single file and it's checksum /// </summary> /// <param name="mpq"></param> /// <param name="archivename"></param> private bool TryReadAlpha(MpqArchive mpq, string archivename) { // strip the local path and extension to get the filename var file = Path.ChangeExtension(GetInternalPath(archivename), null).WoWNormalise(); if (FileList.ContainsKey(file)) { return(true); } // add the filename as the listfile var internalname = Path.GetFileName(file); mpq.AddListFile(internalname); // read file if known if (mpq.HasFile(internalname)) { using var fs = mpq.OpenFile(internalname); if (fs.CanRead && fs.Length > 0) { var map = BlockTableEncoder.GetEMapFromExtension(file, fs.Length); if (!EncodingCache.TryGetRecord(MD5Hash.Parse(fs.GetMD5Hash()), file, out var record)) { record = BlockTableEncoder.EncodeAndExport(fs, map, Options.TempDirectory, file); EncodingCache.AddOrUpdate(record); } record.EBlock.EncodingMap = map; FileList.TryAdd(file, record); return(true); } } return(false); }
internal void WriteNotPool(string value, byte[] buffer, ref EncodingCache encoding) { Buffer.Set(buffer, 0); WriteIndex = encoding.WriteBytesNotEmpty(value, buffer); IsWait = false; }
internal void Write(string value, ref EncodingCache encoding) { WriteIndex += encoding.WriteBytesNotEmpty(value, Buffer.Buffer, WriteIndex); }
internal void Set(UnmanagedStream responseStream, ref EncodingCache encoding) { this.ResponseStream = responseStream; this.encoding = encoding; }