-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdqmjsav.c
2559 lines (2070 loc) · 144 KB
/
dqmjsav.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include <malloc.h>
#include "dqmjsav.h"
#include "dqmjsav.inl"
/************************************************************************/
#define DESMUME_IDENTIFIER "|-DESMUME SAVE-|"
#define NOCASHBGA_IDENTIFIER "NocashGbaBackupMediaSavDataFile\x1A"
#define NOCASHBGA_UNCOMPRESSED 0 /* No$GBA圧縮方法用:未圧縮 */
#define NOCASHBGA_COMPRESSED 1 /* No$GBA圧縮方法用:圧縮済み */
#define SAVE_SIZE_MIN 0xA760 /* セーブデータの最小サイズ */
#define INTERRUPT_OFFSET 0xA700 /* 中断データのオフセット */
#define SAVE_IDENTIFIER 0x004D5144 /* 'DQM\x00' */
#define STATUS_DEAD 0x80 /* 死亡フラグ */
#define STATUS_POISON 0x01 /* 毒フラグ */
#define MINMAX(v, a, b) (max(min((v), (b)), (a)))
#define BETWEEN(v, a, b) (((v) >= (a)) && ((v) < (b)))
#define IS_INTR(handle) (((struct save_info *)(handle))->interrupt_flag)
#define GET_FMT(handle) ((struct save_fmt *)(IS_INTR(handle) ? (((struct save_info *)(handle))->raw_data + INTERRUPT_OFFSET) : ((struct save_info *)(handle))->raw_data))
#define GET_HEADER(handle) (&GET_FMT(handle)->header)
#define GET_BODY(handle) (&GET_FMT(handle)->body)
#define EMPTY_NAME(name) do { ZeroMemory(&(name), sizeof(name)); *((LPBYTE)&(name)) = 0xFF; } while (FALSE)
#define COPY_NAME(dest, src) do { ZeroMemory(&(dest), sizeof(dest)); CopyMemory(&(dest), &(src), min(sizeof(dest), sizeof(src))); } while (FALSE)
#define GET_BIT(buff, i) (((buff)[(i) >> 3] >> ((i) & 7)) & 1)
#define SET_BIT(buff, i) ((VOID)((buff)[(i) >> 3] |= 1 << ((i) & 7)))
#define CLR_BIT(buff, i) ((VOID)((buff)[(i) >> 3] &= ~(1 << ((i) & 7))))
#define GET_HALF(buff, i) (((i) & 1) ? ((buff)[(i) >> 1] >> 4) : ((buff)[(i) >> 1] & 0x0F))
#define SET_HALF(buff, i, v) ((VOID)((buff)[(i) >> 1] = ((i) & 1) ? (((buff)[(i) >> 1] & 0x0F) | ((v) << 4)) : (((buff)[(i) >> 1] & 0xF0) | ((v) & 0x0F))))
/************************************************************************/
#pragma pack(1)
/* モンスターデータフォーマット */
struct monster_fmt
{
UINT8 name[11]; /* 0000: 名前 */
UINT8 unused1; /* 000B: 未使用、通常は0 */
UINT16 race; /* 000C: 種別 */
UINT8 rank; /* 000E: ランク */
UINT8 type; /* 000F: 系統 */
UINT32 sex; /* 0010: 性別 */
UINT8 combine; /* 0014: 配合回数 */
UINT8 rom_data1[4]; /* 0015: 不明、ROMデータその1 */
UINT8 rom_data2[24]; /* 0019: 不明、ROMデータその2 */
UINT8 unused2; /* 0031: 未使用、通常は0 */
UINT8 base_guard[31]; /* 0032: 既定ガード */
UINT8 actual_guard[31]; /* 0051: 実際ガード */
UINT8 source; /* 0070: 入手ルート */
UINT8 status; /* 0071: 異常状態マスク */
UINT8 level; /* 0072: レベル */
UINT8 max_level; /* 0073: レベル成長限界 */
UINT16 base_hp; /* 0074: 基礎最大HP */
UINT16 base_mp; /* 0076: 基礎最大MP */
UINT16 base_atk; /* 0078: 基礎攻撃力 */
UINT16 base_def; /* 007A: 基礎守備力 */
UINT16 base_agi; /* 007C: 基礎素早さ */
UINT16 base_int; /* 007E: 基礎賢さ */
UINT16 current_hp; /* 0080: 現在残りHP */
UINT16 actual_hp; /* 0082: 実際最大HP */
UINT16 current_mp; /* 0084: 現在残りMP */
UINT16 actual_mp; /* 0086: 実際最大MP */
UINT16 actual_atk; /* 0088: 実際攻撃力 */
UINT16 actual_def; /* 008A: 実際守備力 */
UINT16 actual_agi; /* 008C: 実際素早さ */
UINT16 actual_int; /* 008E: 実際賢さ */
UINT32 exp; /* 0090: 経験値 */
UINT32 strategy; /* 0094: 作戦 */
UINT8 growth_type; /* 0098: 爆発成長タイプ */
UINT8 growth_level[2]; /* 0099: 爆発成長レベル範囲 */
UINT32 ability_mask; /* 009B: 特性マスク */
UINT8 weapon; /* 009F: 装備している武器 */
UINT8 skillset[3]; /* 00A0: スキル */
UINT8 assigned_sp[3]; /* 00A3: 各スキルに振り分けたポイント */
UINT8 skill_active_num[3]; /* 00A6: 各スキルの開放特技数 */
UINT8 unused3; /* 00A9: 未使用、通常は0 */
UINT16 remaining_sp; /* 00AA: 残りスキルポイント */
UINT8 skill_list[30]; /* 00AC: 習得特技リスト */
UINT8 ability_list[40]; /* 00CA: 特性リスト */
UINT8 cheat; /* 00F2: 不正フラグ */
UINT8 level_bak; /* 00F3: レベル */
UINT32 checksum; /* 00F4: チェックサム */
UINT8 master[11]; /* 00F8: マスターの名前 */
UINT8 unused4; /* 0103: 未使用、通常は0 */
UINT16 father_race; /* 0104: 父親の種別 */
UINT8 father_name[11]; /* 0106: 父親の名前 */
UINT8 father_master[11]; /* 0111: 父親のマスターの名前 */
UINT16 mother_race; /* 011C: 母親の種別 */
UINT8 mother_name[11]; /* 011E: 母親の名前 */
UINT8 mother_master[11]; /* 0129: 母親のマスターの名前 */
UINT16 father_father_race; /* 0134: 祖父の種別 */
UINT8 father_father_name[11]; /* 0136: 祖父の名前の為に保留、未使用 */
UINT8 father_father_master[11]; /* 0141: 祖父のマスターの名前の為に保留、未使用 */
UINT16 father_mother_race; /* 014C: 祖母の種別 */
UINT8 father_mother_name[11]; /* 014E: 祖母の名前の為に保留、未使用 */
UINT8 father_mother_master[11]; /* 0159: 祖母のマスターの名前の為に保留、未使用 */
UINT16 mother_father_race; /* 0164: 外祖父の種別 */
UINT8 mother_father_name[11]; /* 0166: 外祖父の名前の為に保留、未使用 */
UINT8 mother_father_master[11]; /* 0171: 外祖父のマスターの名前の為に保留、未使用 */
UINT16 mother_mother_race; /* 017C: 外祖母の種別 */
UINT8 mother_mother_name[11]; /* 017E: 外祖母の名前の為に保留、未使用 */
UINT8 mother_mother_master[11]; /* 0189: 外祖母のマスターの名前の為に保留、未使用 */
};
/* セーブデータフォーマットヘッダ */
struct save_fmt_header
{
UINT32 identifier; /* 0000: 常時'DQM\x00' */
UINT8 version[8]; /* 0004: バージョン情報? */
UINT32 checksum; /* 000C: チェックサム */
UINT8 unknown1[28];
UINT32 play_time; /* 002C: プレイ時間(ロード画面表示用) */
UINT8 party_member_num; /* 0030: パーティモンスター数(ロード画面表示用) */
UINT8 place_id; /* 0031: 場所(ロード画面表示用) */
UINT8 player_name[12]; /* 0032: 主人公の名前(ロード画面表示用) */
UINT8 party_member_name[3][12]; /* 003E: パーティモンスターの名前(ロード画面表示用) */
UINT16 party_member_race[3]; /* 0062: パーティモンスターの種別(ロード画面表示用) */
UINT8 party_member_level[3]; /* 0068: パーティモンスターのレベル(ロード画面表示用) */
UINT8 story_progress; /* 006B: ストーリーの進み具合 */
UINT8 unknown2[4];
};
/* セーブデータフォーマットボディ */
struct save_fmt_body
{
UINT8 unknown1[264]; /* 0070: チェックサム計算開始位置 */
UINT8 player_name[12]; /* 0178: 主人公の名前 */
UINT32 gold; /* 0184: 所持金 */
UINT32 deposit; /* 0188: 預金額 */
UINT8 baggage[16]; /* 018C: 手荷物 */
UINT8 inventory[155]; /* 019C: 袋内道具 */
UINT8 unknown3[105];
UINT32 play_time; /* 02A0: プレイ時間 */
UINT8 party_member_num; /* 02A4: パーティモンスター数 */
UINT8 monster_num; /* 02A5: 所持モンスター数 */
UINT8 party_member_idx[3]; /* 02A6: パーティモンスターの番号 */
UINT8 encounter_library[45]; /* 02A9: 遭遇ライブラリ */
UINT8 kill_library[45]; /* 02D6: 討伐ライブラリ */
UINT8 obtain_library[45]; /* 0303: 入手ライブラリ */
UINT8 skill_library[121]; /* 0330: スキルライブラリ */
UINT8 unknown4[10];
UINT8 player_skill; /* 03B3: 主人公特技 */
UINT8 unknown5[20];
UINT32 scout_times; /* 03C8: スカウト成功数 */
UINT8 unknown6[8];
UINT32 victory_times; /* 03D4: 戦闘勝利数 */
UINT32 combine_times; /* 03D8: 配合回数 */
UINT8 unknown7[32];
UINT32 bookstore_onsale[6]; /* 03FC: スキルブックストア販売中ブック */
UINT8 unknown8[500];
UINT32 bookstore_inited; /* 0608: スキルブックストア初期化完了フラグ */
UINT16 standby_num; /* 060C: スタンバイモンスター数 */
UINT8 standby_idx[3]; /* 060E: パーティモンスターの番号 */
UINT8 unknown9[75];
struct monster_fmt monster_list[100]; /* 065C: 所持モンスター情報 */
struct monster_fmt saint_beast; /* A42C: 神獣取得時のモンスター情報 */
UINT8 unknown10[132];
};
/* セーブデータフォーマット */
struct save_fmt
{
struct save_fmt_header header; /* 0000: ヘッダ */
struct save_fmt_body body; /* 0070: ボディ(チェックサム計算する部分) */
UINT8 tail[188]; /* A644: テール */
};
/* No$GBAバッテリーファイルヘッダ */
struct nocashgba_header
{
UINT8 identifier[32]; /* 0000: 常時'NocashGbaBackupMediaSavDataFile\x1A' */
UINT32 unknown1;
UINT8 day; /* 0024: 保存時刻情報(日) */
UINT8 month; /* 0025: 保存時刻情報(月) */
UINT16 year; /* 0026: 保存時刻情報(年) */
UINT8 hour; /* 0028: 保存時刻情報(時間) */
UINT8 minute; /* 0029: 保存時刻情報(分) */
UINT16 unknown2;
UINT32 unknown3;
UINT8 zero[16]; /* 0030: 常時ゼロ */
UINT32 sram; /* 0040: 常時'SRAM' */
UINT32 compression; /* 0044: 圧縮方法 */
UINT32 data_size; /* 0048: 格納データサイズ(圧縮の場合は圧縮後のみ) */
};
/* DeSmuMEバッテリーファイルフッタ */
struct desmume_footer
{
UINT32 actually_written_size; /* 0000: 実際データサイズ */
UINT32 padded_size; /* 0004: パディング後のデータサイズ */
UINT32 save_type; /* 0008: 未使用 */
UINT32 address_size; /* 000C: アドレスバスサイズ */
UINT32 save_size; /* 0010: 未使用 */
UINT32 version_number; /* 0014: 常時0 */
UINT8 identifier[16]; /* 0018: 常時'|-DESMUME SAVE-|' */
};
/* セーブファイルフォーマット用拡張情報 */
union extend_info
{
struct nocashgba_info
{
struct nocashgba_header header; /* No$GBAバッテリーファイルヘッダ */
LPBYTE footer; /* No$GBAバッテリーファイルフッタデータバッファ */
UINT footer_size; /* No$GBAバッテリーファイルフッタデータサイズ */
} nocashgba_info;
struct desmume_info
{
struct desmume_footer footer; /* DeSmuMEバッテリーファイルフッタ */
LPBYTE dummy; /* DeSmuMEダミーデータバッファ */
UINT dummy_size; /* DeSmuMEダミーデータサイズ */
} desmume_info;
};
#pragma pack()
/* 処理用セーブ情報 */
struct save_info
{
int format; /* セーブファイルのフォーマット */
LPBYTE raw_data; /* 生のセーブデータ */
UINT raw_size; /* 生データのサイズ */
BOOL interrupt_flag; /* 中断データの有り無し */
union extend_info extend_data; /* フォーマット拡張情報 */
};
/************************************************************************/
/* 「ふめい」マスターの名前 */
CONST static DQMJ_NAME UNKNOWN_MASTER = { { 0x45, 0x4B, 0x26, 0xFF, 0x5B, 0x8E, 0x7F, 0xFF, 0x45, 0x4B, 0x26 } };
/************************************************************************/
/* 指定の固有情報を探す */
#define lookup_place_info(place) ((CONST struct name_info *)lookup_info_base((place), sizeof(struct name_info), PLACE_LIST, _countof(PLACE_LIST), -1))
#define lookup_guard_info(guard) ((CONST struct name_info *)lookup_info_base((guard), sizeof(struct name_info), GUARD_LIST, _countof(GUARD_LIST), -1))
#define lookup_ability_info(ability) ((CONST struct ability_info *)lookup_info_base((ability), sizeof(struct ability_info), ABILITY_LIST, _countof(ABILITY_LIST), 0x00))
#define lookup_skillset_info(skillset) ((CONST struct skillset_info *)lookup_info_base((skillset), sizeof(struct skillset_info), SKILLSET_LIST, _countof(SKILLSET_LIST), 0x00))
#define lookup_skill_info(skill) ((CONST struct skill_info *)lookup_info_base((skill), sizeof(struct skill_info), SKILL_LIST, _countof(SKILL_LIST), 0x00))
#define lookup_item_info(item) ((CONST struct item_info *)lookup_info_base((item), sizeof(struct item_info), ITEM_LIST, _countof(ITEM_LIST), 0x00))
#define lookup_race_info(race) ((CONST struct race_info *)lookup_info_base((race), sizeof(struct race_info), RACE_LIST, _countof(RACE_LIST), 0x00))
/* モンスター性別をランダムで作成する */
#define gen_monster_sex(type) (((rand() % 16) == 0) ? DQMJ_SEX_BISEXUAL : (rand() % 2))
/* 爆発成長タイプをランダムで作成する */
#define gen_growth_type() (rand() % DQMJ_GROWTH_NUM)
/* 爆発成長開始レベルをランダムで作成する */
#define gen_growth_start_level() (DQMJ_GROWTH_LEVEL_MIN + rand() % (DQMJ_GROWTH_LEVEL_MAX - DQMJ_GROWTH_LEVEL_MIN + 1))
/* 爆発成長終了レベルをランダムで作成する */
#define gen_growth_end_level(start) ((start) + (DQMJ_GROWTH_RANGE_MIN - 1) + (rand() % (DQMJ_GROWTH_RANGE_MAX - DQMJ_GROWTH_RANGE_MIN + 1)))
/* セーブデータのチェックサムを算出 */
static UINT32 calc_save_checksum(struct save_fmt *sav);
/* モンスターデータのチェックサムを算出 */
static UINT32 calc_monster_checksum(struct monster_fmt *monster);
/* セーブ用タイムスタンプを時分秒に変換 */
static VOID timestamp_to_hms(UINT32 timestamp, LPINT hour, LPINT minute, LPINT second);
/* 時分秒をセーブ用タイムスタンプに変換 */
static UINT32 hms_to_timestamp(int hour, int minute, int second, UINT32 timestamp);
/* 指定の固有情報を探す */
static LPCVOID lookup_info_base(int id, size_t size, LPCVOID head, int num, int def);
/* 指定のガードを1段階上げる */
static VOID upgrade_guard(LPINT guard_info, int guard);
/* 指定のスキルを習得する */
static int learn_skillset(LPINT skillset_list, int skillset);
/* 指定の特性を開放する */
static VOID unlock_ability(LPINT ability_list, int ability);
/* 指定の特技を開放する */
static VOID unlock_skill(LPINT skill_list, int skill);
/* 特技リストの順番ソート */
static VOID sort_skill_list(LPINT skill_list, LPINT order);
/* 指定するアイテムは装備可能かを判断する */
static CONST struct item_info *check_equippable(int equipment, CONST struct race_info *ri, CONST int *ability_list);
/* 処理用セーブ情報内部で使われてるメモリーを解放 */
static VOID free_save_info(struct save_info *sav);
/* 特定モンスターはパーティーメンバーか、もしくはスタンバイかを判定 */
static BOOL check_monster_in_party_or_standby(int monster_idx, CONST struct save_fmt_body *body);
/* 全てのパーティーメンバーが削除された場合デフォルトのパーティメンバーを作成 */
static void set_default_party_member(struct save_fmt_header *header, struct save_fmt_body *body);
/* 特定モンスター情報を強制変更 */
static VOID modify_monster_info_force(struct monster_fmt *monster, CONST DQMJ_SAVE_MONSTER_INFO *in);
/* モンスター情報を全て合法値範囲内にノーマライズする */
static BOOL normalize_monster_by_handle(HDQMJSAVE handle, int monster_idx, DQMJ_SAVE_MONSTER_INFO *inout);
/* 特定モンスター情報を全てクリア */
static VOID clear_monster_info(struct monster_fmt *monster);
/* 開いたセーブファイルの読み込み処理 */
static BOOL read_save_file(HANDLE file, struct save_info *sav);
/* 新規セーブファイルの書き込み処理 */
static BOOL write_save_file(HANDLE file, struct save_info *sav, BOOL as_raw);
/* 未圧縮データを直接読み込み */
static BOOL read_uncompressed_data(HANDLE file, UINT size, struct save_info *sav);
/* 圧縮データを解凍しながら読み込む */
static BOOL read_compressed_data(HANDLE file, UINT data_size, UINT raw_size, struct save_info *sav);
/* セーブデータを圧縮して書き込む */
static UINT write_compressed_data(HANDLE file, struct save_info *sav);
/* 圧縮コピーデータ処理 */
static UINT compress_copy_data(HANDLE file, UINT len, LPBYTE data);
/* 圧縮重複データ処理 */
static UINT compress_repeat_data(HANDLE file, UINT len, BYTE data);
/************************************************************************/
/* 名前データをUnicode文字列に変換する */
UINT DQMJSaveNameToString(LPWSTR str, UINT len, CONST DQMJ_NAME *name, WCHAR def)
{
int mode;
UINT i, j;
WCHAR ch;
static WCHAR lookup_table[256][3];
static BOOL init_flag = FALSE;
if (!name)
return 0;
if (!init_flag)
{
for (i = 0; i < _countof(lookup_table); i++)
for (j = 0; j < _countof(lookup_table[i]); j++)
lookup_table[i][j] = L'?';
for (i = 0; i < _countof(NAME_LIST); i++)
{
if (NAME_LIST[i].str[0] == L'{')
continue;
for (j = 0; j < (UINT)lstrlenW(NAME_LIST[i].str); j++)
lookup_table[NAME_LIST[i].id][j] = NAME_LIST[i].str[j];
}
init_flag = TRUE;
}
for (i = 0, j = 0, mode = 0; i < DQMJ_NAME_MAX; i++)
{
if (j >= len)
return j;
if (name->byte_code[i] == 0xFF)
break;
if (name->byte_code[i] == 0x92)
{
mode = 1;
continue;
}
if (name->byte_code[i] == 0x93)
{
mode = 2;
continue;
}
if (str != NULL)
{
ch = lookup_table[name->byte_code[i]][mode];
if (ch == L'?')
str[j] = (def == DQMJ_NAME_DEFAULT_AS_BYTECODE) ? name->byte_code[i] : def;
else
str[j] = ch;
}
mode = 0;
j++;
}
if (str != NULL)
str[j] = L'\0';
return ++j;
}
/* Unicode文字列を名前データに変換する */
UINT DQMJSaveStringToName(DQMJ_NAME *name, LPCWSTR str)
{
UINT i, j, k;
BOOL loop;
static WCHAR lookup_table[3][256];
static BOOL init_flag = FALSE;
if (!init_flag)
{
ZeroMemory(lookup_table, sizeof(lookup_table));
for (i = 0; i < _countof(NAME_LIST); i++)
{
if (NAME_LIST[i].str[0] == L'{')
continue;
for (j = 0; j < (UINT)lstrlenW(NAME_LIST[i].str); j++)
lookup_table[j][NAME_LIST[i].id] = NAME_LIST[i].str[j];
}
init_flag = TRUE;
}
if (name != NULL)
EMPTY_NAME(*name);
for (k = 0; *str != L'\0'; str++)
{
if ((*str & 0xFF00) == 0)
{
if (name != NULL)
name->byte_code[k] = *str & 0xFF;
k++;
}
else
{
loop = TRUE;
for (i = 0; loop && (i < _countof(lookup_table)); i++)
for (j = 0; loop && (j < _countof(lookup_table[i])); j++)
{
if (*str != lookup_table[i][j])
continue;
if (i == 1)
{
if (name != NULL)
name->byte_code[k] = 0x92;
k++;
}
else if (i == 2)
{
if (name != NULL)
name->byte_code[k] = 0x93;
k++;
}
if (k >= DQMJ_NAME_MAX)
return k;
if (name != NULL)
name->byte_code[k] = j;
k++;
loop = FALSE;
}
}
if (k >= DQMJ_NAME_MAX)
return k;
}
if (k >= DQMJ_NAME_MAX)
return k;
if (name != NULL)
name->byte_code[k] = 0xFF;
return ++k;
}
/* モンスター種別設定情報を取得 */
BOOL DQMJSaveGetRaceSetting(int race, DQMJ_SAVE_RACE_SETTING *out)
{
int i;
CONST struct race_info *ri;
if (out == NULL)
return FALSE;
ri = lookup_race_info(race);
if (ri->race == 0x00)
return FALSE;
out->race = ri->race;
out->rank = ri->rank;
out->type = ri->type;
out->name = ri->name;
out->limit_hp = ri->limit_hp;
out->limit_mp = ri->limit_mp;
out->limit_atk = ri->limit_atk;
out->limit_def = ri->limit_def;
out->limit_agi = ri->limit_agi;
out->limit_int = ri->limit_int;
for (i = 0; i < DQMJ_GUARD_MAX; i++)
out->guard[i] = GET_HALF(ri->base_guard, i);
for (i = 0; i < DQMJ_ABILITY_MAX; i++)
out->ability[i] = ri->base_ability[i];
for (i = 0; i < DQMJ_SKILLSET_MAX; i++)
out->skillset[i] = ri->base_skillset[i];
return TRUE;
}
/* 道具IDから道具設定情報を取得 */
BOOL DQMJSaveGetItemSetting(int item, DQMJ_SAVE_ITEM_SETTING *out)
{
CONST struct item_info *ii;
if (out == NULL)
return FALSE;
ii = lookup_item_info(item);
if (ii->item == 0x00)
return FALSE;
out->item = ii->item;
out->type = 0;
out->weapon = (ii->type & ITEM_WEAPON) ? TRUE : FALSE;
out->name = ii->name;
if (ii->type & ITEM_USABLE)
out->type = DQMJ_ITEM_USABLE;
else if (ii->type & ITEM_SPECIAL)
out->type = DQMJ_ITEM_SPECIAL;
else if (ii->type & ITEM_SWORD)
out->type = DQMJ_ITEM_SWORD;
else if (ii->type & ITEM_SPEAR)
out->type = DQMJ_ITEM_SPEAR;
else if (ii->type & ITEM_AXE)
out->type = DQMJ_ITEM_AXE;
else if (ii->type & ITEM_HAMMER)
out->type = DQMJ_ITEM_HAMMER;
else if (ii->type & ITEM_WHIP)
out->type = DQMJ_ITEM_WHIP;
else if (ii->type & ITEM_CLAW)
out->type = DQMJ_ITEM_CLAW;
else if (ii->type & ITEM_STAFF)
out->type = DQMJ_ITEM_STAFF;
else
out->type = DQMJ_ITEM_NORMAL;
return TRUE;
}
/* スキルIDからスキル情報を取得 */
BOOL DQMJSaveGetSkillsetSetting(int skillset, DQMJ_SAVE_SKILLSET_SETTING *out)
{
int i;
CONST struct skillset_info *ssi;
if (out == NULL)
return FALSE;
ssi = lookup_skillset_info(skillset);
if (ssi->skillset == 0x00)
return FALSE;
out->skillset = ssi->skillset;
out->name = ssi->name;
out->param = ssi->param;
out->count = 0;
out->max_point = 0;
for (i = 0; i < _countof(ssi->point); i++)
{
if (ssi->point[i] == 0)
break;
out->count++;
out->max_point = ssi->point[i];
}
return TRUE;
}
/* 場所IDから場所名を取得 */
LPCTSTR DQMJSaveGetPlaceName(int place)
{
CONST struct name_info *ni;
ni = lookup_place_info(place);
if (ni == NULL)
return NULL;
return ni->name;
}
/* ガードIDからガード名を取得 */
LPCTSTR DQMJSaveGetGuardName(int guard)
{
CONST struct name_info *ni;
ni = lookup_guard_info(guard);
if (ni == NULL)
return NULL;
return ni->name;
}
/* 特技IDから特技名を取得 */
LPCTSTR DQMJSaveGetSkillName(int skill)
{
CONST struct skill_info *si;
si = lookup_skill_info(skill);
if (si->skill == 0x00)
return NULL;
return si->name;
}
/* 特性IDから特性名を取得 */
LPCTSTR DQMJSaveGetAbilityName(int ability)
{
CONST struct ability_info *ai;
ai = lookup_ability_info(ability);
if (ai->ability == 0x00)
return NULL;
return ai->name;
}
/* 指定するアイテムは装備可能かを判断する */
BOOL DQMJSaveCheckEquippable(int race, int equipment, CONST int *ability_list)
{
CONST struct race_info *ri;
if (ability_list == NULL)
return FALSE;
ri = lookup_race_info(race);
if (ri->race == 0x00)
return FALSE;
if (check_equippable(equipment, ri, ability_list) == NULL)
return FALSE;
return TRUE;
}
/* モンスタースキルに振り分けたスキルポイントから該当スキルの開放特技数を取得 */
int DQMJSaveCalcSkillActiveCount(int skillset, int skill_point)
{
int i, n;
CONST struct skillset_info *ssi;
ssi = lookup_skillset_info(skillset);
if (ssi == NULL)
return 0;
n = 0;
for (i = 0; i < _countof(ssi->point); i++)
{
if (ssi->point[i] == 0)
break;
if (skill_point >= ssi->point[i])
n++;
}
return n;
}
/* モンスター情報を全て合法値範囲内にノーマライズする */
BOOL DQMJSaveNormalizeMonster(BOOL interrupt, BOOL in_party_or_standby, CONST DQMJ_NAME *player_name, DQMJ_SAVE_MONSTER_INFO *inout)
{
int i, j, max_sp;
DQMJ_SAVE_MONSTER_INFO mi;
CONST struct race_info *ri;
CONST struct item_info *ii;
CONST struct skillset_info *ssi;
if (inout == NULL)
return FALSE;
ri = lookup_race_info(inout->race);
if (ri->race == 0x00)
return FALSE;
mi = *inout;
ZeroMemory(inout, sizeof(*inout));
inout->cheat = FALSE;
inout->dead = (interrupt && in_party_or_standby && mi.dead) ? TRUE : FALSE;
inout->poison = FALSE;
inout->race = ri->race;
inout->sex = (ri->type == DQMJ_TYPE_INCARNI) ? DQMJ_SEX_BISEXUAL : (BETWEEN(mi.sex, 0, DQMJ_SEX_NUM) ? mi.sex : gen_monster_sex(ri->type));
inout->rank = ri->rank;
inout->type = ri->type;
inout->combine = MINMAX(mi.combine, 0, DQMJ_COMBINE_MAX);
inout->source = (!BETWEEN(mi.source, 0, DQMJ_SOURCE_NUM) || ((ri->type == DQMJ_TYPE_INCARNI) && ((mi.combine > 0) || ((mi.source != DQMJ_SOURCE_SYSTEM) && (mi.source != DQMJ_SOURCE_COMBINE))))) ? DQMJ_SOURCE_COMBINE : mi.source;
inout->base_hp = MINMAX(mi.base_hp, 1, ri->limit_hp);
inout->base_mp = MINMAX(mi.base_mp, 0, ri->limit_mp);
inout->base_atk = MINMAX(mi.base_atk, 0, ri->limit_atk);
inout->base_def = MINMAX(mi.base_def, 0, ri->limit_def);
inout->base_agi = MINMAX(mi.base_agi, 0, ri->limit_agi);
inout->base_int = MINMAX(mi.base_int, 0, ri->limit_int);
inout->actual_hp = inout->base_hp;
inout->actual_mp = inout->base_mp;
inout->actual_atk = inout->base_atk;
inout->actual_def = inout->base_def;
inout->actual_agi = inout->base_agi;
inout->actual_int = inout->base_int;
inout->exp = max(mi.exp, 0);
inout->strategy = BETWEEN(mi.strategy & 0xFF, 0, DQMJ_STRATEGY_NUM) ? (mi.strategy & 0xFF) : DQMJ_STRATEGY_GO_ALL_OUT;
inout->remaining_sp = MINMAX(mi.remaining_sp, 0, DQMJ_SKILLPOINT_MAX);
if (inout->combine < 5)
inout->max_level = 50;
else if (((inout->combine < 10) || (mi.max_level < 99)) && (mi.level <= 75))
inout->max_level = 75;
else
inout->max_level = 99;
inout->level = MINMAX(mi.level, 1, inout->max_level);
if (BETWEEN(mi.growth_type, 0, DQMJ_GROWTH_NUM))
inout->growth_type = mi.growth_type;
else
inout->growth_type = gen_growth_type();
if ((mi.growth_start_level == DQMJ_RAND_GROWTH_LEVEL) || (mi.growth_end_level == DQMJ_RAND_GROWTH_LEVEL) || !BETWEEN(mi.growth_start_level, DQMJ_GROWTH_LEVEL_MIN, DQMJ_GROWTH_LEVEL_MAX + 1))
{
inout->growth_start_level = gen_growth_start_level();
inout->growth_end_level = gen_growth_end_level(inout->growth_start_level);
}
else
{
inout->growth_start_level = mi.growth_start_level;
if (BETWEEN(mi.growth_end_level - mi.growth_start_level + 1, DQMJ_GROWTH_RANGE_MIN, DQMJ_GROWTH_RANGE_MAX + 1))
inout->growth_end_level = mi.growth_end_level;
else
inout->growth_end_level = gen_growth_end_level(mi.growth_start_level);
}
for (i = 0; i < DQMJ_GUARD_MAX; i++)
inout->guard_info[i] = GET_HALF(ri->base_guard, i);
for (i = 0; i < DQMJ_ABILITY_MAX; i++)
inout->ability_list[i] = ri->base_ability[i];
for (i = 0; i < DQMJ_SKILLSET_MAX; i++)
{
if (lookup_skillset_info(mi.skillset[i])->skillset == 0x00)
continue;
j = learn_skillset(inout->skillset, mi.skillset[i]);
if (j < 0)
break;
inout->assigned_sp[j] += mi.assigned_sp[i];
}
for (i = 0; i < DQMJ_SKILLSET_MAX; i++)
{
ssi = lookup_skillset_info(inout->skillset[i]);
if (ssi->skillset == 0x00)
break;
for (j = 0, max_sp = 0; j < _countof(ssi->point); j++)
{
if (ssi->point[j] == 0)
break;
if (max_sp < ssi->point[j])
max_sp = ssi->point[j];
}
inout->assigned_sp[i] = MINMAX(inout->assigned_sp[i], 0, max_sp);
inout->skill_active_num[i] = DQMJSaveCalcSkillActiveCount(ssi->skillset, inout->assigned_sp[i]);
for (j = 0; j < inout->skill_active_num[i]; j++)
{
switch (ssi->type[j])
{
case UNLOCK_STATUS_HP:
inout->actual_hp += ssi->value[j];
break;
case UNLOCK_STATUS_MP:
inout->actual_mp += ssi->value[j];
break;
case UNLOCK_STATUS_ATK:
inout->actual_atk += ssi->value[j];
break;
case UNLOCK_STATUS_DEF:
inout->actual_def += ssi->value[j];
break;
case UNLOCK_STATUS_AGI:
inout->actual_agi += ssi->value[j];
break;
case UNLOCK_STATUS_INT:
inout->actual_int += ssi->value[j];
break;
case UNLOCK_GUARD:
upgrade_guard(inout->guard_info, ssi->value[j]);
break;
case UNLOCK_ABILITY:
unlock_ability(inout->ability_list, ssi->value[j]);
break;
case UNLOCK_SKILL:
unlock_skill(inout->skill_list, ssi->value[j]);
break;
}
}
}
sort_skill_list(inout->skill_list, (LPINT)mi.skill_list);
if (in_party_or_standby)
{
ii = check_equippable(mi.weapon, ri, inout->ability_list);
if (ii != NULL)
{
inout->weapon = ii->item;
inout->actual_atk += ii->add_atk;
inout->actual_def += ii->add_def;
inout->actual_agi += ii->add_agi;
inout->actual_int += ii->add_int;
}
}
if (interrupt && in_party_or_standby)
{
inout->current_hp = inout->dead ? 0 : MINMAX(mi.current_hp, 1, inout->actual_hp);
inout->current_mp = MINMAX(mi.current_mp, 0, inout->actual_mp);
}
else
{
inout->current_hp = inout->actual_hp;
inout->current_mp = inout->actual_mp;
}
inout->name = mi.name;
inout->master = mi.master;
if (player_name && (inout->master.byte_code[0] == 0xFF))
inout->master = *player_name;
for (i = 0; i < DQMJ_PARENT_NUM; i++)
{
inout->parent_race[i] = lookup_race_info(mi.parent_race[i])->race;
inout->parent_name[i] = mi.parent_name[i];
inout->parent_master[i] = mi.parent_master[i];
}
if ((inout->parent_race[DQMJ_PARENT_FATHER] == 0x00) || (inout->parent_race[DQMJ_PARENT_MOTHER] == 0x00))
{
EMPTY_NAME(inout->parent_name[DQMJ_PARENT_FATHER]);
EMPTY_NAME(inout->parent_name[DQMJ_PARENT_MOTHER]);
inout->parent_race[DQMJ_PARENT_FATHER] = 0x00;
inout->parent_race[DQMJ_PARENT_MOTHER] = 0x00;
return TRUE;
}
for (i = 0; i < DQMJ_PARENT_NUM; i++)
{
if (inout->parent_master[i].byte_code[0] == 0xFF)
inout->parent_master[i] = UNKNOWN_MASTER;
inout->grandparent_race[i][DQMJ_PARENT_FATHER] = lookup_race_info(mi.grandparent_race[i][DQMJ_PARENT_FATHER])->race;
inout->grandparent_race[i][DQMJ_PARENT_MOTHER] = lookup_race_info(mi.grandparent_race[i][DQMJ_PARENT_MOTHER])->race;
if ((inout->grandparent_race[i][DQMJ_PARENT_FATHER] == 0x00) || (inout->grandparent_race[i][DQMJ_PARENT_MOTHER] == 0x00))
{
inout->grandparent_race[i][DQMJ_PARENT_FATHER] = 0x00;
inout->grandparent_race[i][DQMJ_PARENT_MOTHER] = 0x00;
}
}
return TRUE;
}
/* セーブファイルを開く */
HDQMJSAVE DQMJSaveOpenFile(LPCTSTR file_path)
{
BOOL ok;
HANDLE file;
struct save_info sav;
HDQMJSAVE handle;
if ((file_path == NULL) || (*file_path == TEXT('\0')))
return NULL;
file = CreateFile(file_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (file == INVALID_HANDLE_VALUE)
return NULL;
ZeroMemory(&sav, sizeof(sav));
ok = read_save_file(file, &sav);
CloseHandle(file);
if (ok)
{
handle = HeapAlloc(GetProcessHeap(), 0, sizeof(sav));
if (handle != NULL)
{
CopyMemory(handle, &sav, sizeof(sav));
return handle;
}
}
free_save_info(&sav);
return NULL;
}
/* セーブファイルを閉じる */
BOOL DQMJSaveCloseFile(HDQMJSAVE handle)
{
if ((handle == NULL) || (handle == INVALID_HANDLE_VALUE))
return FALSE;
free_save_info((struct save_info *)handle);
HeapFree(GetProcessHeap(), 0, handle);
return TRUE;
}
/* 名前を付けてセーブファイルを保存 */
BOOL DQMJSaveSaveToFile(HDQMJSAVE handle, LPCTSTR file_path, BOOL as_raw)
{
BOOL ok;
HANDLE file;
if ((handle == NULL) || (handle == INVALID_HANDLE_VALUE))
return FALSE;
if ((file_path == NULL) || (*file_path == TEXT('\0')))
return FALSE;
file = CreateFile(file_path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (file == INVALID_HANDLE_VALUE)
return FALSE;
ok = write_save_file(file, (struct save_info *)handle, as_raw);
CloseHandle(file);
return ok;
}
/* セーブデータファイルフォーマットを取得 */
int DQMJSaveQueryFileFormat(HDQMJSAVE handle)
{
if ((handle == NULL) || (handle == INVALID_HANDLE_VALUE))
return DQMJ_FORMAT_INVALID;
return ((struct save_info *)handle)->format;
}
/* セーブファイルから一括(ロード画面表示用)情報を取得 */
BOOL DQMJSaveQueryBriefing(HDQMJSAVE handle, DQMJ_SAVE_BRIEFING *out)
{
int i;
struct save_fmt_header *header;
if ((handle == NULL) || (handle == INVALID_HANDLE_VALUE) || (out == NULL))
return FALSE;
header = GET_HEADER(handle);
out->interrupt = IS_INTR(handle);
out->place_id = header->place_id;
out->party_member_num = header->party_member_num;
out->story_progress = max(header->story_progress / 5, 10);
COPY_NAME(out->player_name, header->player_name);
timestamp_to_hms(header->play_time, &out->play_time_hour, &out->play_time_min, &out->play_time_sec);
for (i = 0; i < DQMJ_MEMBER_MAX; i++)
{
out->party_member_race[i] = header->party_member_race[i];
out->party_member_level[i] = header->party_member_level[i];
COPY_NAME(out->party_member_name[i], header->party_member_name[i]);
}
return TRUE;
}
/* セーブファイルからプレイ情報を取得 */
BOOL DQMJSaveQueryPlayInfo(HDQMJSAVE handle, DQMJ_SAVE_PLAY_INFO *out)
{
int i;