OSDN Git Service

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