OSDN Git Service

[add] #37370 2.2.1までのページ追加。
[hengband/web.git] / score / dump_file.inc
1 <?php
2 class DumpFile
3 {
4     public function __construct($score_id)
5     {
6         $this->score_id = $score_id;
7         $this->dump_lines = null;
8     }
9
10
11     public function get_filename($dir, $ext)
12     {
13         $dirname = sprintf("%s/%d", $dir, floor($this->score_id / 1000) * 1000);
14         return sprintf("%s/%d.%s.gz", $dirname, $this->score_id, $ext);
15     }
16
17
18     public function show($dir, $ext, $content_type)
19     {
20         $filename = $this->get_filename($dir, $ext);
21
22         if (!file_exists($filename)) {
23             http_response_code(404);
24             return;
25         }
26
27         $contents = file_get_contents($filename);
28         $etag = md5($contents);
29
30         if ($etag === filter_input(INPUT_SERVER, 'HTTP_IF_NONE_MATCH')) {
31             http_response_code(304);
32             return;
33         }
34
35         $content_encoding = self::get_content_encoding();
36         header("Etag: ".$etag);
37         header("Content-Type: ".$content_type);
38
39         if ($content_encoding !== null) {
40             header("Content-Encoding: ".$content_encoding);
41             echo $contents;
42         } else {
43             echo gzdecode($contents);
44         }
45     }
46
47
48     public function save($dir, $ext, $contents)
49     {
50         if ($contents === false) {
51             return;
52         }
53
54         umask(2); // Group書き込み権限許可
55
56         $filename = $this->get_filename($dir, $ext);
57         $dirname = dirname($filename);
58
59         if (!file_exists($dirname)) {
60             mkdir($dirname, 02775, true);
61         }
62
63         $zp = gzopen($filename, "w9");
64
65         foreach ($contents as $line) {
66             gzwrite($zp, $line);
67             gzwrite($zp, "\n");
68         }
69
70         gzclose($zp);
71     }
72
73
74     public function exists($dir, $ext)
75     {
76         $dirname = sprintf("%s/%d", $dir, floor($this->score_id / 1000) * 1000);
77         $filename = sprintf("%s/%d.%s.gz", $dirname, $this->score_id, $ext);
78
79         return file_exists($filename);
80     }
81
82
83     /**
84      * キャラクタダンプを配列に読み込む
85      * 読み込んだ配列はメンバ変数dump_linesに格納
86      *
87      * @return boolean 読み込みに成功した場合TRUE、失敗した場合FALSE
88      */
89     private function readlines()
90     {
91         if ($this->dump_lines !== null) {
92             return true;
93         }
94
95         $lines = gzfile($this->get_filename('dumps', 'txt'));
96         if ($lines !== false) {
97             $this->dump_lines = $lines;
98             return true;
99         }
100
101         return false;
102     }
103
104
105     /**
106      * キャラクタダンプの死ぬ直前のメッセージもしくは勝利メッセージを取得する
107      *
108      * @return array 死ぬ直前のメッセージもしくは勝利メッセージを1行1要素にした文字列の配列
109      */
110     public function get_last_message()
111     {
112         if ($this->readlines() === false) {
113             return [];
114         }
115
116         $in_message = false;
117         $result = [];
118         foreach ($this->dump_lines as $line) {
119             if (preg_match('/^\s*\[(.*)\]\s*$/u', $line, $matches)) {
120                 if ($matches[1] == '*勝利*メッセージ' ||
121                     $matches[1] == '死ぬ直前のメッセージ') {
122                     $in_message = true;
123                 } elseif ($in_message) {
124                     break;
125                 }
126             }
127
128             if ($in_message) {
129                 $result[] = rtrim($line, "\n");
130             }
131         }
132
133         return $result;
134     }
135
136
137     /**
138      * キャラクタダンプから死因の詳細を得る
139      *
140      * @return string 死因の詳細を表す文字列
141      */
142     public function get_death_reason_detail()
143     {
144         if ($this->readlines() === false) {
145             return false;
146         }
147
148         $death_reason_lines = array_slice($this->dump_lines, 30, 3);
149         $death_reason = implode("", array_map('trim', $death_reason_lines));
150
151         if (preg_match("/^…あなたは、?(.+)。/u", $death_reason, $matches)) {
152             return $matches[1];
153         } else {
154             return false;
155         }
156     }
157
158
159     /**
160      * キャラクタダンプから死んだ場所を得る
161      *
162      * @return string|FALSE 死んだ場所を表す文字列。
163      *                死んだ場所が得られなかった場合FALSE。
164      */
165     public function get_dead_place()
166     {
167         $death_reason_detail = $this->get_death_reason_detail();
168         if ($death_reason_detail === false) {
169             return false;
170         }
171         $places = implode("|", [
172             "階", "荒野", "地上", "街",
173             "辺境の地", "モリバント", "アングウィル", "テルモラ", "ズル",
174             "クエスト「.+」", "クエスト",
175         ]);
176
177         if (preg_match(
178             "/^(.*(?:{$places})\s*)で.+$/u",
179             $death_reason_detail,
180             $matches
181         )) {
182             return $matches[1];
183         } else {
184             return false;
185         }
186     }
187
188
189     private static function browser_accept_encodings()
190     {
191         $accept_encoding = filter_input(INPUT_SERVER, 'HTTP_ACCEPT_ENCODING');
192         if ($accept_encoding == null) {
193             return [];
194         }
195
196         return array_map('trim', explode(",", $accept_encoding));
197     }
198
199
200     private static function get_content_encoding()
201     {
202         $supported_gzip_encodings = array_intersect(
203             self::browser_accept_encodings(),
204             ['gzip', 'x-gzip']
205         );
206
207         return array_shift($supported_gzip_encodings);
208     }
209 }