├── 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 | }
--------------------------------------------------------------------------------