4 public function __construct($score_id)
6 $this->score_id = $score_id;
7 $this->dump_lines = null;
11 public function get_filename($dir, $ext)
13 $dirname = sprintf("%s/%d", $dir, floor($this->score_id / 1000) * 1000);
14 return sprintf("%s/%d.%s.gz", $dirname, $this->score_id, $ext);
18 public function show($dir, $ext, $content_type)
20 $filename = $this->get_filename($dir, $ext);
22 if (!file_exists($filename)) {
23 http_response_code(404);
27 $contents = file_get_contents($filename);
28 $etag = md5($contents);
30 if ($etag === filter_input(INPUT_SERVER, 'HTTP_IF_NONE_MATCH')) {
31 http_response_code(304);
35 $content_encoding = self::get_content_encoding();
36 header("Etag: ".$etag);
37 header("Content-Type: ".$content_type);
39 if ($content_encoding !== null) {
40 header("Content-Encoding: ".$content_encoding);
43 echo gzdecode($contents);
48 public function save($dir, $ext, $contents)
50 if ($contents === false) {
54 umask(2); // Group書き込み権限許可
56 $filename = $this->get_filename($dir, $ext);
57 $dirname = dirname($filename);
59 if (!file_exists($dirname)) {
60 mkdir($dirname, 02775, true);
63 $zp = gzopen($filename, "w9");
65 foreach ($contents as $line) {
74 public function exists($dir, $ext)
76 $dirname = sprintf("%s/%d", $dir, floor($this->score_id / 1000) * 1000);
77 $filename = sprintf("%s/%d.%s.gz", $dirname, $this->score_id, $ext);
79 return file_exists($filename);
85 * 読み込んだ配列はメンバ変数dump_linesに格納
87 * @return boolean 読み込みに成功した場合TRUE、失敗した場合FALSE
89 private function readlines()
91 if ($this->dump_lines !== null) {
95 $lines = gzfile($this->get_filename('dumps', 'txt'));
96 if ($lines !== false) {
97 $this->dump_lines = $lines;
106 * キャラクタダンプの死ぬ直前のメッセージもしくは勝利メッセージを取得する
108 * @return array 死ぬ直前のメッセージもしくは勝利メッセージを1行1要素にした文字列の配列
110 public function get_last_message()
112 if ($this->readlines() === false) {
118 foreach ($this->dump_lines as $line) {
119 if (preg_match('/^\s*\[(.*)\]\s*$/u', $line, $matches)) {
120 if ($matches[1] == '*勝利*メッセージ' ||
121 $matches[1] == '死ぬ直前のメッセージ') {
123 } elseif ($in_message) {
129 $result[] = rtrim($line, "\n");
140 * @return string 死因の詳細を表す文字列
142 public function get_death_reason_detail()
144 if ($this->readlines() === false) {
148 $death_reason_lines = array_slice($this->dump_lines, 30, 3);
149 $death_reason = implode("", array_map('trim', $death_reason_lines));
151 if (preg_match("/^…あなたは、?(.+)。/u", $death_reason, $matches)) {
162 * @return string|FALSE 死んだ場所を表す文字列。
163 * 死んだ場所が得られなかった場合FALSE。
165 public function get_dead_place()
167 $death_reason_detail = $this->get_death_reason_detail();
168 if ($death_reason_detail === false) {
171 $places = implode("|", [
172 "階", "荒野", "地上", "街",
173 "辺境の地", "モリバント", "アングウィル", "テルモラ", "ズル",
178 "/^(.*(?:{$places})\s*)で.+$/u",
179 $death_reason_detail,
189 private static function browser_accept_encodings()
191 $accept_encoding = filter_input(INPUT_SERVER, 'HTTP_ACCEPT_ENCODING');
192 if ($accept_encoding == null) {
196 return array_map('trim', explode(",", $accept_encoding));
200 private static function get_content_encoding()
202 $supported_gzip_encodings = array_intersect(
203 self::browser_accept_encodings(),
207 return array_shift($supported_gzip_encodings);