1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package tsukuba_bunko.peko.session;
20
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.ObjectInputStream;
25 import java.io.ObjectOutputStream;
26 import java.net.JarURLConnection;
27 import java.net.URL;
28 import java.net.URLConnection;
29
30 import java.text.DecimalFormat;
31
32 import java.util.Date;
33 import java.util.HashSet;
34
35 import javax.swing.JOptionPane;
36
37 import tsukuba_bunko.peko.Logger;
38 import tsukuba_bunko.peko.PekoSystem;
39
40 import tsukuba_bunko.peko.resource.ResourceManager;
41 import tsukuba_bunko.peko.session.MessageIDs;
42
43
44 /***
45 * セーブデータの管理を行います。
46 * @author $Author: ppoi $
47 * @version $Revision: 1.3 $
48 */
49 public class SessionManager {
50
51 /***
52 * セーブデータ ID のフォーマッタ
53 */
54 protected static final DecimalFormat FORMAT = new DecimalFormat( "000" );
55
56
57 /***
58 * 前回選択したインデックス
59 */
60 protected int _lastIndex = -1;
61
62 /***
63 * システムセーブデータ
64 */
65 protected SystemSaveData _systemSaveData = null;
66
67 /***
68 * 現在のセッション
69 */
70 protected Session _session = null;
71
72
73 /***
74 * <code>SessionManager</code> のインスタンスを生成します。
75 */
76 public SessionManager()
77 {
78 super();
79
80 try {
81 _systemSaveData = loadSystemSaveData();
82 }
83 catch( Exception e ) {
84 Logger.debug( "[session.manager] fail to load SystemSaveData.", e );
85 }
86
87 _session = new Session();
88 _session.setSessionFlagSet( new HashSet(89) );
89 if( _systemSaveData != null ) {
90 _session.setSystemFlagSet( _systemSaveData.getSystemFlagSet(), _systemSaveData.getSaveDataInfo().getTimestamp() );
91 }
92 else {
93 _systemSaveData = new SystemSaveData();
94 SaveDataInfo info = new SaveDataInfo();
95 info.setID( -1 );
96 info.setTitle( "PVNS System Save Data" );
97 _systemSaveData.setSaveDataInfo( info );
98
99 _session.setSystemFlagSet( new HashSet(89), null );
100 }
101 }
102
103
104 /***
105 * 現在のセッションを初期化します。
106 */
107 public void initializeSession()
108 {
109 _session.setSessionFlagSet( new HashSet(89) );
110 _session.setSceneContext( null );
111 }
112
113 /***
114 * 現在のセッションを取得します。
115 * @return 現在のセッション
116 */
117 public Session getSession()
118 {
119 return _session;
120 }
121
122 /***
123 * システムセーブデータを取得します。
124 * @return システムセーブデータ
125 */
126 public SystemSaveData getSystemSaveData()
127 {
128 return _systemSaveData;
129 }
130
131
132 /***
133 * 現在のセッションを保存します。
134 */
135 public void saveCurrentSession()
136 throws SaveFailureException
137 {
138 SaveData saveData = new SaveData();
139 SaveDataInfo saveDataInfo = new SaveDataInfo();
140 saveDataInfo.setTitle( _session.getSceneContext().getSceneTitle() );
141
142 saveData.setSaveDataInfo( saveDataInfo );
143 saveData.setSession( _session );
144
145 save( saveData );
146 }
147
148
149 /***
150 * セーブデータ一覧を取得します。
151 * @param beginIndex 開始インデックス
152 * @param size 取得サイズ
153 * @return セーブデータ一覧
154 */
155 protected SaveDataInfo[] getSaveDataInfoList( int beginIndex, int size )
156 {
157 SaveDataInfo[] list = new SaveDataInfo[ size ];
158 File saveFile = null;
159 SaveData saveData = null;
160 SaveDataInfo saveDataInfo = null;
161 Object data = null;
162 FileInputStream fis = null;
163 ObjectInputStream ois = null;
164 for( int i = 0; i < list.length; ++i ) {
165 saveFile = getSaveFile( i + beginIndex );
166 if( saveFile.isFile() ) {
167 try {
168 fis = new FileInputStream( saveFile );
169 ois = new ObjectInputStream( fis );
170 data = ois.readObject();
171 }
172 catch( Exception e ) {
173 Logger.warn( MessageIDs.SAV0008W, new Object[]{String.valueOf(i + beginIndex)}, e );
174 }
175 finally {
176 if( ois != null ) {
177 try {
178 ois.close();
179 }
180 catch( Exception e ) {
181 Logger.warn( MessageIDs.SAV0004W, e );
182 }
183 }
184 else if( fis != null ) {
185 try {
186 fis.close();
187 }
188 catch( Exception e ) {
189 Logger.warn( MessageIDs.SAV0004W, e );
190 }
191 }
192 ois = null;
193 fis = null;
194 }
195
196 if( data instanceof SaveData ) {
197 saveData = (SaveData)data;
198 if( saveData != null ) {
199 saveDataInfo = saveData.getSaveDataInfo();
200 if( (saveDataInfo != null) && (saveDataInfo.getTitle() != null) && (saveDataInfo.getTimestamp() != null) ) {
201 list[i] = saveDataInfo;
202 }
203 }
204 else {
205 Logger.warn( MessageIDs.SAV0022W, new Object[]{String.valueOf(i)} );
206 }
207 }
208 }
209 }
210
211 return list;
212 }
213
214 /***
215 * セーブデータを保存します。
216 * @param data 保存するセーブデータ
217 * @throws SaveFailureException
218 */
219 protected void save( SaveData data )
220 throws SaveFailureException
221 {
222 SaveDataInfo[] list = getSaveDataInfoList( 0, 20 );
223 int index = SaveDataDialog.showDialog( list, getInitialSelectedIndexForSave(list), true );
224 Logger.debug( "index: " + index );
225 if( index == -1 ) {
226 return;
227 }
228 else {
229 data.getSaveDataInfo().setID( index );
230 }
231 Date timestamp = new Date();
232 data.getSaveDataInfo().setTimestamp( timestamp );
233
234 FileOutputStream fos = null;
235 ObjectOutputStream oos = null;
236 try {
237 File path = getSaveFile( data.getSaveDataInfo().getID() );
238 Logger.debug( "save to " + path.getAbsolutePath() );
239
240 fos = new FileOutputStream( path );
241
242 oos = new ObjectOutputStream( fos );
243 oos.writeObject( data );
244 oos.flush();
245 _lastIndex = index;
246 }
247 catch( Exception e ) {
248 Logger.error( MessageIDs.SAV0001E, new Object[]{String.valueOf(data.getSaveDataInfo().getID())}, e );
249 SaveFailureException sfe = new SaveFailureException( "fail to save.", e );
250 throw sfe;
251 }
252 finally {
253 if( oos != null ) {
254 try {
255 oos.close();
256 }
257 catch( Exception e ) {
258 Logger.warn( MessageIDs.SAV0002W, e );
259 }
260 }
261 else if( fos != null ) {
262 try {
263 fos.close();
264 }
265 catch( Exception e ) {
266 Logger.warn( MessageIDs.SAV0002W, e );
267 }
268 }
269 }
270 }
271
272
273
274 /***
275 * セーブデータを読み込みます。
276 * @return セーブデータをロードした場合 <code>true</code>,ロードしなかった場合 <code>false</code>。
277 * @throws LoadFailureException ロードに失敗した場合
278 */
279 public boolean load()
280 throws LoadFailureException
281 {
282 SaveDataInfo[] list = getSaveDataInfoList( 0, 20 );
283 boolean nodata = false;
284 if( list != null ) {
285 nodata = true;
286 for( int i = 0; i < list.length; ++i ) {
287 if( list[i] != null ) {
288 nodata = false;
289 }
290 }
291 }
292 else {
293 nodata = true;
294 }
295
296 if( nodata ) {
297 ResourceManager resources = ResourceManager.getInstance();
298 String title = (String)resources.getResource( ResourceIDs.DIALOG_NOTIFY_NODATA_TITLE );
299 if( title == null ) {
300 Logger.warn( MessageIDs.SAV0020W );
301 }
302 String message = (String)resources.getResource( ResourceIDs.DIALOG_NOTIFY_NODATA_MESSAGE );
303 if( message == null ) {
304 message = "No save data for loading.";
305 Logger.warn( MessageIDs.SAV0021W, new Object[]{"\"" + message + "\""} );
306 }
307 JOptionPane.showMessageDialog( PekoSystem.getInstance().getMainWindow(), message, title, JOptionPane.INFORMATION_MESSAGE );
308 return false;
309 }
310
311 int index = SaveDataDialog.showDialog( list, getInitialSelectedIndexForLoad(list), false );
312 if( index == -1 ) {
313 return false;
314 }
315
316 File path = getSaveFile( index );
317 if( !path.exists() ) {
318 Logger.error( MessageIDs.SAV0005E, new Object[]{String.valueOf(index)} );
319 LoadFailureException lfe = new LoadFailureException( "no such save file" );
320 throw lfe;
321 }
322
323 FileInputStream fis = null;
324 ObjectInputStream ois = null;
325 try {
326 fis = new FileInputStream( path );
327 ois = new ObjectInputStream( fis );
328
329 SaveData data = (SaveData)ois.readObject();
330 if( !isValidData(data) ) {
331 Logger.error( MessageIDs.SAV0025E );
332 return false;
333 }
334
335 Session session = data.getSession();
336 session.setSystemFlagSet( _session.getSystemFlagSet(), _session.getTimestamp() );
337 _session = session;
338
339 _lastIndex = index;
340 return true;
341 }
342 catch( Exception e ) {
343 Logger.error( MessageIDs.SAV0003E, new Object[]{String.valueOf(String.valueOf(index))}, e );
344 LoadFailureException lfe = new LoadFailureException( "fail to load.", e );
345 throw lfe;
346 }
347 finally {
348 if( ois != null ) {
349 try {
350 ois.close();
351 }
352 catch( Exception e ) {
353 Logger.warn( MessageIDs.SAV0004W, e );
354 }
355 }
356 else if( fis != null ) {
357 try {
358 fis.close();
359 }
360 catch( Exception e ) {
361 Logger.warn( MessageIDs.SAV0004W, e );
362 }
363 }
364 }
365 }
366
367 /***
368 * システムセーブデータを保存します。
369 */
370 public void saveSystemSaveData()
371 throws SaveFailureException
372 {
373 Logger.debug( "save SystemSaveData." );
374 SystemSaveData data = _systemSaveData;
375 data.setSystemFlagSet( _session.getSystemFlagSet() );
376 FileOutputStream fos = null;
377 ObjectOutputStream oos = null;
378 try {
379 data.getSaveDataInfo().setTimestamp( new Date() );
380
381 File path = getSystemSaveFile();
382
383 fos = new FileOutputStream( path );
384
385 oos = new ObjectOutputStream( fos );
386 oos.writeObject( data );
387 oos.flush();
388 }
389 catch( Exception e ) {
390 Logger.error( MessageIDs.SAV0006F, e );
391 SaveFailureException sfe = new SaveFailureException( "fail to save system save data.", e );
392 throw sfe;
393 }
394 finally {
395 if( oos != null ) {
396 try {
397 oos.close();
398 }
399 catch( Exception e ) {
400 Logger.warn( MessageIDs.SAV0002W, e );
401 }
402 }
403 else if( fos != null ) {
404 try {
405 fos.close();
406 }
407 catch( Exception e ) {
408 Logger.warn( MessageIDs.SAV0002W, e );
409 }
410 }
411 }
412 }
413
414 /***
415 * システムセーブデータを読み込みます。
416 * @return システムセーブデータ
417 * @throws LoadFailureException 読み込みに失敗した場合
418 */
419 public SystemSaveData loadSystemSaveData()
420 throws LoadFailureException
421 {
422 SystemSaveData saveData = null;
423
424 File path = getSystemSaveFile();
425 if( !path.exists() ) {
426 return null;
427 }
428
429 FileInputStream fis = null;
430 ObjectInputStream ois = null;
431 try {
432 fis = new FileInputStream( path );
433 ois = new ObjectInputStream( fis );
434
435 saveData = (SystemSaveData)ois.readObject();
436 }
437 catch( Exception e ) {
438 Logger.fatal( MessageIDs.SAV0006F, e );
439 LoadFailureException lfe = new LoadFailureException( "fail to load.", e );
440 throw lfe;
441 }
442 finally {
443 if( ois != null ) {
444 try {
445 ois.close();
446 }
447 catch( Exception e ) {
448 Logger.warn( MessageIDs.SAV0004W, e );
449 }
450 }
451 else if( fis != null ) {
452 try {
453 fis.close();
454 }
455 catch( Exception e ) {
456 Logger.warn( MessageIDs.SAV0004W, e );
457 }
458 }
459 }
460
461 return saveData;
462 }
463
464 /***
465 * <code>id</code> で識別されるセーブデータを格納するセーブファイルを取得します。
466 * @param id セーブデータ ID
467 * @return セーブファイル
468 */
469 protected File getSaveFile( int id )
470 {
471 ResourceManager resources = ResourceManager.getInstance();
472 return new File( resources.getLocationResources().getSaveDirectory(), "save" + FORMAT.format(new Integer(id)) + ".dat" );
473 }
474
475 /***
476 * システムの状態を保存するセーブデータを格納するセーブファイルを取得します。
477 * @return セーブファイル
478 */
479 protected File getSystemSaveFile()
480 {
481 ResourceManager resources = ResourceManager.getInstance();
482 return new File( resources.getLocationResources().getSaveDirectory(), "config.dat" );
483 }
484
485 /***
486 * セーブ時に最初に選択されているインデックスを取得します。
487 * @param list 一覧
488 * @return セーブ時に最初に選択されているインデックス
489 */
490 protected int getInitialSelectedIndexForSave( SaveDataInfo[] list )
491 {
492 if( (list == null) || (list.length == 0) ) {
493 throw new IllegalArgumentException( "void list is specified." );
494 }
495
496 int selected = 0;
497 SaveDataInfo info = null;
498 for( int i = 0; i < list.length; ++i ) {
499 info = list[i];
500 if( info == null ) {
501 selected = i;
502 break;
503 }
504 else {
505 if( (list[selected] == null) || info.getTimestamp().before(list[selected].getTimestamp()) ) {
506 selected = i;
507 }
508 }
509 }
510 return selected;
511 }
512
513 /***
514 * ロード時に最初に選択されているインデックスを取得します。
515 * @param list 一覧
516 * @return ロード時に最初に選択されているインデックス
517 */
518 protected int getInitialSelectedIndexForLoad( SaveDataInfo[] list )
519 {
520 if( (list == null) || (list.length == 0) ) {
521 throw new IllegalArgumentException( "void list is specified." );
522 }
523
524 if( _lastIndex != -1 ) {
525 return _lastIndex;
526 }
527
528 int selected = 0;
529 SaveDataInfo info = null;
530 for( int i = 0; i < list.length; ++i ) {
531 info = list[i];
532 if( info != null ) {
533 if( (list[selected] == null) || info.getTimestamp().after(list[selected].getTimestamp()) ) {
534 selected = i;
535 }
536 }
537 }
538 return selected;
539 }
540
541 /***
542 * セーブデータの妥当性検証をします。
543 * @param data セーブデータ
544 * @return 妥当な場合 <code>true</code>,使用不能な場合 <code>false</code>
545 */
546 protected boolean isValidData( SaveData data )
547 {
548 String sceneName = data.getSession().getSceneContext().getSceneName();
549 URL sceneURL = getSceneURL( sceneName );
550 long lastModified = 0L;
551 try {
552 URLConnection connection = sceneURL.openConnection();
553 connection.connect();
554 if( connection instanceof JarURLConnection ) {
555 JarURLConnection jarconnection = (JarURLConnection)connection;
556 lastModified = jarconnection.getJarEntry().getTime();
557 }
558 else {
559 lastModified = connection.getLastModified();
560 }
561 Logger.info( "last modified time=" + new java.util.Date(lastModified) + ", scene=" + sceneName );
562 }
563 catch( Exception ex ) {
564 Logger.warn( "fail to get last modified timestamp.", ex );
565 }
566
567 if( lastModified > 0L ) {
568 return (data.getSaveDataInfo().getTimestamp().getTime() >= lastModified);
569 }
570 else {
571 return true;
572 }
573 }
574
575 /***
576 * シーンデータの URL を取得します。
577 * @param scene シーン名
578 * @return シーンデータの URL
579 */
580 protected URL getSceneURL( String scene )
581 {
582 ResourceManager resources = ResourceManager.getInstance();
583 URL sceneDir = resources.getLocationResources().getScenesDirecotryURL();
584 try {
585 return new URL( sceneDir, scene );
586 }
587 catch( Exception e ) {
588 Logger.fatal( tsukuba_bunko.peko.scenario.MessageIDs.SCN0006F, e );
589 PekoSystem.showErrorDialog( tsukuba_bunko.peko.scenario.MessageIDs.SCN0006F.getMessage(), e, true );
590 return null;
591 }
592 }
593 }