comic_walker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +70 -0
- data/Rakefile +2 -0
- data/bin/comic-walker +4 -0
- data/comic_walker.gemspec +28 -0
- data/lib/comic_walker.rb +5 -0
- data/lib/comic_walker/cipher.rb +63 -0
- data/lib/comic_walker/cli.rb +54 -0
- data/lib/comic_walker/client.rb +52 -0
- data/lib/comic_walker/client/license.rb +46 -0
- data/lib/comic_walker/end_layer_decoder.rb +14 -0
- data/lib/comic_walker/item_decoder.rb +43 -0
- data/lib/comic_walker/item_decoder/unknown.rb +183 -0
- data/lib/comic_walker/version.rb +5 -0
- metadata +160 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e501759d14c46245526e2b8cbff03a38b9f390f9
|
4
|
+
data.tar.gz: a1eccbbfd4a9f823614adc8c1adf8b6a98bebc5b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1fe26f0cc27ad11cda330ff55e502e27b4a09a71a9f1c264026042765a0a311e68cec060b1e555fb9a5e58ca44319c8baa75e6e863e81fddf0adcfb37d0f6eed
|
7
|
+
data.tar.gz: 2d2393961e1859b017c960e29deed845913df53c98472382785418ccd9b51cf04675e17570f2c95d439efec7f1af9d6557651b0d0417e4a7220daae1660c24df
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Kohei Suzuki
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# ComicWalker
|
2
|
+
|
3
|
+
Client library for [ComicWalker](http://comic-walker.com/).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'comic_walker'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install comic_walker
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### CLI
|
22
|
+
|
23
|
+
```
|
24
|
+
% comic-walker KDCW_KS04000001010001_68
|
25
|
+
item/xhtml/p-cover.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/000_p-cover.jpg
|
26
|
+
item/xhtml/p-001.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/001_p-001.jpg
|
27
|
+
item/xhtml/p-002.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/002_p-002.jpg
|
28
|
+
item/xhtml/p-white.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/003_p-white.jpg
|
29
|
+
item/xhtml/p-004.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/004_p-004.jpg
|
30
|
+
item/xhtml/p-005.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/005_p-005.jpg
|
31
|
+
item/xhtml/p-006.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/006_p-006.jpg
|
32
|
+
item/xhtml/p-007.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/007_p-007.jpg
|
33
|
+
item/xhtml/p-008.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/008_p-008.jpg
|
34
|
+
item/xhtml/p-009.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/009_p-009.jpg
|
35
|
+
item/xhtml/p-010.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/010_p-010.jpg
|
36
|
+
item/xhtml/p-011.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/011_p-011.jpg
|
37
|
+
item/xhtml/p-012.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/012_p-012.jpg
|
38
|
+
item/xhtml/p-013.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/013_p-013.jpg
|
39
|
+
item/xhtml/p-014.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/014_p-014.jpg
|
40
|
+
item/xhtml/p-015.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/015_p-015.jpg
|
41
|
+
item/xhtml/p-016.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/016_p-016.jpg
|
42
|
+
item/xhtml/p-017.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/017_p-017.jpg
|
43
|
+
item/xhtml/p-018.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/018_p-018.jpg
|
44
|
+
item/xhtml/p-019.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/019_p-019.jpg
|
45
|
+
item/xhtml/p-020.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/020_p-020.jpg
|
46
|
+
item/xhtml/p-021.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/021_p-021.jpg
|
47
|
+
item/xhtml/p-022.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/022_p-022.jpg
|
48
|
+
item/xhtml/p-023.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/023_p-023.jpg
|
49
|
+
item/xhtml/p-024.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/024_p-024.jpg
|
50
|
+
item/xhtml/p-025.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/025_p-025.jpg
|
51
|
+
item/xhtml/p-026.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/026_p-026.jpg
|
52
|
+
item/xhtml/p-027.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/027_p-027.jpg
|
53
|
+
item/xhtml/p-028.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/028_p-028.jpg
|
54
|
+
item/xhtml/p-029.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/029_p-029.jpg
|
55
|
+
item/xhtml/p-030.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/030_p-030.jpg
|
56
|
+
item/xhtml/p-031.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/031_p-031.jpg
|
57
|
+
item/xhtml/p-032.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/032_p-032.jpg
|
58
|
+
item/xhtml/p-033.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/033_p-033.jpg
|
59
|
+
item/xhtml/p-034.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/034_p-034.jpg
|
60
|
+
item/xhtml/p-035.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/035_p-035.jpg
|
61
|
+
item/xhtml/p-036.xhtml/0.dat -> Fate/kaleid liner プリズマ☆イリヤ ドライ!!/KDCW_KS04000001010001_68/036_p-036.jpg
|
62
|
+
```
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it ( https://github.com/eagletmt/comic_walker/fork )
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/comic-walker
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'comic_walker/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "comic_walker"
|
8
|
+
spec.version = ComicWalker::VERSION
|
9
|
+
spec.authors = ["Kohei Suzuki"]
|
10
|
+
spec.email = ["eagletmt@gmail.com"]
|
11
|
+
spec.summary = %q{Client library for ComicWalker}
|
12
|
+
spec.description = %q{Client library for ComicWalker}
|
13
|
+
spec.homepage = "https://github.com/eagletmt/comic_walker"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "yard"
|
24
|
+
spec.add_dependency "addressable"
|
25
|
+
spec.add_dependency "http-cookie"
|
26
|
+
spec.add_dependency "rmagick"
|
27
|
+
spec.add_dependency "rubyzip"
|
28
|
+
end
|
data/lib/comic_walker.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'digest'
|
3
|
+
|
4
|
+
module ComicWalker
|
5
|
+
module Cipher
|
6
|
+
module_function
|
7
|
+
|
8
|
+
BASE_KEY = [173, 43, 117, 127, 230, 58, 73, 84, 154, 177, 47, 81, 108, 200, 101, 65].pack('C*')
|
9
|
+
|
10
|
+
# Decrypt Base64-encoded license object
|
11
|
+
# @param [String] bid browser id
|
12
|
+
# @param [String] u1 got from cookie
|
13
|
+
# @param [String] license_b64 Base64-encoded license object
|
14
|
+
# @return [String]
|
15
|
+
def decrypt_license(bid, u1, license_b64)
|
16
|
+
h = bid.chars.map(&:ord)
|
17
|
+
if u1
|
18
|
+
h += u1.chars.map(&:ord)
|
19
|
+
end
|
20
|
+
decrypt_b64(h.pack('C*') + BASE_KEY, license_b64)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Decrypt Base64-encoded data
|
24
|
+
# @param [String] key key
|
25
|
+
# @param [String] b64data Encrypted Base64-encoded data
|
26
|
+
# @return [String] Decrypted data
|
27
|
+
def decrypt_b64(key, b64data)
|
28
|
+
data = Base64.decode64(b64data)
|
29
|
+
md5 = Digest::MD5.hexdigest(key + data.byteslice(8, 8))
|
30
|
+
l = md5.scan(/.{2}/).map { |xy| Integer(xy, 16) }
|
31
|
+
decrypt_rc4(l, data[16 .. -1].unpack('C*')).pack('C*')
|
32
|
+
end
|
33
|
+
|
34
|
+
# Decrypt data
|
35
|
+
# @param [Array<Fixnum>] key
|
36
|
+
# @param [Array<Fixnum>] data
|
37
|
+
# @return [Array<Fixnum>] Decrypt data
|
38
|
+
def decrypt_rc4(key, data)
|
39
|
+
s = gen_rc4_table(key)
|
40
|
+
i = 0
|
41
|
+
j = 0
|
42
|
+
data.map do |x|
|
43
|
+
i = (i + 1) & 0xff
|
44
|
+
j = (j + s[i]) & 0xff
|
45
|
+
s[i], s[j] = s[j], s[i]
|
46
|
+
k = s[(s[i] + s[j]) & 0xff]
|
47
|
+
x ^ k
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param [Array<Fixnum>] key
|
52
|
+
# @return [Array<Fixnum>] RC4 table
|
53
|
+
def gen_rc4_table(key)
|
54
|
+
s = 256.times.to_a
|
55
|
+
j = 0
|
56
|
+
256.times do |i|
|
57
|
+
j = (j + s[i] + key[i % key.size]) & 0xff
|
58
|
+
s[i], s[j] = s[j], s[i]
|
59
|
+
end
|
60
|
+
s
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'pathname'
|
3
|
+
require 'pp'
|
4
|
+
require 'comic_walker/client'
|
5
|
+
require 'comic_walker/end_layer_decoder'
|
6
|
+
require 'comic_walker/item_decoder'
|
7
|
+
|
8
|
+
module ComicWalker
|
9
|
+
class CLI
|
10
|
+
def run(cids)
|
11
|
+
client = Client.new
|
12
|
+
cids.each do |cid|
|
13
|
+
save_content(client, cid)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def save_content(client, cid)
|
18
|
+
license = client.get_license(cid)
|
19
|
+
info = license.get_info.first
|
20
|
+
last_update = Time.strptime(info['last_update'], '%Y%m%d%H%M')
|
21
|
+
title = info['issues'].first['content_name']
|
22
|
+
|
23
|
+
license.with_jar do |zip|
|
24
|
+
pages = []
|
25
|
+
items = {}
|
26
|
+
decoder = nil
|
27
|
+
while entry = zip.get_next_entry
|
28
|
+
case entry.name
|
29
|
+
when 'configuration_pack.json'
|
30
|
+
config = JSON.parse(zip.read)
|
31
|
+
decoder = ItemDecoder.new(config)
|
32
|
+
config['configuration']['contents'].each do |content|
|
33
|
+
pages[content['index']-1] = content['file']
|
34
|
+
end
|
35
|
+
when /end_layer\.json/
|
36
|
+
pp EndLayerDecoder.new(license.key).decode(zip.read)
|
37
|
+
when %r{\Aitem/}
|
38
|
+
items[entry.name] = zip.read
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
img_dir = Pathname.new(title).join(cid).tap(&:mkpath)
|
43
|
+
pages.each.with_index do |file, i|
|
44
|
+
dat_path = Pathname.new(file).join('0.dat')
|
45
|
+
data = items[dat_path.to_s]
|
46
|
+
img_fname = dat_path.parent.basename.sub_ext('.jpg')
|
47
|
+
img_path = img_dir.join(sprintf('%03d_%s', i, img_fname))
|
48
|
+
puts "#{dat_path} -> #{img_path}"
|
49
|
+
decoder.decode(dat_path, img_path, data)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'http-cookie'
|
3
|
+
require 'json'
|
4
|
+
require 'net/http'
|
5
|
+
require 'comic_walker/cipher'
|
6
|
+
require 'comic_walker/client/license'
|
7
|
+
|
8
|
+
module ComicWalker
|
9
|
+
class Client
|
10
|
+
def initialize
|
11
|
+
@bid = '000000000000000000000NFBR'
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_license(cid)
|
15
|
+
u1 = get_u1(cid)
|
16
|
+
json = get_li(cid, u1)
|
17
|
+
license_b64 = json['license']
|
18
|
+
License.new(JSON.parse(Cipher.decrypt_license(@bid, u1, license_b64)))
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_u1(cid)
|
22
|
+
uri = Addressable::URI.parse("http://comic-walker.com/viewer/?cid=#{cid}")
|
23
|
+
Net::HTTP.start(uri.host, 80) do |http|
|
24
|
+
res = http.get(uri.request_uri)
|
25
|
+
Addressable::URI.unescape(HTTP::Cookie.cookie_value_to_hash(res['set-cookie'])['u1'])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_li(cid, u1)
|
30
|
+
uri = Addressable::URI.parse("https://cnts.comic-walker.com/api4js/v1/c/#{cid}/li")
|
31
|
+
uri.query_values = {
|
32
|
+
'BID' => @bid,
|
33
|
+
'AID' => 'browser',
|
34
|
+
'AVER' => '1.2.0',
|
35
|
+
'S' => u1,
|
36
|
+
'FORMATS' => 'epub_brws,epub_brws_fixedlayout,epub_brws_omf',
|
37
|
+
'W' => 720,
|
38
|
+
'H' => 1280,
|
39
|
+
}
|
40
|
+
|
41
|
+
https = Net::HTTP.new(uri.host, 443)
|
42
|
+
https.use_ssl = true
|
43
|
+
https.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
44
|
+
body = https.start do
|
45
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
46
|
+
res = https.request(req)
|
47
|
+
res.body
|
48
|
+
end
|
49
|
+
JSON.parse(body)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'base64'
|
3
|
+
require 'net/http'
|
4
|
+
require 'zip'
|
5
|
+
|
6
|
+
module ComicWalker
|
7
|
+
class Client
|
8
|
+
class License
|
9
|
+
def initialize(json)
|
10
|
+
@json = json
|
11
|
+
end
|
12
|
+
|
13
|
+
def agreement
|
14
|
+
@json['agreement']
|
15
|
+
end
|
16
|
+
|
17
|
+
def jar_url
|
18
|
+
agreement['url_prefix'] + agreement['jar_file_name']
|
19
|
+
end
|
20
|
+
|
21
|
+
def info_url
|
22
|
+
agreement['url_prefix'] + agreement['info_file_name']
|
23
|
+
end
|
24
|
+
|
25
|
+
def key
|
26
|
+
Base64.decode64(agreement['key'])
|
27
|
+
end
|
28
|
+
|
29
|
+
def with_jar(&block)
|
30
|
+
uri = Addressable::URI.parse(jar_url)
|
31
|
+
body = Net::HTTP.start(uri.host) do |http|
|
32
|
+
http.get(uri.request_uri).body
|
33
|
+
end
|
34
|
+
Zip::InputStream.open(StringIO.new(body), &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get_info
|
38
|
+
uri = Addressable::URI.parse(info_url)
|
39
|
+
body = Net::HTTP.start(uri.host) do |http|
|
40
|
+
http.get(uri.request_uri).body
|
41
|
+
end
|
42
|
+
JSON.parse(body)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
require 'comic_walker/item_decoder/unknown'
|
3
|
+
|
4
|
+
module ComicWalker
|
5
|
+
class ItemDecoder
|
6
|
+
def initialize(configuration_pack)
|
7
|
+
fname = '0.dat'
|
8
|
+
ct = configuration_pack['ct']
|
9
|
+
st = configuration_pack['st']
|
10
|
+
et = configuration_pack['et']
|
11
|
+
@key1 = (ct + st + fname).unpack('C*')
|
12
|
+
@key2 = (ct + fname + et).unpack('C*')
|
13
|
+
@key3 = (fname + st + et).unpack('C*')
|
14
|
+
end
|
15
|
+
|
16
|
+
def decode(dat_path, img_path, b64data)
|
17
|
+
bs = 128
|
18
|
+
hs = 1024
|
19
|
+
blob = Unknown.finish(@key1, hs,
|
20
|
+
Unknown.decrypt(@key2, bs,
|
21
|
+
Unknown.prepare(@key3,
|
22
|
+
Base64.decode64(b64data).unpack('C*')
|
23
|
+
)
|
24
|
+
)
|
25
|
+
).pack('C*')
|
26
|
+
|
27
|
+
src = Magick::Image.from_blob(blob).first
|
28
|
+
width = src.columns
|
29
|
+
height = src.rows
|
30
|
+
t = dat_path.sub_ext('').to_s.chars.inject(0) { |acc, c| acc + c.ord }
|
31
|
+
pat = t%4 + 1
|
32
|
+
moves = Unknown.calculate_moves(width, height, 64, 64, pat)
|
33
|
+
|
34
|
+
dst = Magick::Image.new(width, height)
|
35
|
+
dst.format = 'jpeg'
|
36
|
+
moves.each do |move|
|
37
|
+
crop = src.excerpt(move[:destX], move[:destY], move[:width], move[:height])
|
38
|
+
dst.composite!(crop, move[:srcX], move[:srcY], Magick::SrcOverCompositeOp)
|
39
|
+
end
|
40
|
+
dst.write(img_path.to_s)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
require 'comic_walker/cipher'
|
2
|
+
|
3
|
+
module ComicWalker
|
4
|
+
class ItemDecoder
|
5
|
+
module Unknown
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# @param [Array<Fixnum>] key
|
9
|
+
# @param [Array<Fixnum>] data Encrypted data
|
10
|
+
# @return [Array<Fixnum>]
|
11
|
+
def prepare(key, data)
|
12
|
+
s = Cipher.gen_rc4_table(key)
|
13
|
+
data.map.with_index do |b, i|
|
14
|
+
b ^ s[i % 256]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [Array<Fixnum>] key
|
19
|
+
# @param [Integer] bsize block size?
|
20
|
+
# @param [Array<Fixnum>] data Encrypted data
|
21
|
+
# @return [Array<Fixnum>] Chunked decrypted data
|
22
|
+
def decrypt(key, bsize, data)
|
23
|
+
s = []
|
24
|
+
0.step(data.size-1, bsize) do |i|
|
25
|
+
s << data[i]
|
26
|
+
end
|
27
|
+
|
28
|
+
c = Cipher.decrypt_rc4(key, s)
|
29
|
+
# s.size == c.size
|
30
|
+
|
31
|
+
0.step(data.size-1, bsize) do |i|
|
32
|
+
data[i] = c.shift
|
33
|
+
end
|
34
|
+
data
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param [Array<Fixnum>] key
|
38
|
+
# @param [Integer] hsize header size?
|
39
|
+
# @param [Array<Fixnum>] data Encrypted data
|
40
|
+
# @return [Array<Fixnum>] data
|
41
|
+
def finish(key, hsize, data)
|
42
|
+
hsize = [hsize, data.size].min
|
43
|
+
Cipher.decrypt_rc4(key, data.slice(0, hsize)).each.with_index do |x, i|
|
44
|
+
data[i] = x
|
45
|
+
end
|
46
|
+
data
|
47
|
+
end
|
48
|
+
|
49
|
+
# Calculate moves.
|
50
|
+
# @param [Integer] width Width of the image
|
51
|
+
# @param [Integer] height Height of the image
|
52
|
+
# @param [Integer] rect_width Width of the sub-image
|
53
|
+
# @param [Integer] rect_height Height of the sub-image
|
54
|
+
# @param [Integer] pattern pattern? integer ranging from 1 to 4
|
55
|
+
# @return [Array<Hash>] List of hash that represents a move
|
56
|
+
def calculate_moves(width, height, rect_width, rect_height, pattern)
|
57
|
+
wcnt = width / rect_width
|
58
|
+
hcnt = height / rect_height
|
59
|
+
width %= rect_width
|
60
|
+
height %= rect_height
|
61
|
+
moves = []
|
62
|
+
w_t0 = wcnt - (43*pattern)%wcnt
|
63
|
+
w_t1 =
|
64
|
+
if w_t0 % wcnt == 0
|
65
|
+
(wcnt - 4) % wcnt
|
66
|
+
else
|
67
|
+
w_t0
|
68
|
+
end
|
69
|
+
h_t0 = hcnt - 47 * pattern % hcnt;
|
70
|
+
h_t1 =
|
71
|
+
if h_t0 % hcnt == 0
|
72
|
+
(hcnt - 4) % hcnt
|
73
|
+
else
|
74
|
+
h_t0;
|
75
|
+
end
|
76
|
+
|
77
|
+
if width > 0 && height > 0
|
78
|
+
x = w_t1 * rect_width
|
79
|
+
y = h_t1 * rect_height
|
80
|
+
moves.push(srcX: x, srcY: y, destX: x, destY: y, width: width, height: height)
|
81
|
+
end
|
82
|
+
|
83
|
+
if height > 0
|
84
|
+
wcnt.times do |j|
|
85
|
+
l = calcXCoordinateXRest_(j, wcnt, pattern)
|
86
|
+
k = calcYCoordinateXRest_(l, w_t1, h_t1, hcnt, pattern)
|
87
|
+
destX = calcPositionWithRest_(l, w_t1, width, rect_width)
|
88
|
+
destY = k * rect_height
|
89
|
+
srcX = calcPositionWithRest_(j, w_t1, width, rect_width)
|
90
|
+
srcY = h_t1 * rect_height
|
91
|
+
moves.push(srcX: srcX, srcY: srcY, destX: destX, destY: destY, width: rect_width, height: height)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
if width > 0
|
95
|
+
hcnt.times do |i|
|
96
|
+
k = calcYCoordinateYRest_(i, hcnt, pattern)
|
97
|
+
l = calcXCoordinateYRest_(k, w_t1, h_t1, wcnt, pattern)
|
98
|
+
destX = l * rect_width
|
99
|
+
destY = calcPositionWithRest_(k, h_t1, height, rect_height)
|
100
|
+
srcX = w_t1 * rect_width
|
101
|
+
srcY = calcPositionWithRest_(i, h_t1, height, rect_height)
|
102
|
+
moves.push(srcX: srcX, srcY: srcY, destX: destX, destY: destY, width: width, height: rect_height)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
wcnt.times do |j|
|
106
|
+
hcnt.times do |i|
|
107
|
+
p = (j + 29 * pattern + 31 * i) % wcnt
|
108
|
+
k = (i + 37 * pattern + 41 * p) % hcnt
|
109
|
+
q = p >= calcXCoordinateYRest_(k, w_t1, h_t1, wcnt, pattern) ? width : 0
|
110
|
+
m = k >= calcYCoordinateXRest_(p, w_t1, h_t1, hcnt, pattern) ? height : 0
|
111
|
+
destX = p * rect_width + q
|
112
|
+
destY = k * rect_height + m
|
113
|
+
srcX = j * rect_width + (j >= w_t1 ? width : 0)
|
114
|
+
srcY = i * rect_height + (i >= h_t1 ? height : 0)
|
115
|
+
moves.push(srcX: srcX, srcY: srcY, destX: destX, destY: destY, width: rect_width, height: rect_height)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
moves
|
119
|
+
end
|
120
|
+
|
121
|
+
# @param [Integer] a
|
122
|
+
# @param [Integer] f
|
123
|
+
# @param [Integer] b
|
124
|
+
# @param [Integer] e
|
125
|
+
# @return [Integer]
|
126
|
+
def calcPositionWithRest_(a, f, b, e)
|
127
|
+
a * e + (a >= f ? b : 0)
|
128
|
+
end
|
129
|
+
|
130
|
+
# @param [Integer] a
|
131
|
+
# @param [Integer] f
|
132
|
+
# @param [Integer] b
|
133
|
+
# @return [Integer]
|
134
|
+
def calcXCoordinateXRest_(a, f, b)
|
135
|
+
(a + 61 * b) % f
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param [Integer] a
|
139
|
+
# @param [Integer] f
|
140
|
+
# @param [Integer] b
|
141
|
+
# @param [Integer] e
|
142
|
+
# @param [Integer] d
|
143
|
+
# @return [Integer]
|
144
|
+
def calcYCoordinateXRest_(a, f, b, e, d)
|
145
|
+
c = 1 == d % 2
|
146
|
+
if a < f ? c : !c
|
147
|
+
e = b
|
148
|
+
f = 0
|
149
|
+
else
|
150
|
+
e -= b
|
151
|
+
f = b
|
152
|
+
end
|
153
|
+
(a + 53*d + 59*b) % e + f
|
154
|
+
end
|
155
|
+
|
156
|
+
# @param [Integer] a
|
157
|
+
# @param [Integer] f
|
158
|
+
# @param [Integer] b
|
159
|
+
# @param [Integer] e
|
160
|
+
# @param [Integer] d
|
161
|
+
# @return [Integer]
|
162
|
+
def calcXCoordinateYRest_(a, f, b, e, d)
|
163
|
+
c = 1 == d % 2
|
164
|
+
if a < b ? c : !c
|
165
|
+
e -= f
|
166
|
+
b = f
|
167
|
+
else
|
168
|
+
e = f
|
169
|
+
b = 0
|
170
|
+
end
|
171
|
+
(a + 67*d + f + 71) % e + b
|
172
|
+
end
|
173
|
+
|
174
|
+
# @param [Integer] a
|
175
|
+
# @param [Integer] f
|
176
|
+
# @param [Integer] b
|
177
|
+
# @return [Integer]
|
178
|
+
def calcYCoordinateYRest_(a, f, b)
|
179
|
+
(a + 73*b) % f
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
metadata
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: comic_walker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kohei Suzuki
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: yard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: addressable
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: http-cookie
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rmagick
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubyzip
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Client library for ComicWalker
|
112
|
+
email:
|
113
|
+
- eagletmt@gmail.com
|
114
|
+
executables:
|
115
|
+
- comic-walker
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- Gemfile
|
121
|
+
- LICENSE.txt
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/comic-walker
|
125
|
+
- comic_walker.gemspec
|
126
|
+
- lib/comic_walker.rb
|
127
|
+
- lib/comic_walker/cipher.rb
|
128
|
+
- lib/comic_walker/cli.rb
|
129
|
+
- lib/comic_walker/client.rb
|
130
|
+
- lib/comic_walker/client/license.rb
|
131
|
+
- lib/comic_walker/end_layer_decoder.rb
|
132
|
+
- lib/comic_walker/item_decoder.rb
|
133
|
+
- lib/comic_walker/item_decoder/unknown.rb
|
134
|
+
- lib/comic_walker/version.rb
|
135
|
+
homepage: https://github.com/eagletmt/comic_walker
|
136
|
+
licenses:
|
137
|
+
- MIT
|
138
|
+
metadata: {}
|
139
|
+
post_install_message:
|
140
|
+
rdoc_options: []
|
141
|
+
require_paths:
|
142
|
+
- lib
|
143
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
requirements: []
|
154
|
+
rubyforge_project:
|
155
|
+
rubygems_version: 2.2.2
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Client library for ComicWalker
|
159
|
+
test_files: []
|
160
|
+
has_rdoc:
|