1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of libreallive, a dependency of RLVM.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (c) 2006, 2007 Peter Jolly
11 //
12 // Permission is hereby granted, free of charge, to any person
13 // obtaining a copy of this software and associated documentation
14 // files (the "Software"), to deal in the Software without
15 // restriction, including without limitation the rights to use, copy,
16 // modify, merge, publish, distribute, sublicense, and/or sell copies
17 // of the Software, and to permit persons to whom the Software is
18 // furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 // SOFTWARE.
31 //
32 // -----------------------------------------------------------------------
33 
34 #include "libreallive/archive.h"
35 
36 #include <boost/algorithm/string.hpp>
37 #include <boost/filesystem.hpp>
38 #include <cstring>
39 #include <string>
40 
41 #include "libreallive/compression.h"
42 
43 using boost::istarts_with;
44 using boost::iends_with;
45 namespace fs = boost::filesystem;
46 
47 namespace libreallive {
48 
Archive(const std::string & filename)49 Archive::Archive(const std::string& filename)
50     : name_(filename), info_(filename, Read), second_level_xor_key_(NULL) {
51   ReadTOC();
52   ReadOverrides();
53 }
54 
Archive(const std::string & filename,const std::string & regname)55 Archive::Archive(const std::string& filename, const std::string& regname)
56     : name_(filename),
57       info_(filename, Read),
58       second_level_xor_key_(NULL),
59       regname_(regname) {
60   ReadTOC();
61   ReadOverrides();
62 
63   if (regname == "KEY\\CLANNAD_FV") {
64     second_level_xor_key_ =
65         libreallive::compression::clannad_full_voice_xor_mask;
66   } else if (regname ==
67              "\x4b\x45\x59\x5c\x83\x8a\x83\x67\x83\x8b\x83"
68              "\x6f\x83\x58\x83\x5e\x81\x5b\x83\x59\x81\x49") {
69     second_level_xor_key_ = libreallive::compression::little_busters_xor_mask;
70   } else if (regname ==
71              "\x4b\x45\x59\x5c\x83\x8a\x83\x67\x83\x8b\x83\x6f\x83\x58\x83\x5e"
72              "\x81\x5b\x83\x59\x81\x49\x82\x64\x82\x77") {
73     // "KEY\<little busters in katakana>!EX", with all fullwidth latin
74     // characters.
75     second_level_xor_key_ =
76         libreallive::compression::little_busters_ex_xor_mask;
77   } else if (regname == "StudioMebius\\SNOWSE") {
78     second_level_xor_key_ =
79         libreallive::compression::snow_standard_edition_xor_mask;
80   } else if (regname ==
81              "\x4b\x45\x59\x5c\x83\x4e\x83\x68\x82\xed\x82\xd3\x82"
82              "\xbd\x81\x5b") {
83     // "KEY\<Kud Wafter in hiragana>"
84     second_level_xor_key_ =
85         libreallive::compression::kud_wafter_xor_mask;
86   } else if (regname ==
87              "\x4b\x45\x59\x5c\x83\x4e\x83\x68\x82\xed\x82\xd3\x82"
88              "\xbd\x81\x5b\x81\x79\x91\x53\x94\x4e\x97\xee\x91\xce"
89              "\x8f\xdb\x94\xc5\x81\x7a") {
90     second_level_xor_key_ =
91         libreallive::compression::kud_wafter_all_ages_xor_mask;
92   }
93 }
94 
~Archive()95 Archive::~Archive() {}
96 
GetScenario(int index)97 Scenario* Archive::GetScenario(int index) {
98   accessed_t::const_iterator at = accessed_.find(index);
99   if (at != accessed_.end())
100     return at->second.get();
101   scenarios_t::const_iterator st = scenarios_.find(index);
102   if (st != scenarios_.end()) {
103     Scenario* scene =
104         new Scenario(st->second, index, regname_, second_level_xor_key_);
105     accessed_[index].reset(scene);
106     return scene;
107   }
108   return NULL;
109 }
110 
GetProbableEncodingType() const111 int Archive::GetProbableEncodingType() const {
112   // Directly create Header objects instead of Scenarios. We don't want to
113   // parse the entire SEEN file here.
114   for (auto it = scenarios_.cbegin(); it != scenarios_.cend(); ++it) {
115     Header header(it->second.data, it->second.length);
116     if (header.rldev_metadata_.text_encoding() != 0)
117       return header.rldev_metadata_.text_encoding();
118   }
119 
120   return 0;
121 }
122 
ReadTOC()123 void Archive::ReadTOC() {
124   const char* idx = info_.get();
125   for (int i = 0; i < 10000; ++i, idx += 8) {
126     const int offs = read_i32(idx);
127     if (offs)
128       scenarios_[i] = FilePos(info_.get() + offs, read_i32(idx + 4));
129   }
130 }
131 
ReadOverrides()132 void Archive::ReadOverrides() {
133   // Iterate over all files in the directory and override the table of contents
134   // if there is a free SEENXXXX.TXT file.
135   fs::path seen_dir = fs::path(name_).branch_path();
136   fs::directory_iterator end;
137   for (fs::directory_iterator it(seen_dir); it != end; ++it) {
138     std::string filename = it->path().filename().string();
139     if (filename.size() == 12 && istarts_with(filename, "seen") &&
140         iends_with(filename, ".txt") && isdigit(filename[4]) &&
141         isdigit(filename[5]) && isdigit(filename[6]) && isdigit(filename[7])) {
142       Mapping* mapping = new Mapping((seen_dir / filename).string(), Read);
143       maps_to_delete_.emplace_back(mapping);
144 
145       int index = std::stoi(filename.substr(4, 4));
146       scenarios_[index] = FilePos(mapping->get(), mapping->size());
147     }
148   }
149 }
150 
151 }  // namespace libreallive
152