├── BcsExtractor.csproj ├── Rune.md ├── BcsExtractor.sln ├── FileFormat.md ├── ProxyStream.cs ├── README.md ├── .gitignore ├── LICENSE ├── BcsExtractor.cs └── Blowfish.cs /BcsExtractor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | 1.1.0 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | D:\Program Files\Visual Studio\Common7\IDE\PrivateAssemblies\System.Text.Encoding.CodePages.dll 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Rune.md: -------------------------------------------------------------------------------- 1 | # Differences with Rune visual novels 2 | 3 | In Rune visual novels, the first and second sections are identical. However the third one is not encrypted with blowfish. Instead it is compressed with lzss, the same lzss that the entire `.bcs` file is initially compressed with. This means that the string section in the `.bcs` of Rune visual novels are compressed twice. 4 | 5 | `bcsextractor.exe` should be able to extract the scripts from the Rune games, but it looks like other things also change so the folder replacement trick that can be used in Tanuki Soft and Kaeru Soft games is not guaranteed to work. 6 | 7 | I've only tried on Musume Shimai, and it appeared to try opening the folder as a `.g2` archive and failed. The only other instance I know of is a previous translation of Hatsukoi, where they were able to use the folder replacement but the game was older and used `.g` archives. 8 | There might be some way to get it to work, but I did not look too hard. If anyone was interested, I would start there and try to see if the folder replacement can work. 9 | 10 | Otherwise, you'd have to turn the csvs back to bcs, and then put the bcs files back in a `.g2` archive. 11 | -------------------------------------------------------------------------------- /BcsExtractor.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29519.87 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BcsExtractor", "BcsExtractor.csproj", "{B8B7DA37-AC2E-44F3-AA54-916DEB4A5592}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5F1C2D86-0447-4CD6-ACFA-ACFA311BED45}" 9 | ProjectSection(SolutionItems) = preProject 10 | Class2.cs = Class2.cs 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {B8B7DA37-AC2E-44F3-AA54-916DEB4A5592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {B8B7DA37-AC2E-44F3-AA54-916DEB4A5592}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {B8B7DA37-AC2E-44F3-AA54-916DEB4A5592}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {B8B7DA37-AC2E-44F3-AA54-916DEB4A5592}.Release|Any CPU.Build.0 = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {4E3A3170-1962-4623-A212-4AF093C4974A} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /FileFormat.md: -------------------------------------------------------------------------------- 1 | # Documentation of `.bcs` format 2 | 3 | The file itself is lzss compressed, and after decompression it is separated into 3 distinct sections. The header is as follows: 4 | 5 | ``` 6 | struct BCSHeader 7 | { 8 | char magic[4]; // TSV 9 | unsigned int uncompressedSize; 10 | unsigned int firstSectionObjectCount; 11 | unsigned int objectMark; 12 | unsigned int secondSectionObjectCount; 13 | unsigned int thirdSectionSize; 14 | }; 15 | ``` 16 | 17 | The first section is not that useful, it consists of consecutive entries that are split in two parts, each 4 bytes. The first part will be the number of columns in the eventual csv, the second will be an offset. The offset just increases by the number of columns in each consecutive entry. 18 | The second section is an index that is used to build the csv. Like the first section, it consists of consecutive entries split into two parts, each 4 bytes. The first part is an operand, denoting what should be done. 19 | 20 | - `0x01` means that the value is a plain 32-bit integer, the second part of the entry is then that integer. 21 | - `0x03` means that it is an offset into the third section, the table of strings. 22 | - `0x00` means this field is empty, and should be left blank. 23 | 24 | Each entry corresponds to one field in one row of the csv. Thus, to build one row of the csv, an equal amount of entries as the number of columns must be read. 25 | 26 | The third section is the table of strings. However, it actually appears to be a separate file format entirely. This is also encrypted with blowfish using the key `TLibDefKey`. After decryption, the all the strings in the csv come from offsets into this section. 27 | -------------------------------------------------------------------------------- /ProxyStream.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.IO; 5 | 6 | namespace BcsExtractor 7 | { 8 | // This is basically ProxyStream taken from GARBro, all credit to its creator morkt 9 | public class ProxyStream : Stream 10 | { 11 | Stream m_stream; 12 | bool m_should_dispose; 13 | 14 | public ProxyStream(Stream input, bool leave_open = false) 15 | { 16 | m_stream = input; 17 | m_should_dispose = !leave_open; 18 | } 19 | 20 | public Stream BaseStream { get { return m_stream; } } 21 | 22 | public override bool CanRead { get { return m_stream.CanRead; } } 23 | public override bool CanSeek { get { return m_stream.CanSeek; } } 24 | public override bool CanWrite { get { return m_stream.CanWrite; } } 25 | public override long Length { get { return m_stream.Length; } } 26 | public override long Position 27 | { 28 | get { return m_stream.Position; } 29 | set { m_stream.Position = value; } 30 | } 31 | 32 | public override int Read(byte[] buffer, int offset, int count) 33 | { 34 | return m_stream.Read(buffer, offset, count); 35 | } 36 | 37 | public override void Flush() 38 | { 39 | m_stream.Flush(); 40 | } 41 | 42 | public override long Seek(long offset, SeekOrigin origin) 43 | { 44 | return m_stream.Seek(offset, origin); 45 | } 46 | 47 | public override void SetLength(long length) 48 | { 49 | m_stream.SetLength(length); 50 | } 51 | 52 | public override void Write(byte[] buffer, int offset, int count) 53 | { 54 | m_stream.Write(buffer, offset, count); 55 | } 56 | 57 | bool _proxy_disposed = false; 58 | protected override void Dispose(bool disposing) 59 | { 60 | if (!_proxy_disposed) 61 | { 62 | if (m_should_dispose && disposing) 63 | m_stream.Dispose(); 64 | _proxy_disposed = true; 65 | base.Dispose(disposing); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BcsExtractor 2 | 3 | Extracts scripts from Tanuki Soft and Kaeru Soft visual novels, specifically `.bcs` files. 4 | 5 | Script extraction should work for every Tanuki Soft, Kaeru Soft, and Rune visual novel. The folder replacement 6 | described here to put the extracted csv scripts back in the games should work for all Tanuki Soft and Kaeru Soft 7 | visual novels, but it is not guaranteed to work for all Rune visual novels. 8 | 9 | # Replacing the original scripts 10 | 11 | 1. Extract the `.bcs` files from the archives, generally they are in `datascn.tac`, `scenario.g2`, or `scenario.g`. I recommend using GARBro for this, but other tools will work and might be necessary depending on how old the game is. 12 | 2. Run `bcsextractor.exe` using extract and point it to the `.bcs` files. 13 | 14 | ``` 15 | bcsextractor.exe extract bcs_files -o put_csvs_here 16 | ``` 17 | 18 | 3. Delete or move the original scenario archive(ie. `datascn.tac`) in the visual novel's main directory. 19 | 4. Make a new folder with the same name as the scenario archive(ie. `datascn.tac`) as replacement. 20 | 5. Place the csvs generated by `bcsextractor.exe` into the new folder created. 21 | 22 | # Translating text 23 | 24 | To translate the text, all you need to do is replace the japanese text in the csvs with other text. The game should automatically pick them up and work without issue. Mostly you will replace text under the `%text%` fields, but names and choices can also be translated. 25 | 26 | Note that since this is a csv, you MUST wrap text that contains a comma in quotation marks like so: 27 | ``` 28 | 10900 ,,,,,,,,,,,,,,,,,,,,"Text that has, a comma" 29 | ``` 30 | 31 | Be careful not to change a command or variable, the game might break if it can't find those. 32 | 33 | `bcsextractor.exe` also has a format command that adds word wrapping for ease of use. See `bcsextractor.exe format -h`. 34 | 35 | # Troubleshooting 36 | 37 | - Q: GARBro extracts the `.bcs` files as garbled strings instead of something like `04haruna.bcs`. 38 | - A: Due to how the `.tac` extraction works, unless GARBro knows what the name is going to be before it extracts it, it can't determine the name. It keeps a file in `GameData/tanuki.lst` in its installation directory that is a list of all the known Tanuki Soft/Kaeru Soft `.bcs` filenames. If you extract `_project.csv`, it should tell you all the `.bcs` filenames for the scripts in that game. Add them to GARBro's `GameData/tanuki.lst` and GARBro should recognize it after. I've added an expanded `tanuki.lst` in this repo as well. 39 | 40 | - Q: All the emote sprites disappeared. 41 | - A: `_emote.csv` is currently broken, just place the original `_emote.bcs` in the `datascn.tac` folder instead. There is nothing to translate in `_emote.bcs` anyway. The extractor should refuse to extract emote.bcs due to this. 42 | 43 | - Q: The game doesn't work after replacing the scripts. 44 | - A: Make sure the folder has the exact same name as the scenario archive like `datascn.tac` or `scenario.g2`. If the game is a Rune visual novel, see the linked documentation as it's not guaranteed to work. 45 | 46 | # Documentation 47 | 48 | [.bcs format](FileFormat.md) 49 | 50 | [Differences with Rune visual novels](Rune.md) 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /BcsExtractor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.CommandLine; 4 | using System.CommandLine.Invocation; 5 | using System.IO; 6 | using System.Text; 7 | using Microsoft.VisualBasic.FileIO; 8 | 9 | namespace BcsExtractor 10 | { 11 | class BcsExtractor 12 | { 13 | static int Main(string[] args) 14 | { 15 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 16 | var extract = new Command("extract", "Extract .csv scripts from .bcs files. Extracts from every recognizable .bcs file if given a directory.") 17 | { 18 | new Argument("input", "File or directory containing .bcs files."), 19 | new Option(new[] { "--output", "-o" }, "Output directory. Defaults to current working directory."), 20 | new Option(new[] { "--overwrite" }, "Overwrite existing files in the output path."), 21 | new Option(new[] { "--verbose", "-v" }, "Print more details."), 22 | }; 23 | 24 | var format = new Command("format", "Format .csv scripts for word wrapping purposes. Works on every .csv file if given a directory.") 25 | { 26 | new Argument("input", "File or directory containing .csv files."), 27 | new Option(new[] { "--output", "-o" }, "Output directory. Defaults to current working directory."), 28 | new Option(new[] { "--overwrite" }, "Overwrite existing files in the output path."), 29 | new Option(new[] { "--verbose", "-v" }, "Print more details."), 30 | new Option(new[] { "--keep-newlines" }, "Keep the old newlines(\\n) in the file, otherwise they are removed before formatting."), 31 | new Option(new[] { "--wrap-length", "-wl" }, "Number of characters in a row until it should wrap. Defaults to 50."), 32 | }; 33 | 34 | extract.Handler = CommandHandler.Create(Extract); 35 | format.Handler = CommandHandler.Create(Format); 36 | 37 | var cmd = new RootCommand 38 | { 39 | extract, 40 | format 41 | }; 42 | 43 | return cmd.InvokeAsync(args).Result; 44 | } 45 | 46 | internal static void Extract(String input, String? output, bool verbose, bool overwrite) 47 | { 48 | String outputDir; 49 | FileAttributes attr = File.GetAttributes(input); 50 | if (output != null) 51 | outputDir = Directory.CreateDirectory(output).FullName; 52 | else 53 | outputDir = Directory.GetCurrentDirectory(); 54 | 55 | if (attr.HasFlag(FileAttributes.Directory)) 56 | { 57 | DirectoryInfo dir = new DirectoryInfo(input); 58 | FileInfo[] files = dir.GetFiles(); 59 | foreach (FileInfo file in files) 60 | ExtractFile(file.FullName, outputDir, verbose, overwrite); 61 | } 62 | else 63 | ExtractFile(new FileInfo(input).FullName, outputDir, verbose, overwrite); 64 | } 65 | 66 | internal static void Format(String input, String? output, bool verbose, bool overwrite, bool keepNewlines, int wrapLength) 67 | { 68 | String outputDir; 69 | int wc = wrapLength; 70 | if (wc == 0) 71 | wc = 50; 72 | 73 | FileAttributes attr = File.GetAttributes(input); 74 | if (output != null) 75 | outputDir = Directory.CreateDirectory(output).FullName; 76 | else 77 | outputDir = Directory.GetCurrentDirectory(); 78 | 79 | 80 | if (attr.HasFlag(FileAttributes.Directory)) 81 | { 82 | DirectoryInfo dir = new DirectoryInfo(input); 83 | FileInfo[] files = dir.GetFiles(); 84 | foreach (FileInfo file in files) 85 | FormatFile(file.FullName, outputDir, wc, verbose, overwrite, keepNewlines); 86 | } 87 | else 88 | FormatFile(new FileInfo(input).FullName, outputDir, wc, verbose, overwrite, keepNewlines); 89 | } 90 | 91 | internal static void FormatFile(String input, String output, int wrapCount, bool verbose, bool overwrite, bool keepNewlines) 92 | { 93 | if (Path.GetExtension(input) != ".csv") 94 | { 95 | if (verbose == true) 96 | Console.WriteLine("Skipping {0} because it is not a .csv file", input); 97 | return; 98 | } 99 | else if(overwrite == false && File.Exists(output + "/" + Path.GetFileName(input))) 100 | { 101 | Console.WriteLine("Skipping {0} because output file already exists", input); 102 | return; 103 | } 104 | 105 | String[] fields; 106 | String[] splitLine; 107 | String temp; 108 | int wordCount; 109 | 110 | using (TextFieldParser csvParser = new TextFieldParser(input, Encoding.GetEncoding(932))) 111 | { 112 | csvParser.CommentTokens = new string[] { "#" }; 113 | csvParser.SetDelimiters(new string[] { "," }); 114 | csvParser.HasFieldsEnclosedInQuotes = true; 115 | csvParser.TrimWhiteSpace = false; 116 | 117 | fields = csvParser.ReadFields(); 118 | int textIndex = 0; 119 | 120 | foreach (String field in fields) 121 | { 122 | if (field.Equals("%text%") || field.Equals("%text")) 123 | break; 124 | textIndex++; 125 | } 126 | 127 | if (textIndex >= fields.Length) 128 | { 129 | if (verbose == true) 130 | Console.WriteLine("Skipping {0} because it has no text field", input); 131 | return; 132 | } 133 | 134 | using (StreamWriter sw = new StreamWriter(File.Open(output + "/" + Path.GetFileName(input), FileMode.Create), Encoding.GetEncoding(932))) 135 | { 136 | sw.Write(String.Join(",", fields)); 137 | sw.Write("\r\n"); 138 | while (!csvParser.EndOfData) 139 | { 140 | fields = csvParser.ReadFields(); 141 | if(keepNewlines == true) 142 | splitLine = fields[textIndex].Split(" "); 143 | else 144 | splitLine = fields[textIndex].Replace("\\n", " ").Split(" "); 145 | wordCount = 0; 146 | temp = ""; 147 | foreach (String word in splitLine) 148 | { 149 | if (temp.Length != 0) 150 | { 151 | if (word.Length + 1 + wordCount > wrapCount) 152 | { 153 | temp += "\\n"; 154 | wordCount = 0; 155 | } 156 | else 157 | { 158 | temp += " "; 159 | wordCount++; 160 | } 161 | } 162 | temp += word; 163 | wordCount += word.Length; 164 | } 165 | fields[textIndex] = temp; 166 | for (int i = 0; i < fields.Length; i++) 167 | { 168 | if (fields[i].Contains(",")) 169 | fields[i] = "\"" + fields[i] + "\""; 170 | } 171 | sw.Write(String.Join(",", fields)); 172 | sw.Write("\r\n"); 173 | } 174 | } 175 | } 176 | } 177 | 178 | internal static void ExtractFile(String input, String output, bool verbose, bool overwrite) 179 | { 180 | String baseName = Path.GetFileNameWithoutExtension(input); 181 | String finalOutput = output + "/" + baseName + ".csv"; 182 | String blowfishKey = "TLibDefKey"; 183 | int bcsHeaderSize = 24; 184 | 185 | if (baseName == "_emote" || baseName == "emote") 186 | { 187 | Console.WriteLine("{0} file detected. This bcs file currently can't be extracted and will be left as is", baseName); 188 | finalOutput = output + "/" + baseName + ".bcs"; 189 | 190 | if (overwrite == false && File.Exists(finalOutput)) 191 | { 192 | Console.WriteLine("Skipping {0} because it already exists", finalOutput); 193 | return; 194 | } 195 | 196 | File.Copy(input, finalOutput, true); 197 | return; 198 | } 199 | 200 | if (overwrite == false && File.Exists(finalOutput)) 201 | { 202 | Console.WriteLine("Skipping {0} because it already exists", finalOutput); 203 | return; 204 | } 205 | 206 | byte[] resultBuffer = ReadBCSHeader(input); 207 | bool isTSV; 208 | if ((char)resultBuffer[0] == 'T' && 209 | (char)resultBuffer[1] == 'S' && 210 | (char)resultBuffer[2] == 'V') 211 | { 212 | isTSV = true; 213 | } 214 | else if ((char)resultBuffer[0] == 'B' && 215 | (char)resultBuffer[1] == 'C' && 216 | (char)resultBuffer[2] == 'S') 217 | { 218 | isTSV = false; 219 | } 220 | else 221 | { 222 | Console.WriteLine("Skipping {0} because it is not a .bcs file", input); 223 | return; 224 | } 225 | 226 | uint unpackedSize = BitConverter.ToUInt32(resultBuffer, 4); 227 | uint objectCount = BitConverter.ToUInt32(resultBuffer, 8); 228 | uint objectMark = BitConverter.ToUInt32(resultBuffer, 12); 229 | uint objectPartsCount = BitConverter.ToUInt32(resultBuffer, 16); 230 | uint bodySize = BitConverter.ToUInt32(resultBuffer, 20); 231 | 232 | using (FileStream fs = File.OpenRead(input)) 233 | { 234 | uint firstTableSize = objectCount * 8; // Each object is 8 bytes 235 | uint secondTableSize = objectPartsCount * 8; // Index table 236 | uint tnkSize = unpackedSize - (firstTableSize + secondTableSize); // TNK/GMS portion is after the two tables 237 | byte[] unpacked = new byte[unpackedSize]; 238 | byte[] indexTable = new byte[secondTableSize]; 239 | byte[] tnkTable = new byte[tnkSize]; 240 | 241 | using (var stream = new ProxyStream(fs)) 242 | { 243 | LzssUnpack(stream, bcsHeaderSize, unpacked); 244 | } 245 | 246 | // Only use of the first table is it tells how many columns there are 247 | int numCols = (int)BitConverter.ToUInt32(unpacked, 0); 248 | 249 | uint unpackedIndex = firstTableSize; // Start at index table 250 | for (int itIndex = 0; itIndex < secondTableSize; itIndex++) 251 | { 252 | indexTable[itIndex] = unpacked[unpackedIndex]; 253 | unpackedIndex++; 254 | } 255 | 256 | for (int tnkIndex = 0; tnkIndex < tnkSize; tnkIndex++) 257 | { 258 | tnkTable[tnkIndex] = unpacked[unpackedIndex]; 259 | unpackedIndex++; 260 | } 261 | 262 | byte[] tnkBody; 263 | // If TSV, then decrypt with blowfish, this is the format for new Tanuki/Kaeru Soft games. 264 | // Else it's GMS, then unpack with lzss, this is the format for old Tanuki Soft/Rune games. 265 | if (isTSV) 266 | { 267 | // TNK has a 12 byte header 268 | tnkBody = new List(tnkTable).GetRange(12, tnkTable.Length - 12).ToArray(); 269 | var blowfish = new Blowfish(Encoding.ASCII.GetBytes(blowfishKey)); 270 | blowfish.Decipher(tnkBody, tnkBody.Length & ~7); 271 | } 272 | else 273 | { 274 | // This GMS format is unknown, but it seems that the lzss unpacked size is here 275 | uint unpackedGmsSize = BitConverter.ToUInt32(tnkTable, 10); 276 | tnkBody = new byte[unpackedGmsSize]; 277 | 278 | MemoryStream stream1 = new MemoryStream(); 279 | stream1.Write(tnkTable, 0, tnkTable.Length); 280 | 281 | LzssUnpack(stream1, 16, tnkBody, true); 282 | } 283 | 284 | int indexCurr = 0; 285 | List finalBytes = new List(); 286 | String finalLine = ""; 287 | String line; 288 | while (indexCurr <= indexTable.Length) 289 | { 290 | line = BuildScriptLine(indexTable, tnkBody, indexCurr, numCols) + "\r\n"; 291 | finalLine += line; 292 | indexCurr += (numCols * 8); // Each col is 8 bytes 293 | } 294 | 295 | // Write using shift-jis encoding 296 | using (StreamWriter sw = new StreamWriter(File.Open(finalOutput, FileMode.Create), Encoding.GetEncoding(932))) 297 | sw.Write(finalLine); 298 | 299 | Console.WriteLine("Finished extracting from {0}", input); 300 | } 301 | } 302 | 303 | internal static byte[] ReadBCSHeader(String path) 304 | { 305 | byte[] tempBuffer = new byte[0x100]; 306 | using (FileStream fs = File.OpenRead(path)) 307 | fs.Read(tempBuffer, 0, 24); 308 | 309 | return tempBuffer; 310 | } 311 | 312 | internal static String BuildScriptLine(byte[] script, byte[] tnkTable, int index, int numCols) 313 | { 314 | uint operand; 315 | uint value; 316 | String work; 317 | List part; 318 | int indexOffset = 0; 319 | String returnString = ""; 320 | byte mask = 0b11; 321 | 322 | for (int curr = 0; curr < numCols && (index + indexOffset) < script.Length; curr++) 323 | { 324 | operand = BitConverter.ToUInt32(script, index + indexOffset) & mask; 325 | 326 | indexOffset += 4; 327 | if (operand == 0x01) // Plain integer value 328 | { 329 | value = BitConverter.ToUInt32(script, index + indexOffset); 330 | returnString += value; 331 | } 332 | else if (operand == 0x03) // Offset into the TNK table of strings 333 | { 334 | value = BitConverter.ToUInt32(script, index + indexOffset); 335 | part = ReadUntil00(tnkTable, value); 336 | work = Encoding.GetEncoding(932).GetString(part.ToArray()); 337 | if (work.Contains(",")) 338 | work = "\"" + work + "\""; 339 | returnString += work; 340 | } 341 | else if (operand == 0x00) 342 | returnString = returnString; 343 | 344 | indexOffset += 4; 345 | if (curr < numCols - 1 && (index + indexOffset) < script.Length) 346 | returnString += ","; 347 | } 348 | 349 | return returnString; 350 | } 351 | 352 | internal static List ReadUntil00(byte[] toRead, uint start) 353 | { 354 | List ret = new List(); 355 | int index = (int)start; 356 | byte curr = toRead[index]; 357 | 358 | while (curr != 0x00) 359 | { 360 | // If there's a quote, add double quotes for csv escaping 361 | if (curr == 0x22) 362 | ret.Add(curr); 363 | ret.Add(curr); 364 | index++; 365 | if (index >= toRead.Length) 366 | break; 367 | curr = toRead[index]; 368 | } 369 | 370 | return ret; 371 | } 372 | 373 | // This is basically LzssUnpack taken from GARBro, all credit to its creator morkt 374 | internal static void LzssUnpack(Stream input, int skip, byte[] output, bool invert = false) 375 | { 376 | const int frameMask = 0xFFF; 377 | byte[] frame = new byte[0x1000]; 378 | int framePos = 0xFEE; 379 | int dst = 0; 380 | int ctl = 2; 381 | input.Seek(skip, 0); 382 | while (dst < output.Length) 383 | { 384 | ctl >>= 1; 385 | if (1 == ctl) 386 | { 387 | ctl = input.ReadByte(); 388 | if (-1 == ctl) 389 | break; 390 | ctl |= 0x100; 391 | } 392 | 393 | // New character 394 | if (0 != (ctl & 1)) 395 | { 396 | int b = input.ReadByte(); 397 | if (-1 == b) 398 | break; 399 | frame[framePos++ & frameMask] = (byte)b; 400 | if (invert) 401 | output[dst++] = (byte)(~(b)); 402 | else 403 | output[dst++] = (byte)b; 404 | } 405 | // Already seen 406 | else 407 | { 408 | int lo = input.ReadByte(); 409 | if (-1 == lo) 410 | break; 411 | int hi = input.ReadByte(); 412 | if (-1 == hi) 413 | break; 414 | int offset = (hi & 0xf0) << 4 | lo; 415 | int count = Math.Min((~hi & 0xF) + 3, output.Length - dst); 416 | while (count-- > 0) 417 | { 418 | byte v = frame[offset++ & frameMask]; 419 | frame[framePos++ & frameMask] = v; 420 | if (invert) 421 | output[dst++] = (byte)(~(v)); 422 | else 423 | output[dst++] = v; 424 | } 425 | } 426 | } 427 | } 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /Blowfish.cs: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | | 3 | | Copyright (c) 2007 Novell, Inc. 4 | | All Rights Reserved. 5 | | 6 | | This program is free software; you can redistribute it and/or 7 | | modify it under the terms of version 2 of the GNU General Public License as 8 | | published by the Free Software Foundation. 9 | | 10 | | This program is distributed in the hope that it will be useful, 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | | GNU General Public License for more details. 14 | | 15 | | You should have received a copy of the GNU General Public License 16 | | along with this program; if not, contact Novell, Inc. 17 | | 18 | | To contact Novell about this file by physical or electronic mail, 19 | | you may find current contact information at www.novell.com 20 | | 21 | | Author: Russ Young 22 | | Thanks to: Bruce Schneier / Counterpane Labs 23 | | for the Blowfish encryption algorithm and 24 | | reference implementation. http://www.schneier.com/blowfish.html 25 | |***************************************************************************/ 26 | 27 | using System; 28 | using System.Collections.Generic; 29 | using System.IO; 30 | using System.Security.Cryptography; 31 | 32 | namespace BcsExtractor 33 | { 34 | /// 35 | /// Class that provides blowfish encryption. 36 | /// 37 | public class Blowfish 38 | { 39 | const int N = 16; 40 | const int KEYBYTES = 8; 41 | 42 | static uint[] _P = 43 | { 44 | 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 45 | 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 46 | 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b 47 | }; 48 | static uint[,] _S = 49 | { 50 | { 51 | 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 52 | 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 53 | 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 54 | 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 55 | 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 56 | 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 57 | 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, 58 | 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 59 | 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 60 | 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 61 | 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 62 | 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 63 | 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 64 | 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 65 | 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, 66 | 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 67 | 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 68 | 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 69 | 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 70 | 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 71 | 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 72 | 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 73 | 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 74 | 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 75 | 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 76 | 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 77 | 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 78 | 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 79 | 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 80 | 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 81 | 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 82 | 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 83 | 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 84 | 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 85 | 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, 86 | 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 87 | 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 88 | 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 89 | 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 90 | 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 91 | 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 92 | 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 93 | 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a 94 | }, 95 | { 96 | 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 97 | 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 98 | 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 99 | 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 100 | 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 101 | 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 102 | 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 103 | 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 104 | 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 105 | 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 106 | 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, 107 | 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 108 | 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 109 | 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 110 | 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 111 | 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 112 | 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 113 | 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 114 | 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, 115 | 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 116 | 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 117 | 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 118 | 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 119 | 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 120 | 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 121 | 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 122 | 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 123 | 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 124 | 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 125 | 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 126 | 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, 127 | 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 128 | 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 129 | 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 130 | 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 131 | 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 132 | 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 133 | 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 134 | 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, 135 | 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 136 | 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 137 | 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 138 | 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 139 | }, 140 | { 141 | 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 142 | 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 143 | 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 144 | 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 145 | 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 146 | 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 147 | 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 148 | 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 149 | 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 150 | 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 151 | 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 152 | 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 153 | 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 154 | 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 155 | 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 156 | 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 157 | 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 158 | 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 159 | 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 160 | 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 161 | 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 162 | 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 163 | 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 164 | 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 165 | 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 166 | 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 167 | 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 168 | 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 169 | 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 170 | 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 171 | 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 172 | 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 173 | 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 174 | 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 175 | 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 176 | 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 177 | 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 178 | 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 179 | 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 180 | 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 181 | 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 182 | 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 183 | 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 184 | }, 185 | { 186 | 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 187 | 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 188 | 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 189 | 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 190 | 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 191 | 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 192 | 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, 193 | 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 194 | 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 195 | 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 196 | 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 197 | 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 198 | 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 199 | 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 200 | 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 201 | 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 202 | 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 203 | 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 204 | 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, 205 | 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 206 | 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 207 | 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 208 | 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 209 | 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 210 | 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 211 | 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 212 | 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, 213 | 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 214 | 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 215 | 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 216 | 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 217 | 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 218 | 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 219 | 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 220 | 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, 221 | 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 222 | 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 223 | 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 224 | 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 225 | 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 226 | 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 227 | 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 228 | 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 229 | } 230 | }; 231 | 232 | uint[] P; 233 | uint[,] S; 234 | 235 | /// 236 | /// Constructs and initializes a blowfish instance with the supplied key. 237 | /// 238 | /// The key to cipher with. 239 | public Blowfish(byte[] key) 240 | { 241 | short i; 242 | short j; 243 | short k; 244 | uint data; 245 | uint datal; 246 | uint datar; 247 | 248 | P = _P.Clone() as uint[]; 249 | S = _S.Clone() as uint[,]; 250 | 251 | j = 0; 252 | for (i = 0; i < N + 2; ++i) 253 | { 254 | data = 0x00000000; 255 | for (k = 0; k < 4; ++k) 256 | { 257 | data = (data << 8) | key[j]; 258 | j++; 259 | if (j >= key.Length) 260 | { 261 | j = 0; 262 | } 263 | } 264 | P[i] = P[i] ^ data; 265 | } 266 | 267 | datal = 0x00000000; 268 | datar = 0x00000000; 269 | 270 | for (i = 0; i < N + 2; i += 2) 271 | { 272 | Encipher(ref datal, ref datar); 273 | P[i] = datal; 274 | P[i + 1] = datar; 275 | } 276 | 277 | for (i = 0; i < 4; ++i) 278 | { 279 | for (j = 0; j < 256; j += 2) 280 | { 281 | Encipher(ref datal, ref datar); 282 | 283 | S[i, j] = datal; 284 | S[i, j + 1] = datar; 285 | } 286 | } 287 | } 288 | 289 | public ICryptoTransform CreateDecryptor() 290 | { 291 | return new BlowfishDecryptor(this); 292 | } 293 | 294 | private uint F(uint x) 295 | { 296 | ushort a; 297 | ushort b; 298 | ushort c; 299 | ushort d; 300 | uint y; 301 | 302 | d = (ushort)(x & 0x00FF); 303 | x >>= 8; 304 | c = (ushort)(x & 0x00FF); 305 | x >>= 8; 306 | b = (ushort)(x & 0x00FF); 307 | x >>= 8; 308 | a = (ushort)(x & 0x00FF); 309 | //y = ((S[0][a] + S[1][b]) ^ S[2][c]) + S[3][d]; 310 | y = S[0, a] + S[1, b]; 311 | y = y ^ S[2, c]; 312 | y = y + S[3, d]; 313 | 314 | return y; 315 | } 316 | 317 | /// 318 | /// Encrypts a byte array in place. 319 | /// 320 | /// The array to encrypt. 321 | /// The amount to encrypt. 322 | public void Encipher(byte[] data, int length) 323 | { 324 | uint xl, xr; 325 | if ((length % 8) != 0) 326 | throw new Exception("Invalid Length"); 327 | for (int i = 0; i < length; i += 8) 328 | { 329 | // Encode the data in 8 byte blocks. 330 | xl = (uint)((data[i] << 24) | (data[i + 1] << 16) | (data[i + 2] << 8) | data[i + 3]); 331 | xr = (uint)((data[i + 4] << 24) | (data[i + 5] << 16) | (data[i + 6] << 8) | data[i + 7]); 332 | Encipher(ref xl, ref xr); 333 | // Now Replace the data. 334 | data[i] = (byte)(xl >> 24); 335 | data[i + 1] = (byte)(xl >> 16); 336 | data[i + 2] = (byte)(xl >> 8); 337 | data[i + 3] = (byte)(xl); 338 | data[i + 4] = (byte)(xr >> 24); 339 | data[i + 5] = (byte)(xr >> 16); 340 | data[i + 6] = (byte)(xr >> 8); 341 | data[i + 7] = (byte)(xr); 342 | } 343 | } 344 | 345 | /// 346 | /// Encrypts 8 bytes of data (1 block) 347 | /// 348 | /// The left part of the 8 bytes. 349 | /// The right part of the 8 bytes. 350 | private void Encipher(ref uint xl, ref uint xr) 351 | { 352 | uint Xl; 353 | uint Xr; 354 | uint temp; 355 | short i; 356 | 357 | Xl = xl; 358 | Xr = xr; 359 | 360 | for (i = 0; i < N; ++i) 361 | { 362 | Xl = Xl ^ P[i]; 363 | Xr = F(Xl) ^ Xr; 364 | 365 | temp = Xl; 366 | Xl = Xr; 367 | Xr = temp; 368 | } 369 | 370 | temp = Xl; 371 | Xl = Xr; 372 | Xr = temp; 373 | 374 | Xr = Xr ^ P[N]; 375 | Xl = Xl ^ P[N + 1]; 376 | 377 | xl = Xl; 378 | xr = Xr; 379 | } 380 | 381 | /// 382 | /// Decrypts a byte array in place. 383 | /// 384 | /// The array to decrypt. 385 | /// The amount to decrypt. 386 | public void Decipher(byte[] data, int length) 387 | { 388 | uint xl, xr; 389 | if ((length % 8) != 0) 390 | throw new Exception("Invalid Length"); 391 | for (int i = 0; i < length; i += 8) 392 | { 393 | // Encode the data in 8 byte blocks. 394 | xl = (uint)(data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24)); 395 | xr = (uint)(data[i + 4] | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24)); 396 | Decipher(ref xl, ref xr); 397 | // Now Replace the data. 398 | data[i] = (byte)(xl); 399 | data[i + 1] = (byte)(xl >> 8); 400 | data[i + 2] = (byte)(xl >> 16); 401 | data[i + 3] = (byte)(xl >> 24); 402 | data[i + 4] = (byte)(xr); 403 | data[i + 5] = (byte)(xr >> 8); 404 | data[i + 6] = (byte)(xr >> 16); 405 | data[i + 7] = (byte)(xr >> 24); 406 | } 407 | } 408 | 409 | /// 410 | /// Decrypts 8 bytes of data (1 block) 411 | /// 412 | /// The left part of the 8 bytes. 413 | /// The right part of the 8 bytes. 414 | public void Decipher(ref uint xl, ref uint xr) 415 | { 416 | uint Xl; 417 | uint Xr; 418 | uint temp; 419 | short i; 420 | 421 | Xl = xl; 422 | Xr = xr; 423 | 424 | for (i = N + 1; i > 1; --i) 425 | { 426 | Xl = Xl ^ P[i]; 427 | Xr = F(Xl) ^ Xr; 428 | 429 | /* Exchange Xl and Xr */ 430 | temp = Xl; 431 | Xl = Xr; 432 | Xr = temp; 433 | } 434 | 435 | /* Exchange Xl and Xr */ 436 | temp = Xl; 437 | Xl = Xr; 438 | Xr = temp; 439 | 440 | Xr = Xr ^ P[1]; 441 | Xl = Xl ^ P[0]; 442 | 443 | xl = Xl; 444 | xr = Xr; 445 | } 446 | } 447 | 448 | /// 449 | /// ICryptoTransform implementation for use with CryptoStream. 450 | /// 451 | public sealed class BlowfishDecryptor : ICryptoTransform 452 | { 453 | Blowfish m_bf; 454 | 455 | public const int BlockSize = 8; 456 | 457 | public bool CanTransformMultipleBlocks { get { return true; } } 458 | public bool CanReuseTransform { get { return true; } } 459 | public int InputBlockSize { get { return BlockSize; } } 460 | public int OutputBlockSize { get { return BlockSize; } } 461 | 462 | public BlowfishDecryptor(Blowfish bf) 463 | { 464 | m_bf = bf; 465 | } 466 | 467 | public static uint ToUInt32(TArray value, int index) where TArray : IList 468 | { 469 | return (uint)(value[index] | value[index + 1] << 8 | value[index + 2] << 16 | value[index + 3] << 24); 470 | } 471 | 472 | public static void Pack(uint value, byte[] buf, int index) 473 | { 474 | buf[index] = (byte)(value); 475 | buf[index + 1] = (byte)(value >> 8); 476 | buf[index + 2] = (byte)(value >> 16); 477 | buf[index + 3] = (byte)(value >> 24); 478 | } 479 | 480 | public int TransformBlock(byte[] inBuffer, int offset, int count, byte[] outBuffer, int outOffset) 481 | { 482 | for (int i = 0; i < count; i += BlockSize) 483 | { 484 | uint xl = ToUInt32(inBuffer, offset + i); 485 | uint xr = ToUInt32(inBuffer, offset + i + 4); 486 | m_bf.Decipher(ref xl, ref xr); 487 | Pack(xl, outBuffer, outOffset + i); 488 | Pack(xr, outBuffer, outOffset + i + 4); 489 | } 490 | return count; 491 | } 492 | 493 | static readonly byte[] EmptyArray = new byte[0]; 494 | 495 | public byte[] TransformFinalBlock(byte[] inBuffer, int offset, int count) 496 | { 497 | if (0 == count) 498 | return EmptyArray; 499 | if (0 != count % BlockSize) 500 | throw new InvalidDataException("Non-padded block in Blowfish encrypted stream"); 501 | var output = new byte[count]; 502 | TransformBlock(inBuffer, offset, count, output, 0); 503 | return output; 504 | } 505 | 506 | #region IDisposable implementation 507 | bool _disposed = false; 508 | public void Dispose() 509 | { 510 | if (!_disposed) 511 | { 512 | _disposed = true; 513 | } 514 | GC.SuppressFinalize(this); 515 | } 516 | #endregion 517 | } 518 | } --------------------------------------------------------------------------------