From 73bead714278b2be99131e50cd90f38b7e6153ef Mon Sep 17 00:00:00 2001 From: Stewart Marshall Date: Fri, 16 May 2014 02:19:05 +1200 Subject: [PATCH] first git commit --- .classpath | 6 + .gitignore | 23 + .project | 17 + .settings/org.eclipse.core.resources.prefs | 2 + .settings/org.eclipse.jdt.core.prefs | 291 + .settings/org.eclipse.jdt.ui.prefs | 3 + README.md | 0 settings/balanced.rnqs | 1 + settings/classic.rnqs | 1 + settings/randomizer_race.rnqs | 1 + settings/super_randomizer_race.rnqs | 1 + settings/trainers_only.rnqs | 1 + src/com/dabomstew/pkrandom/CodeTweaks.java | 9 + src/com/dabomstew/pkrandom/FileFunctions.java | 177 + src/com/dabomstew/pkrandom/RandomSource.java | 162 + src/com/dabomstew/pkrandom/RomFunctions.java | 369 ++ .../dabomstew/pkrandom/config/Generation4.tbl | 1148 ++++ .../dabomstew/pkrandom/config/Generation5.tbl | 46 + .../dabomstew/pkrandom/config/gameboy_jap.tbl | 179 + .../dabomstew/pkrandom/config/gba_english.tbl | 159 + src/com/dabomstew/pkrandom/config/gba_jap.tbl | 254 + .../pkrandom/config/gen1_offsets.ini | 707 +++ .../pkrandom/config/gen2_offsets.ini | 604 ++ .../pkrandom/config/gen3_offsets.ini | 1806 ++++++ .../pkrandom/config/gen4_offsets.ini | 539 ++ .../pkrandom/config/gen5_offsets.ini | 334 ++ .../pkrandom/config/green_translation.tbl | 66 + .../dabomstew/pkrandom/config/gsc_english.tbl | 96 + .../dabomstew/pkrandom/config/gsc_espita.tbl | 122 + .../dabomstew/pkrandom/config/gsc_freger.tbl | 115 + .../dabomstew/pkrandom/config/nicknames.txt | 20 + .../dabomstew/pkrandom/config/rby_english.tbl | 88 + .../dabomstew/pkrandom/config/rby_espita.tbl | 119 + .../dabomstew/pkrandom/config/rby_freger.tbl | 112 + .../pkrandom/config/trainerclasses.txt | 83 + .../pkrandom/config/trainernames.txt | 84 + .../dabomstew/pkrandom/config/vietcrystal.tbl | 54 + .../dabomstew/pkrandom/gui/AboutDialog.form | 117 + .../dabomstew/pkrandom/gui/AboutDialog.java | 154 + .../dabomstew/pkrandom/gui/Bundle.properties | 280 + .../pkrandom/gui/CodeTweaksDialog.form | 142 + .../pkrandom/gui/CodeTweaksDialog.java | 180 + .../pkrandom/gui/GenerationLimitDialog.form | 314 + .../pkrandom/gui/GenerationLimitDialog.java | 448 ++ .../gui/InvalidSupplementFilesException.java | 36 + .../pkrandom/gui/OperationDialog.form | 79 + .../pkrandom/gui/OperationDialog.java | 125 + .../pkrandom/gui/PresetFileFilter.java | 57 + .../pkrandom/gui/PresetLoadDialog.form | 197 + .../pkrandom/gui/PresetLoadDialog.java | 564 ++ .../pkrandom/gui/PresetMakeDialog.form | 162 + .../pkrandom/gui/PresetMakeDialog.java | 239 + .../dabomstew/pkrandom/gui/QSFileFilter.java | 57 + src/com/dabomstew/pkrandom/gui/ROMFilter.java | 58 + .../dabomstew/pkrandom/gui/RandomizerGUI.form | 2449 ++++++++ .../dabomstew/pkrandom/gui/RandomizerGUI.java | 5307 +++++++++++++++++ .../pkrandom/gui/UpdateCheckThread.java | 75 + .../pkrandom/gui/UpdateFoundDialog.form | 105 + .../pkrandom/gui/UpdateFoundDialog.java | 281 + src/com/dabomstew/pkrandom/gui/loading.gif | Bin 0 -> 3208 bytes src/com/dabomstew/pkrandom/newnds/CRC16.java | 66 + .../dabomstew/pkrandom/newnds/NDSFile.java | 122 + src/com/dabomstew/pkrandom/newnds/NDSRom.java | 699 +++ .../dabomstew/pkrandom/newnds/NDSY9Entry.java | 147 + .../pkrandom/patches/crystal_en_bwxp.ips | Bin 0 -> 1552 bytes .../dabomstew/pkrandom/patches/gs_en_bwxp.ips | Bin 0 -> 1552 bytes .../dabomstew/pkrandom/patches/rb_en_bwxp.ips | Bin 0 -> 1345 bytes .../pkrandom/patches/rb_en_critrate.ips | Bin 0 -> 80 bytes .../pkrandom/patches/rb_en_xaccnerf.ips | Bin 0 -> 48 bytes .../pkrandom/patches/yellow_en_bwxp.ips | Bin 0 -> 1345 bytes .../pkrandom/patches/yellow_en_critrate.ips | Bin 0 -> 80 bytes .../pkrandom/patches/yellow_en_xaccnerf.ips | Bin 0 -> 48 bytes .../dabomstew/pkrandom/pokemon/Encounter.java | 43 + .../pkrandom/pokemon/EncounterSet.java | 42 + .../dabomstew/pkrandom/pokemon/Evolution.java | 82 + .../pkrandom/pokemon/EvolutionType.java | 42 + .../dabomstew/pkrandom/pokemon/ExpCurve.java | 43 + .../pkrandom/pokemon/GenRestrictions.java | 88 + .../pkrandom/pokemon/IngameTrade.java | 17 + .../dabomstew/pkrandom/pokemon/ItemList.java | 74 + src/com/dabomstew/pkrandom/pokemon/Move.java | 47 + .../pkrandom/pokemon/MoveLearnt.java | 35 + .../dabomstew/pkrandom/pokemon/Pokemon.java | 221 + .../dabomstew/pkrandom/pokemon/Trainer.java | 92 + .../pkrandom/pokemon/TrainerPokemon.java | 44 + src/com/dabomstew/pkrandom/pokemon/Type.java | 60 + .../romhandlers/AbstractDSRomHandler.java | 406 ++ .../romhandlers/AbstractGBRomHandler.java | 135 + .../romhandlers/AbstractRomHandler.java | 2841 +++++++++ .../pkrandom/romhandlers/Gen1RomHandler.java | 2543 ++++++++ .../pkrandom/romhandlers/Gen2RomHandler.java | 2517 ++++++++ .../pkrandom/romhandlers/Gen3RomHandler.java | 2674 +++++++++ .../pkrandom/romhandlers/Gen4RomHandler.java | 2808 +++++++++ .../pkrandom/romhandlers/Gen5RomHandler.java | 2705 +++++++++ .../pkrandom/romhandlers/NARCContents.java | 36 + .../pkrandom/romhandlers/RomHandler.java | 392 ++ src/cuecompressors/BLZCoder.java | 576 ++ src/dsdecmp/HexInputStream.java | 240 + src/dsdecmp/InvalidFileException.java | 40 + src/dsdecmp/JavaDSDecmp.java | 167 + src/pptxt/PPTxtHandler.java | 414 ++ src/thenewpoketext/PokeTextData.java | 205 + src/thenewpoketext/TextToPoke.java | 190 + src/thenewpoketext/UnicodeParser.java | 42 + 104 files changed, 40120 insertions(+) create mode 100755 .classpath create mode 100755 .gitignore create mode 100755 .project create mode 100755 .settings/org.eclipse.core.resources.prefs create mode 100755 .settings/org.eclipse.jdt.core.prefs create mode 100755 .settings/org.eclipse.jdt.ui.prefs create mode 100755 README.md create mode 100755 settings/balanced.rnqs create mode 100755 settings/classic.rnqs create mode 100755 settings/randomizer_race.rnqs create mode 100755 settings/super_randomizer_race.rnqs create mode 100755 settings/trainers_only.rnqs create mode 100755 src/com/dabomstew/pkrandom/CodeTweaks.java create mode 100755 src/com/dabomstew/pkrandom/FileFunctions.java create mode 100755 src/com/dabomstew/pkrandom/RandomSource.java create mode 100755 src/com/dabomstew/pkrandom/RomFunctions.java create mode 100755 src/com/dabomstew/pkrandom/config/Generation4.tbl create mode 100755 src/com/dabomstew/pkrandom/config/Generation5.tbl create mode 100755 src/com/dabomstew/pkrandom/config/gameboy_jap.tbl create mode 100755 src/com/dabomstew/pkrandom/config/gba_english.tbl create mode 100755 src/com/dabomstew/pkrandom/config/gba_jap.tbl create mode 100755 src/com/dabomstew/pkrandom/config/gen1_offsets.ini create mode 100755 src/com/dabomstew/pkrandom/config/gen2_offsets.ini create mode 100755 src/com/dabomstew/pkrandom/config/gen3_offsets.ini create mode 100755 src/com/dabomstew/pkrandom/config/gen4_offsets.ini create mode 100755 src/com/dabomstew/pkrandom/config/gen5_offsets.ini create mode 100755 src/com/dabomstew/pkrandom/config/green_translation.tbl create mode 100755 src/com/dabomstew/pkrandom/config/gsc_english.tbl create mode 100755 src/com/dabomstew/pkrandom/config/gsc_espita.tbl create mode 100755 src/com/dabomstew/pkrandom/config/gsc_freger.tbl create mode 100755 src/com/dabomstew/pkrandom/config/nicknames.txt create mode 100755 src/com/dabomstew/pkrandom/config/rby_english.tbl create mode 100755 src/com/dabomstew/pkrandom/config/rby_espita.tbl create mode 100755 src/com/dabomstew/pkrandom/config/rby_freger.tbl create mode 100755 src/com/dabomstew/pkrandom/config/trainerclasses.txt create mode 100755 src/com/dabomstew/pkrandom/config/trainernames.txt create mode 100755 src/com/dabomstew/pkrandom/config/vietcrystal.tbl create mode 100755 src/com/dabomstew/pkrandom/gui/AboutDialog.form create mode 100755 src/com/dabomstew/pkrandom/gui/AboutDialog.java create mode 100755 src/com/dabomstew/pkrandom/gui/Bundle.properties create mode 100755 src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.form create mode 100755 src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.java create mode 100755 src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.form create mode 100755 src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.java create mode 100755 src/com/dabomstew/pkrandom/gui/InvalidSupplementFilesException.java create mode 100755 src/com/dabomstew/pkrandom/gui/OperationDialog.form create mode 100755 src/com/dabomstew/pkrandom/gui/OperationDialog.java create mode 100755 src/com/dabomstew/pkrandom/gui/PresetFileFilter.java create mode 100755 src/com/dabomstew/pkrandom/gui/PresetLoadDialog.form create mode 100755 src/com/dabomstew/pkrandom/gui/PresetLoadDialog.java create mode 100755 src/com/dabomstew/pkrandom/gui/PresetMakeDialog.form create mode 100755 src/com/dabomstew/pkrandom/gui/PresetMakeDialog.java create mode 100755 src/com/dabomstew/pkrandom/gui/QSFileFilter.java create mode 100755 src/com/dabomstew/pkrandom/gui/ROMFilter.java create mode 100755 src/com/dabomstew/pkrandom/gui/RandomizerGUI.form create mode 100755 src/com/dabomstew/pkrandom/gui/RandomizerGUI.java create mode 100755 src/com/dabomstew/pkrandom/gui/UpdateCheckThread.java create mode 100755 src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.form create mode 100755 src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.java create mode 100755 src/com/dabomstew/pkrandom/gui/loading.gif create mode 100755 src/com/dabomstew/pkrandom/newnds/CRC16.java create mode 100755 src/com/dabomstew/pkrandom/newnds/NDSFile.java create mode 100755 src/com/dabomstew/pkrandom/newnds/NDSRom.java create mode 100755 src/com/dabomstew/pkrandom/newnds/NDSY9Entry.java create mode 100755 src/com/dabomstew/pkrandom/patches/crystal_en_bwxp.ips create mode 100755 src/com/dabomstew/pkrandom/patches/gs_en_bwxp.ips create mode 100755 src/com/dabomstew/pkrandom/patches/rb_en_bwxp.ips create mode 100755 src/com/dabomstew/pkrandom/patches/rb_en_critrate.ips create mode 100755 src/com/dabomstew/pkrandom/patches/rb_en_xaccnerf.ips create mode 100755 src/com/dabomstew/pkrandom/patches/yellow_en_bwxp.ips create mode 100755 src/com/dabomstew/pkrandom/patches/yellow_en_critrate.ips create mode 100755 src/com/dabomstew/pkrandom/patches/yellow_en_xaccnerf.ips create mode 100755 src/com/dabomstew/pkrandom/pokemon/Encounter.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/EncounterSet.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/Evolution.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/EvolutionType.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/ExpCurve.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/GenRestrictions.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/IngameTrade.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/ItemList.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/Move.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/MoveLearnt.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/Pokemon.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/Trainer.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/TrainerPokemon.java create mode 100755 src/com/dabomstew/pkrandom/pokemon/Type.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/AbstractDSRomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/AbstractGBRomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/AbstractRomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/Gen1RomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/Gen2RomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/Gen3RomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/Gen4RomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/Gen5RomHandler.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/NARCContents.java create mode 100755 src/com/dabomstew/pkrandom/romhandlers/RomHandler.java create mode 100755 src/cuecompressors/BLZCoder.java create mode 100755 src/dsdecmp/HexInputStream.java create mode 100755 src/dsdecmp/InvalidFileException.java create mode 100755 src/dsdecmp/JavaDSDecmp.java create mode 100755 src/pptxt/PPTxtHandler.java create mode 100755 src/thenewpoketext/PokeTextData.java create mode 100755 src/thenewpoketext/TextToPoke.java create mode 100755 src/thenewpoketext/UnicodeParser.java diff --git a/.classpath b/.classpath new file mode 100755 index 000000000..07ca123c6 --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100755 index 000000000..608358cd8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +*.NDS +*.nds +*.GB +*.gb +*.GBC +*.gbc +*.GBA +*.gba +*.RNDP +*.rndp +tmp_* +*.sgm +*.sav +*.rtc +*.sgb +*.sn* +*.rar +*.zip +config.ini +*.pkm +error_*.txt +src/com/dabomstew/pkrandom/localtests +bin diff --git a/.project b/.project new file mode 100755 index 000000000..a5742bed3 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + universal-pokemon-randomizer + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100755 index 000000000..4824b8026 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100755 index 000000000..27ae71be0 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,291 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=80 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100755 index 000000000..fc6d66ce4 --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +formatter_profile=_PokeRandomizer +formatter_settings_version=12 diff --git a/README.md b/README.md new file mode 100755 index 000000000..e69de29bb diff --git a/settings/balanced.rnqs b/settings/balanced.rnqs new file mode 100755 index 000000000..980a7cd48 --- /dev/null +++ b/settings/balanced.rnqs @@ -0,0 +1 @@ + TBBhkCO4B8QH0AQLHIAICFBQCBAAAAAAAAAAAE1Bva2Vtb24gQmxhY2sgMiAoVSnuSji4ZSd9HahDq+5llTzy \ No newline at end of file diff --git a/settings/classic.rnqs b/settings/classic.rnqs new file mode 100755 index 000000000..75f3d4782 --- /dev/null +++ b/settings/classic.rnqs @@ -0,0 +1 @@ + TBBgkCO4B8QH0AQQgJAACFBRABAAAAAAAAAAAE1Bva2Vtb24gQmxhY2sgMiAoVSkNT2VRZSd9HahDq+5llTzy \ No newline at end of file diff --git a/settings/randomizer_race.rnqs b/settings/randomizer_race.rnqs new file mode 100755 index 000000000..f9bb458b9 --- /dev/null +++ b/settings/randomizer_race.rnqs @@ -0,0 +1 @@ + T5ph0Eu4B8QH0ARGGJAgECQk9BAAAAAAAAAAAE1Bva2Vtb24gQmxhY2sgMiAoVSmJEgBbZSd9HahDq+5llTzy \ No newline at end of file diff --git a/settings/super_randomizer_race.rnqs b/settings/super_randomizer_race.rnqs new file mode 100755 index 000000000..95d1b8c70 --- /dev/null +++ b/settings/super_randomizer_race.rnqs @@ -0,0 +1 @@ + T5qJyEu4B8QH0ARGGJAgECQk9AQAAAAAAAAAAE1Bva2Vtb24gQmxhY2sgMiAoVSkdR0elZSd9HahDq+5llTzy \ No newline at end of file diff --git a/settings/trainers_only.rnqs b/settings/trainers_only.rnqs new file mode 100755 index 000000000..ff9096291 --- /dev/null +++ b/settings/trainers_only.rnqs @@ -0,0 +1 @@ + TwBgEBO4B8QH0AQTdRAABFBRABAAAAAAAAAAAE1Bva2Vtb24gQmxhY2sgMiAoVSklePsNZSd9HahDq+5llTzy \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/CodeTweaks.java b/src/com/dabomstew/pkrandom/CodeTweaks.java new file mode 100755 index 000000000..3b8d7707f --- /dev/null +++ b/src/com/dabomstew/pkrandom/CodeTweaks.java @@ -0,0 +1,9 @@ +package com.dabomstew.pkrandom; + +public class CodeTweaks { + + public static final int BW_EXP_PATCH = 1; + public static final int NERF_X_ACCURACY = 2; + public static final int FIX_CRIT_RATE = 4; + +} diff --git a/src/com/dabomstew/pkrandom/FileFunctions.java b/src/com/dabomstew/pkrandom/FileFunctions.java new file mode 100755 index 000000000..5e2b69c64 --- /dev/null +++ b/src/com/dabomstew/pkrandom/FileFunctions.java @@ -0,0 +1,177 @@ +package com.dabomstew.pkrandom; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.List; + +public class FileFunctions { + + public static File fixFilename(File original, String defaultExtension) { + return fixFilename(original, defaultExtension, null); + } + + // Behavior: + // if file has no extension, add defaultExtension + // if there are banned extensions & file has a banned extension, replace + // with defaultExtension + // else, leave as is + public static File fixFilename(File original, String defaultExtension, + List bannedExtensions) { + String filename = original.getName(); + if (filename.lastIndexOf('.') >= filename.length() - 5 + && filename.lastIndexOf('.') != filename.length() - 1 + && filename.length() > 4 && filename.lastIndexOf('.') != -1) { + // valid extension, read it off + String ext = filename.substring(filename.lastIndexOf('.') + 1) + .toLowerCase(); + if (bannedExtensions != null && bannedExtensions.contains(ext)) { + // replace with default + filename = filename.substring(0, filename.lastIndexOf('.') + 1) + + defaultExtension; + } + // else no change + } else { + // add extension + filename += "." + defaultExtension; + } + return new File(original.getAbsolutePath().replace(original.getName(), + "") + + filename); + } + + private static List overrideFiles = Arrays.asList(new String[] { + "trainerclasses.txt", "trainernames.txt", "nicknames.txt" }); + + public static boolean configExists(String filename) { + if (overrideFiles.contains(filename)) { + File fh = new File("./" + filename); + if (fh.exists() && fh.canRead()) { + return true; + } + } + return FileFunctions.class + .getResource("/com/dabomstew/pkrandom/config/" + filename) != null; + } + + public static InputStream openConfig(String filename) + throws FileNotFoundException { + if (overrideFiles.contains(filename)) { + File fh = new File("./" + filename); + if (fh.exists() && fh.canRead()) { + return new FileInputStream(fh); + } + } + return FileFunctions.class + .getResourceAsStream("/com/dabomstew/pkrandom/config/" + + filename); + } + + public static byte[] getCodeTweakFile(String filename) throws IOException { + InputStream is = FileFunctions.class + .getResourceAsStream("/com/dabomstew/pkrandom/patches/" + + filename); + byte[] buf = new byte[is.available()]; + is.read(buf); + is.close(); + return buf; + } + + public static byte[] downloadFile(String url) throws IOException { + BufferedInputStream in = new BufferedInputStream( + new URL(url).openStream()); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + int count; + while ((count = in.read(buf, 0, 1024)) != -1) { + out.write(buf, 0, count); + } + in.close(); + byte[] output = out.toByteArray(); + return output; + } + + public static void applyPatch(byte[] rom, String patchName) + throws IOException { + byte[] patch = getCodeTweakFile(patchName + ".ips"); + // check sig + int patchlen = patch.length; + if (patchlen < 8 || patch[0] != 'P' || patch[1] != 'A' + || patch[2] != 'T' || patch[3] != 'C' || patch[4] != 'H') { + System.out.println("not a valid IPS file"); + return; + } + + // records + int offset = 5; + while (offset + 2 < patchlen) { + int writeOffset = readIPSOffset(patch, offset); + if (writeOffset == 0x454f46) { + // eof, done + System.out.println("patch successful"); + return; + } + offset += 3; + if (offset + 1 >= patchlen) { + // error + System.out.println("abrupt ending to IPS file, entry cut off before size"); + return; + } + int size = readIPSSize(patch, offset); + offset += 2; + if (size == 0) { + // RLE + if (offset + 1 >= patchlen) { + // error + System.out.println("abrupt ending to IPS file, entry cut off before RLE size"); + return; + } + int rleSize = readIPSSize(patch, offset); + if(writeOffset + rleSize > rom.length) { + // error + System.out.println("trying to patch data past the end of the ROM file"); + return; + } + offset += 2; + if (offset >= patchlen) { + // error + System.out.println("abrupt ending to IPS file, entry cut off before RLE byte"); + return; + } + byte rleByte = patch[offset++]; + for (int i = writeOffset; i < writeOffset + rleSize; i++) { + rom[i] = rleByte; + } + } else { + if (offset + size > patchlen) { + // error + System.out.println("abrupt ending to IPS file, entry cut off before end of data block"); + return; + } + if(writeOffset + size > rom.length) { + // error + System.out.println("trying to patch data past the end of the ROM file"); + return; + } + System.arraycopy(patch, offset, rom, writeOffset, size); + offset += size; + } + } + System.out.println("improperly terminated IPS file"); + } + + private static int readIPSOffset(byte[] data, int offset) { + return ((data[offset] & 0xFF) << 16) | ((data[offset + 1] & 0xFF) << 8) + | (data[offset + 2] & 0xFF); + } + + private static int readIPSSize(byte[] data, int offset) { + return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF); + } +} diff --git a/src/com/dabomstew/pkrandom/RandomSource.java b/src/com/dabomstew/pkrandom/RandomSource.java new file mode 100755 index 000000000..25364d9e2 --- /dev/null +++ b/src/com/dabomstew/pkrandom/RandomSource.java @@ -0,0 +1,162 @@ +package com.dabomstew.pkrandom; + +/*----------------------------------------------------------------------------*/ +/*-- RandomSource.java - functions as a centralized source of randomness --*/ +/*-- to allow the same seed to produce the same random --*/ +/*-- ROM consistently. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.security.SecureRandom; +import java.util.Random; + +public class RandomSource { + + private static Random source = new Random(); + private static int calls = 0; + private static Random instance = new RandomSourceInstance(); + + public static void reset() { + source = new Random(); + calls = 0; + } + + public static void seed(long seed) { + source.setSeed(seed); + calls = 0; + } + + public static double random() { + calls++; + return source.nextDouble(); + } + + public static int nextInt(int size) { + calls++; + return source.nextInt(size); + } + + public static void nextBytes(byte[] bytes) { + calls++; + source.nextBytes(bytes); + } + + public static int nextInt() { + calls++; + return source.nextInt(); + } + + public static long nextLong() { + calls++; + return source.nextLong(); + } + + public static boolean nextBoolean() { + calls++; + return source.nextBoolean(); + } + + public static float nextFloat() { + calls++; + return source.nextFloat(); + } + + public static double nextDouble() { + calls++; + return source.nextDouble(); + } + + public static synchronized double nextGaussian() { + calls++; + return source.nextGaussian(); + } + + public static long pickSeed() { + long value = 0; + byte[] by = SecureRandom.getSeed(8); + for (int i = 0; i < by.length; i++) { + value += ((long) by[i] & 0xffL) << (8 * i); + } + return value; + } + + public static Random instance() { + return instance; + } + + public static int callsSinceSeed() { + return calls; + } + + private static class RandomSourceInstance extends Random { + + /** + * + */ + private static final long serialVersionUID = -4876737183441746322L; + + @Override + public synchronized void setSeed(long seed) { + RandomSource.seed(seed); + } + + @Override + public void nextBytes(byte[] bytes) { + RandomSource.nextBytes(bytes); + } + + @Override + public int nextInt() { + return RandomSource.nextInt(); + } + + @Override + public int nextInt(int n) { + return RandomSource.nextInt(n); + } + + @Override + public long nextLong() { + return RandomSource.nextLong(); + } + + @Override + public boolean nextBoolean() { + return RandomSource.nextBoolean(); + } + + @Override + public float nextFloat() { + return RandomSource.nextFloat(); + } + + @Override + public double nextDouble() { + return RandomSource.nextDouble(); + } + + @Override + public synchronized double nextGaussian() { + return RandomSource.nextGaussian(); + } + + } +} diff --git a/src/com/dabomstew/pkrandom/RomFunctions.java b/src/com/dabomstew/pkrandom/RomFunctions.java new file mode 100755 index 000000000..9100d29e2 --- /dev/null +++ b/src/com/dabomstew/pkrandom/RomFunctions.java @@ -0,0 +1,369 @@ +package com.dabomstew.pkrandom; + +/*----------------------------------------------------------------------------*/ +/*-- RomFunctions.java - contains functions useful throughout the program. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.romhandlers.RomHandler; + +public class RomFunctions { + + public static final boolean[] bannedRandomMoves = new boolean[560], + bannedForDamagingMove = new boolean[560]; + static { + bannedRandomMoves[144] = true; // Transform, glitched in RBY + bannedRandomMoves[165] = true; // Struggle, self explanatory + + bannedForDamagingMove[120] = true; // SelfDestruct + bannedForDamagingMove[138] = true; // Dream Eater + bannedForDamagingMove[153] = true; // Explosion + bannedForDamagingMove[173] = true; // Snore + bannedForDamagingMove[206] = true; // False Swipe + bannedForDamagingMove[248] = true; // Future Sight + bannedForDamagingMove[252] = true; // Fake Out + bannedForDamagingMove[264] = true; // Focus Punch + bannedForDamagingMove[353] = true; // Doom Desire + bannedForDamagingMove[364] = true; // Feint + bannedForDamagingMove[387] = true; // Last Resort + bannedForDamagingMove[389] = true; // Sucker Punch + + // new 160 + bannedForDamagingMove[132] = true; // Constrict, overly weak + bannedForDamagingMove[99] = true; // Rage, lock-in in gen1 + bannedForDamagingMove[205] = true; // Rollout, lock-in + bannedForDamagingMove[301] = true; // Ice Ball, Rollout clone + + // make sure these cant roll + bannedForDamagingMove[39] = true; // Sonicboom + bannedForDamagingMove[82] = true; // Dragon Rage + bannedForDamagingMove[32] = true; // Horn Drill + bannedForDamagingMove[12] = true; // Guillotine + bannedForDamagingMove[90] = true; // Fissure + bannedForDamagingMove[329] = true; // Sheer Cold + + } + + public static Set getBasicOrNoCopyPokemon(RomHandler baseRom) { + List allPokes = baseRom.getPokemon(); + List evos = baseRom.getEvolutions(); + + Set doCopyPokes = new TreeSet(); + for (Evolution e : evos) { + if (e.carryStats) { + doCopyPokes.add(allPokes.get(e.to)); + } + } + Set dontCopyPokes = new TreeSet(); + for (Pokemon pkmn : allPokes) { + if (pkmn != null) { + if (doCopyPokes.contains(pkmn) == false) { + dontCopyPokes.add(pkmn); + } + } + } + return dontCopyPokes; + } + + public static Set getFirstEvolutions(RomHandler baseRom) { + List allPokes = baseRom.getPokemon(); + List evos = baseRom.getEvolutions(); + Set basicPokemon = getBasicOrNoCopyPokemon(baseRom); + + Set firstEvos = new TreeSet(); + for (Evolution e : evos) { + if (basicPokemon.contains(allPokes.get(e.from))) { + firstEvos.add(allPokes.get(e.to)); + } + } + return firstEvos; + } + + public static Set getSecondEvolutions(RomHandler baseRom) { + List allPokes = baseRom.getPokemon(); + List evos = baseRom.getEvolutions(); + Set firstEvos = getFirstEvolutions(baseRom); + + Set secondEvos = new TreeSet(); + for (Evolution e : evos) { + if (firstEvos.contains(allPokes.get(e.from))) { + secondEvos.add(allPokes.get(e.to)); + } + } + return secondEvos; + } + + public static boolean pokemonHasEvo(RomHandler baseRom, Pokemon pkmn) { + List evos = baseRom.getEvolutions(); + for (Evolution evo : evos) { + if (evo.from == pkmn.number) { + return true; + } + } + return false; + } + + public static Pokemon evolvesFrom(RomHandler baseRom, Pokemon pkmn) { + List evos = baseRom.getEvolutions(); + for (Evolution evo : evos) { + if (evo.to == pkmn.number) { + return baseRom.getPokemon().get(evo.from); + } + } + return null; + } + + public static String camelCase(String original) { + char[] string = original.toLowerCase().toCharArray(); + boolean docap = true; + for (int j = 0; j < string.length; j++) { + char current = string[j]; + if (docap && Character.isLetter(current)) { + string[j] = Character.toUpperCase(current); + docap = false; + } else { + if (!docap && !Character.isLetter(current) && current != '\'') { + docap = true; + } + } + } + return new String(string); + } + + public static int freeSpaceFinder(byte[] rom, byte freeSpace, int amount, + int offset) { + // by default align to 4 bytes to make sure things don't break + return freeSpaceFinder(rom, freeSpace, amount, offset, true); + } + + public static int freeSpaceFinder(byte[] rom, byte freeSpace, int amount, + int offset, boolean longAligned) { + if (!longAligned) { + // Find 2 more than necessary and return 2 into it, + // to preserve stuff like FF terminators for strings + // 161: and FFFF terminators for movesets + byte[] searchNeedle = new byte[amount + 2]; + for (int i = 0; i < amount + 2; i++) { + searchNeedle[i] = freeSpace; + } + return searchForFirst(rom, offset, searchNeedle) + 2; + } else { + // Find 5 more than necessary and return into it as necessary for + // 4-alignment, + // to preserve stuff like FF terminators for strings + // 161: and FFFF terminators for movesets + byte[] searchNeedle = new byte[amount + 5]; + for (int i = 0; i < amount + 5; i++) { + searchNeedle[i] = freeSpace; + } + return (searchForFirst(rom, offset, searchNeedle) + 5) & ~3; + } + } + + public static List search(byte[] haystack, byte[] needle) { + return search(haystack, 0, needle); + } + + public static List search(byte[] haystack, int beginOffset, + byte[] needle) { + int currentMatchStart = beginOffset; + int currentCharacterPosition = 0; + + int docSize = haystack.length; + int needleSize = needle.length; + + int[] toFillTable = buildKMPSearchTable(needle); + List results = new ArrayList(); + + while ((currentMatchStart + currentCharacterPosition) < docSize) { + + if (needle[currentCharacterPosition] == (haystack[currentCharacterPosition + + currentMatchStart])) { + currentCharacterPosition = currentCharacterPosition + 1; + + if (currentCharacterPosition == (needleSize)) { + results.add(currentMatchStart); + currentCharacterPosition = 0; + currentMatchStart = currentMatchStart + needleSize; + + } + + } else { + currentMatchStart = currentMatchStart + + currentCharacterPosition + - toFillTable[currentCharacterPosition]; + + if (toFillTable[currentCharacterPosition] > -1) { + currentCharacterPosition = toFillTable[currentCharacterPosition]; + } + + else { + currentCharacterPosition = 0; + + } + + } + } + return results; + } + + public static int searchForFirst(byte[] haystack, int beginOffset, + byte[] needle) { + int currentMatchStart = beginOffset; + int currentCharacterPosition = 0; + + int docSize = haystack.length; + int needleSize = needle.length; + + int[] toFillTable = buildKMPSearchTable(needle); + + while ((currentMatchStart + currentCharacterPosition) < docSize) { + + if (needle[currentCharacterPosition] == (haystack[currentCharacterPosition + + currentMatchStart])) { + currentCharacterPosition = currentCharacterPosition + 1; + + if (currentCharacterPosition == (needleSize)) { + return currentMatchStart; + } + + } else { + currentMatchStart = currentMatchStart + + currentCharacterPosition + - toFillTable[currentCharacterPosition]; + + if (toFillTable[currentCharacterPosition] > -1) { + currentCharacterPosition = toFillTable[currentCharacterPosition]; + } + + else { + currentCharacterPosition = 0; + + } + + } + } + return -1; + } + + private static int[] buildKMPSearchTable(byte[] needle) { + int[] stable = new int[needle.length]; + int pos = 2; + int j = 0; + stable[0] = -1; + stable[1] = 0; + while (pos < needle.length) { + if (needle[pos - 1] == needle[j]) { + stable[pos] = j + 1; + pos++; + j++; + } else if (j > 0) { + j = stable[j]; + } else { + stable[pos] = 0; + pos++; + } + } + return stable; + } + + public static String rewriteDescriptionForNewLineSize(String moveDesc, + String newline, int lineSize, StringSizeDeterminer ssd) { + // We rewrite the description we're given based on some new chars per + // line. + moveDesc = moveDesc.replace("-" + newline, "").replace(newline, " "); + // Keep spatk/spdef as one word on one line + moveDesc = moveDesc.replace("Sp. Atk", "Sp__Atk"); + moveDesc = moveDesc.replace("Sp. Def", "Sp__Def"); + moveDesc = moveDesc.replace("SP. ATK", "SP__ATK"); + moveDesc = moveDesc.replace("SP. DEF", "SP__DEF"); + String[] words = moveDesc.split(" "); + StringBuilder fullDesc = new StringBuilder(); + StringBuilder thisLine = new StringBuilder(); + int currLineWC = 0; + int currLineCC = 0; + int linesWritten = 0; + for (int i = 0; i < words.length; i++) { + // Reverse the spatk/spdef preservation from above + words[i] = words[i].replace("SP__", "SP. "); + words[i] = words[i].replace("Sp__", "Sp. "); + int reqLength = ssd.lengthFor(words[i]); + if (currLineWC > 0) { + reqLength++; + } + if (currLineCC + reqLength <= lineSize) { + // add to current line + if (currLineWC > 0) { + thisLine.append(' '); + } + thisLine.append(words[i]); + currLineWC++; + currLineCC += reqLength; + } else { + // Save current line, if applicable + if (currLineWC > 0) { + if (linesWritten > 0) { + fullDesc.append(newline); + } + fullDesc.append(thisLine.toString()); + linesWritten++; + thisLine = new StringBuilder(); + } + // Start the new line + thisLine.append(words[i]); + currLineWC = 1; + currLineCC = ssd.lengthFor(words[i]); + } + } + + // If the last line has anything add it + if (currLineWC > 0) { + if (linesWritten > 0) { + fullDesc.append(newline); + } + fullDesc.append(thisLine.toString()); + linesWritten++; + } + + return fullDesc.toString(); + } + + public interface StringSizeDeterminer { + public int lengthFor(String encodedText); + } + + public static class StringLengthSD implements StringSizeDeterminer { + + @Override + public int lengthFor(String encodedText) { + return encodedText.length(); + } + + } + +} diff --git a/src/com/dabomstew/pkrandom/config/Generation4.tbl b/src/com/dabomstew/pkrandom/config/Generation4.tbl new file mode 100755 index 000000000..c4ca932dd --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/Generation4.tbl @@ -0,0 +1,1148 @@ +0000=\x0000 +0001=\x0001 +0002=ã +0003=ã‚ +0004=ム+0005=ã„ +0006=ã… +0007=ㆠ+0008=㇠+0009=㈠+000A=㉠+000B=㊠+000C=ã‹ +000D=㌠+000E=ã +000F=㎠+0010=ã +0011=ã +0012=ã‘ +0013=ã’ +0014=ã“ +0015=ã” +0016=ã• +0017=ã– +0018=ã— +0019=㘠+001A=ã™ +001B=ãš +001C=ã› +001D=㜠+001E=ã +001F=ãž +0020=㟠+0021=ã  +0022=ã¡ +0023=㢠+0024=㣠+0025=㤠+0026=㥠+0027=㦠+0028=㧠+0029=㨠+002A=ã© +002B=㪠+002C=ã« +002D=㬠+002E=ã­ +002F=ã® +0030=㯠+0031=ã° +0032=ã± +0033=ã² +0034=ã³ +0035=ã´ +0036=ãµ +0037=㶠+0038=ã· +0039=㸠+003A=ã¹ +003B=㺠+003C=ã» +003D=ã¼ +003E=ã½ +003F=ã¾ +0040=ã¿ +0041=ã‚€ +0042=ã‚ +0043=ã‚‚ +0044=ゃ +0045=ã‚„ +0046=ã‚… +0047=ゆ +0048=ょ +0049=よ +004A=ら +004B=ã‚Š +004C=ã‚‹ +004D=ã‚Œ +004E=ã‚ +004F=ã‚ +0050=ã‚’ +0051=ã‚“ +0052=ã‚¡ +0053=ã‚¢ +0054=ã‚£ +0055=イ +0056=ã‚¥ +0057=ウ +0058=ェ +0059=エ +005A=ã‚© +005B=オ +005C=ã‚« +005D=ガ +005E=ã‚­ +005F=ã‚® +0060=ク +0061=ã‚° +0062=ケ +0063=ゲ +0064=コ +0065=ã‚´ +0066=サ +0067=ザ +0068=ã‚· +0069=ジ +006A=ス +006B=ズ +006C=ã‚» +006D=ゼ +006E=ソ +006F=ゾ +0070=ã‚¿ +0071=ダ +0072=ム+0073=ヂ +0074=ッ +0075=ツ +0076=ヅ +0077=テ +0078=デ +0079=ト +007A=ド +007B=ナ +007C=ニ +007D=ヌ +007E=ム+007F=ノ +0080=ム+0081=ム+0082=パ +0083=ヒ +0084=ビ +0085=ピ +0086=フ +0087=ブ +0088=プ +0089=ヘ +008A=ベ +008B=ペ +008C=ホ +008D=ボ +008E=ム+008F=マ +0090=ミ +0091=ム +0092=メ +0093=モ +0094=ャ +0095=ヤ +0096=ュ +0097=ユ +0098=ョ +0099=ヨ +009A=ラ +009B=リ +009C=ル +009D=レ +009E=ロ +009F=ワ +00A0=ヲ +00A1=ン +00A2=ï¼ +00A3=1 +00A4=ï¼’ +00A5=3 +00A6=ï¼” +00A7=5 +00A8=ï¼– +00A9=ï¼— +00AA=8 +00AB=ï¼™ +00AC=A +00AD=ï¼¢ +00AE=ï¼£ +00AF=D +00B0=ï¼¥ +00B1=F +00B2=G +00B3=H +00B4=I +00B5=J +00B6=K +00B7=L +00B8=ï¼­ +00B9=ï¼® +00BA=O +00BB=ï¼° +00BC=ï¼± +00BD=ï¼² +00BE=ï¼³ +00BF=ï¼´ +00C0=ï¼µ +00C1=V +00C2=ï¼· +00C3=X +00C4=ï¼¹ +00C5=Z +00C6=ï½ +00C7=b +00C8=c +00C9=d +00CA=ï½… +00CB=f +00CC=g +00CD=h +00CE=i +00CF=j +00D0=k +00D1=l +00D2=ï½ +00D3=n +00D4=ï½ +00D5=ï½ +00D6=q +00D7=ï½’ +00D8=s +00D9=ï½” +00DA=u +00DB=ï½– +00DC=ï½— +00DD=x +00DE=ï½™ +00DF=z +00E0= (224) +00E1=ï¼ +00E2=? +00E3=〠+00E4=。 +00E5=… +00E6=・ +00E7=ï¼ +00E8=「 +00E9=〠+00EA=『 +00EB=〠+00EC=( +00ED=) +00EE=♂ +00EF=♀ +00F0=+ +00F1=ー +00F2=× +00F3=÷ +00F4== +00F5=~ +00F6=: +00F7=ï¼› +00F8=. +00F9=, +00FA=â™  +00FB=♣ +00FC=♥ +00FD=♦ +00FE=★ +00FF=â—Ž +0100=â—‹ +0101=â–¡ +0102=â–³ +0103=â—‡ +0104=ï¼  +0105=♪ +0106=% +0107=☀ +0108=☠+0109=☂ +010A=☃ +010B=\x010B +010C=\x010C +010D=\x010D +010E=\x010E +010F=⤴ +0110=⤵ +0111=\x0111 +0112=円 +0113=\x0113 +0114=\x0114 +0115=\x0115 +0116=✉ +0117=\x0117 +0118=\x0118 +0119=\x0119 +011A=\x011A +011B=↠+011C=↑ +011D=↓ +011E=→ +011F=\x011F +0120=& +0121=0 +0122=1 +0123=2 +0124=3 +0125=4 +0126=5 +0127=6 +0128=7 +0129=8 +012A=9 +012B=A +012C=B +012D=C +012E=D +012F=E +0130=F +0131=G +0132=H +0133=I +0134=J +0135=K +0136=L +0137=M +0138=N +0139=O +013A=P +013B=Q +013C=R +013D=S +013E=T +013F=U +0140=V +0141=W +0142=X +0143=Y +0144=Z +0145=a +0146=b +0147=c +0148=d +0149=e +014A=f +014B=g +014C=h +014D=i +014E=j +014F=k +0150=l +0151=m +0152=n +0153=o +0154=p +0155=q +0156=r +0157=s +0158=t +0159=u +015A=v +015B=w +015C=x +015D=y +015E=z +015F=À +0160=à +0161= +0162=\x0162 +0163=Ä +0164=\x0164 +0165=\x0165 +0166=Ç +0167=È +0168=É +0169=Ê +016A=Ë +016B=ÃŒ +016C=à +016D=ÃŽ +016E=à +016F=\x016F +0170=Ñ +0171=Ã’ +0172=Ó +0173=Ô +0174=\x0174 +0175=Ö +0176=× +0177=\x0177 +0178=Ù +0179=Ú +017A=Û +017B=Ãœ +017C=\x017C +017D=\x017D +017E=ß +017F=à +0180=á +0181=â +0182=\x0182 +0183=ä +0184=\x0184 +0185=\x0185 +0186=ç +0187=è +0188=é +0189=ê +018A=ë +018B=ì +018C=í +018D=î +018E=ï +018F=\x018F +0190=ñ +0191=ò +0192=ó +0193=ô +0194=\x0194 +0195=ö +0196=÷ +0197=\x0197 +0198=ù +0199=ú +019A=û +019B=ü +019C=\x019C +019D=\x019D +019E=\x019E +019F=Å’ +01A0=Å“ +01A1=\x01A1 +01A2=\x01A2 +01A3=ª +01A4=º +01A5=ᵉʳ +01A6=ʳᵉ +01A7=ʳ +01A8=Â¥ +01A9=¡ +01AA=¿ +01AB=! +01AC=? +01AD=, +01AE=. +01AF=… +01B0=· +01B1=/ +01B2=‘ +01B3=' +01B3=’ +01B4=“ +01B5=†+01B6=„ +01B7=« +01B8=» +01B9=( +01BA=) +01BB=♂ +01BC=♀ +01BD=+ +01BE=- +01BF=* +01C0=# +01C1== +01C2=\and +01C3=~ +01C4=: +01C5=; +01C6=â™  +01C7=♣ +01C8=♥ +01C9=♦ +01CA=★ +01CB=â—Ž +01CC=â—‹ +01CD=â–¡ +01CE=â–³ +01CF=â—‡ +01D0=@ +01D1=♪ +01D2=% +01D3=☀ +01D4=☠+01D5=☂ +01D6=☃ +01D7=\x01D7 +01D8=\x01D8 +01D9=\x01D9 +01DA=\x01DA +01DB=⤴ +01DC=⤵ +01DD=\x01DD +01DE= +01DF=\x01DF +01E0=[PK] +01E1=[MN] +0401=ê°€ +0405=ê°ˆ +0409=ê°‘ +040D=ê°• +0413=ê°œ +041B=ê°± +041C=ê°¸ +0425=ê±° +042B=ê²€ +0434=게 +0436=ê²” +0439=겟 +044D=ê³  +044F=곤 +0451=골 +0455=ê³° +0458=ê³µ +0462=ê´‘ +0469=ê´´ +0476=구 +0478=êµ° +047A=êµ´ +048B=ê·€ +0495=ê·¸ +0497=ê·¼ +0499=글 +04A0=기 +04A9=ê¹… +04AC=까 +04AD=ê¹ +04B2=깜 +04B3=ê¹ +04B8=깨 +04C5=꺽 +04CA=ê» +04DB=꼬 +04DF=ê¼´ +04E5=꽃 +04F5=꾸 +04F8=ê¿€ +0524=나 +0529=ë‚  +0535=ë‚´ +0539=냄 +0543=냥 +0544=너 +0551=네 +0565=ë…¸ +056A=놈 +056D=ë† +0580=뇽 +0581=누 +0583=눈 +0598=ëŠ +05A3=늪 +05A7=니 +05B1=다 +05B2=ë‹¥ +05B4=단 +05BB=ë‹´ +05C3=대 +05CD=ë” +05CE=ë• +05D8=ë© +05DB=ë° +05DE=ë¸ +05EB=ë„ +05EC=ë… +05ED=ëˆ +05EF=ëŒ +05F5=ë™ +0604=ë‘ +0606=ë‘” +0608=ë‘  +060B=ë‘¥ +061B=ë“œ +061F=들 +0626=ë”” +0628=딘 +062A=딜 +062C=딥 +0632=ë”± +0634=딸 +065B=ë˜ +0665=ëšœ +0666=ëš +0668=뚤 +0687=ë¼ +0688=ë½ +0689=란 +068A=ëž„ +068F=ëž‘ +0693=래 +0695=ëžœ +0697=램 +0698=ëž© +06A1=러 +06A2=럭 +06A3=런 +06A9=ë  +06AB=ë ˆ +06AD=ë Œ +06B1=ë › +06B4=ë ¥ +06C0=ë¡œ +06C1=ë¡ +06C3=롤 +06C5=ë¡­ +06C7=롱 +06D8=룡 +06D9=루 +06DD=룸 +06EC=륙 +06F3=르 +06FE=리 +0700=린 +0701=릴 +0702=림 +0705=ë§ +0706=마 +0708=만 +070B=ë§ +070E=맘 +0711=ë§ +0715=매 +0717=맨 +0724=먹 +072E=ë©” +0740=모 +0743=몬 +0749=몽 +0759=무 +075E=물 +0764=ë­‰ +0770=뮤 +077A=미 +077E=ë°€ +0787=ë°” +078D=ë°œ +0791=ë°¤ +0794=ë°© +0796=ë°° +079C=뱃 +07A4=버 +07A5=ë²… +07A6=번 +07AA=ë²” +07AF=ë²  +07B3=벨 +07BC=별 +07C4=ë³´ +07C5=ë³µ +07C8=ë³¼ +07DA=부 +07DB=ë¶ +07DC=분 +07DE=불 +07E1=ë¶ +07E4=붕 +07F0=ë·° +07F6=브 +07F9=블 +07FD=비 +07FF=빈 +0800=빌 +081F=ë» +0831=ë½€ +083B=ë¿Œ +083E=ë¿” +0841=ë¿¡ +0844=ì˜ +0849=ì‚ +0851=사 +0854=ì‚° +0859=삼 +085D=ìƒ +085F=새 +0860=색 +0868=샤 +0879=ì„  +087B=설 +0880=섯 +0882=성 +0884=세 +0885=섹 +0887=ì…€ +089A=소 +089D=ì† +089E=솔 +08A0=솜 +08A3=송 +08BE=수 +08C2=술 +08C6=숭 +08CC=ì‰ +08CF=쉘 +08DA=슈 +08E0=스 +08E3=슬 +08E9=ì‹œ +08EA=ì‹ +08EB=ì‹  +08ED=실 +0905=쌩 +0909=ì¬ +0914=ì˜ +0936=ì“° +0942=씨 +094A=ì•„ +094C=안 +094F=ì•Œ +0953=ì•” +095A=ì•  +095C=앤 +095F=앱 +0963=야 +0972=ì–´ +0977=ì–¼ +097F=ì—‰ +0983=ì— +0986=ì—˜ +0987=ì—  +098B=ì—¬ +098E=ì—° +0992=ì—¼ +0997=ì˜ +09A2=오 +09A4=온 +09AD=옹 +09AF=와 +09B2=왈 +09B7=왕 +09C6=ìš” +09CD=ìš© +09CE=ìš° +09D1=울 +09D4=움 +09DA=ì› +09E9=윈 +09EF=유 +09F0=육 +09F1=윤 +09FB=ì„ +09FD=ìŒ +0A0C=ì´ +0A0E=ì¸ +0A0F=ì¼ +0A13=ìž„ +0A14=ìž… +0A17=잉 +0A19=잎 +0A1A=ìž +0A21=ìž  +0A25=장 +0A27=재 +0A30=쟈 +0A36=ìŸ +0A3A=ì € +0A3C=ì „ +0A3F=ì  +0A44=ì œ +0A47=ì ¤ +0A4C=ì ¸ +0A54=ì¡° +0A72=죤 +0A74=주 +0A7D=중 +0A88=쥬 +0A8C=즈 +0A94=지 +0A95=ì§ +0A96=진 +0A98=질 +0AAB=짱 +0AEA=ì°Œ +0AF3=ì°¨ +0AF8=ì°¸ +0B06=ì±™ +0B07=ì±  +0B10=ì²  +0B16=ì²´ +0B24=ì´ˆ +0B2B=ì´ +0B37=쵸 +0B40=충 +0B4C=츄 +0B51=츠 +0B59=치 +0B5D=ì¹  +0B5F=침 +0B63=ì¹´ +0B65=칸 +0B6B=ìº +0B73=캥 +0B80=컹 +0B81=ì¼€ +0B83=켄 +0B84=켈 +0B92=ì½” +0B94=콘 +0B95=콜 +0BA5=ì¿  +0BA7=쿤 +0BB5=퀸 +0BBF=í¬ +0BC6=키 +0BC9=킬 +0BCD=킹 +0BCE=타 +0BCF=íƒ +0BD7=탕 +0BD8=태 +0BE0=탱 +0BE3=í„° +0BE5=í„´ +0BE8=í…€ +0BEC=í…… +0BED=í…Œ +0BFA=토 +0BFB=톡 +0BFC=톤 +0BFF=톱 +0C01=통 +0C0B=투 +0C22=트 +0C28=틈 +0C30=í‹° +0C31=틱 +0C33=틸 +0C38=파 +0C3E=팜 +0C42=팡 +0C44=패 +0C46=팬 +0C4C=팽 +0C4F=í¼ +0C50=í½ +0C52=펄 +0C58=페 +0C5E=펫 +0C6B=í¬ +0C6C=í­ +0C6E=í´ +0C72=í +0C7C=푸 +0C80=í’€ +0C85=í’ +0C93=프 +0C95=플 +0C99=피 +0C9A=픽 +0CA0=í•‘ +0CA1=하 +0CA3=í•œ +0CA8=í•« +0CAA=í•´ +0CAC=핸 +0CB7=í—Œ +0CBE=í—¤ +0CC1=í—¬ +0CCE=형 +0CD3=호 +0CDB=í™ +0CDD=í™” +0CF4=후 +0D14=í‰ +0D17=í” +0D27=히 +0402=ê° +0403=ê°„ +0408=ê° +0411=ê°š +0414=ê° +0429=걸 +042C=ê² +043D=격 +0441=ê²° +0446=ê²½ +045C=ê´€ +0471=êµ +0479=êµ³ +0485=권 +049B=금 +04A4=길 +04A6=ê¹€ +04A8=깃 +04CB=ê» +04FA=꿈 +04FF=ê¿” +0503=ê¿° +051C=ë¼ +0527=ë‚œ +0534=낳 +053D=냉 +0548=ë„ +0557=ë„· +055E=ë… +0566=ë…¹ +0567=ë…¼ +0568=놀 +059A=는 +059B=늘 +05A8=닉 +05B6=달 +05BF=당 +05C5=댄 +05D0=ë˜ +0605=ë‘‘ +060D=ë’€ +0624=등 +062D=딧 +0631=ë”° +0639=ë•… +063B=ë•Œ +0647=ë–¨ +0669=ëš« +066D=ë›° +068B=람 +06AC=ë ‰ +06B3=ë ¤ +06BB=ë ¹ +06BC=ë¡€ +06D3=료 +06EB=류 +06F7=름 +06FD=릎 +06FF=릭 +0704=릿 +0707=막 +071D=맹 +0723=머 +0726=ë©€ +072B=ë© +0734=멧 +0739=ë©´ +073A=멸 +073D=명 +0741=목 +0746=몸 +075A=묵 +075B=묶 +075C=문 +077B=믹 +077C=민 +0788=ë°• +078B=ë°˜ +078C=ë°› +0790=ë°Ÿ +0792=ë°¥ +079A=ë±€ +07A8=벌 +07AB=법 +07B1=벤 +07BA=ë²½ +07BB=ë³€ +07C7=본 +07C9=ë´„ +07CC=ë´‰ +0802=ë¹” +0805=ë¹™ +0807=ë¹› +081E=뺨 +082A=뼈 +0835=ë½ +083F=ë¿œ +0845=ìœ +0856=ì‚´ +0867=ìƒ +0870=ì„€ +0875=ì„œ +0876=ì„ +089B=ì† +08B6=쇼 +08BC=ìˆ +08C0=순 +08C1=숟 +08C3=숨 +08D2=쉬 +08E6=습 +08E8=승 +08EE=ì‹« +08EF=심 +08F4=싸 +094B=ì•… +0954=ì•• +0959=ì•ž +095B=ì•¡ +0962=앵 +096B=ì–‘ +0973=ì–µ +0974=ì–¸ +097A=ì—„ +097B=ì—… +098C=ì—­ +098F=ì—´ +099B=예 +09A3=옥 +09D0=ìš´ +09D7=ì›… +09D8=워 +09E0=웨 +09E5=웹 +09E7=위 +09F8=으 +09FA=ì€ +0A07=ì˜ +0A1B=ìž‘ +0A2B=ìž¼ +0A2F=ìŸ +0A3B=ì  +0A3D=ì ˆ +0A42=ì • +0A46=ì   +0A48=ì ¬ +0A75=죽 +0A81=ì¥ +0A8E=즌 +0A9B=집 +0A9C=짓 +0A9E=짖 +0AA1=짜 +0AA2=ì§ +0AAC=째 +0AC5=쪼 +0AEB=ì° +0AEF=ì° +0AFE=채 +0B02=ì±” +0B0D=처 +0B0F=천 +0B15=ì²­ +0B1E=ì³ +0B29=ì´™ +0B30=최 +0B39=추 +0B3A=축 +0B3C=출 +0B3D=춤 +0B45=ì·¨ +0B66=ì¹¼ +0B77=커 +0B7B=컬 +0B7E=ì»· +0B96=콤 +0BA0=쾌 +0BB3=퀴 +0BC2=í´ +0BC7=í‚¥ +0BD0=탄 +0BD1=탈 +0BD3=íƒ +0BD9=íƒ +0BE6=털 +0BEE=í… +0BF0=í…” +0BFE=톰 +0C16=튀 +0C1D=튜 +0C3B=íŒ +0C3C=팔 +0C45=팩 +0C51=펀 +0C53=펌 +0C6D=í° +0C82=í’ˆ +0C96=í”” +0C9B=í•€ +0C9C=í•„ +0CA4=í•  +0CA5=í•¥ +0CA6=함 +0CA7=í•© +0CA9=í•­ +0CAE=í–„ +0CB0=í–‡ +0CB4=í–¥ +0CC9=혈 +0CCF=혜 +0CD4=혹 +0CD5=혼 +0CD6=홀 +0CDF=환 +0CE8=회 +0CEF=효 +0D06=휘 +0D0B=휩 +0D16=í‘ +0D1B=í™ +0D1D=í¡ +0D21=í¬ +0D22=í° +0D2B=힘 +0416=ê°¤ +0427=ê±´ +0448=계 +045A=ê³¼ +047B=êµµ +0492=ê·œ +049C=급 +04A7=ê¹ +0512=ëˆ +0526=ë‚š +052A=ë‚¡ +052C=남 +05A1=능 +05A9=ë‹Œ +05BD=ë‹· +0673=뜨 +0680=ë  +069B=ëž­ +06CC=뢰 +06DC=룰 +06F5=른 +0710=맛 +0712=맞 +0716=맥 +071A=맵 +0730=멘 +0731=ë©œ +0732=멤 +0797=ë°± +0798=ë°´ +07C0=병 +080B=빨 +0863=샘 +088D=ì…” +0890=ì…œ +08B0=쇠 +08BF=숙 +08C9=숲 +08DF=ìŠ +08F1=싯 +0921=ì +094E=ì•Š +0964=약 +097C=ì—† +0A16=있 +0A1C=ìž” +0A1F=잘 +0A5F=좋 +0A77=줄 +0A9D=징 +0AFC=ì°½ +0B12=첩 +0B5B=친 +0B67=캄 +0B87=켓 +0BB4=퀵 +0BC1=í° +0C24=튼 +0C26=í‹€ +0C59=펙 +0C5A=펜 +0C61=편 +0C66=í‰ +0C77=í‘œ +0C7E=푼 +0CA2=í•™ +0CB2=í–‰ +0CB5=í—ˆ +0CBA=í—˜ +0CE0=활 +0D2A=íž +E000=\n +25BC=\r diff --git a/src/com/dabomstew/pkrandom/config/Generation5.tbl b/src/com/dabomstew/pkrandom/config/Generation5.tbl new file mode 100755 index 000000000..8c3835503 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/Generation5.tbl @@ -0,0 +1,46 @@ +2467=× +2468=÷ +246C=… +246D=♂ +246E=♀ +246F=â™  +2470=♣ +2471=♥ +2472=♦ +2473=★ +2474=â—Ž +2475=â—‹ +2476=â–¡ +2477=â–³ +2478=â—‡ +2479=♪ +247A=☀ +247B=☠+247D=☂ +21D2=\[angry] +21D4=\[turnup] +2200=\[turndown] +2203=\[zzz] +2227=\[=)] +2228=\[=D] +2460=\[:)] +2461=\[:D] +2462=\[>:O] +2463=\[:(] +2464=\[turnup2] +2465=\[turndown2] +2466=\[zzz2] +2469=\[er] +246A=\[re] +246B=\[corner] +247E=\[:)s] +247F=\[:Ds] +2480=\[>:Os] +2481=\[:(s] +2482=\[turnups] +2483=\[turndowns] +2484=\[zzzs] +2485=\[e] +2486=\[PK] +2487=\[MN] +FFE2=\[>:O2] diff --git a/src/com/dabomstew/pkrandom/config/gameboy_jap.tbl b/src/com/dabomstew/pkrandom/config/gameboy_jap.tbl new file mode 100755 index 000000000..fce8c2f23 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gameboy_jap.tbl @@ -0,0 +1,179 @@ +05=ガ +06=ã‚® +07=ã‚° +08=ゲ +09=ã‚´ +0A=ザ +0B=ジ +0C=ズ +0D=ゼ +0E=ゾ +0F=ダ +10=ヂ +11=ヅ +12=デ +13=ド +19=ム+1A=ビ +1B=ブ +1C=ボ +26=㌠+27=㎠+28=ã +29=ã’ +2A=ã” +2B=ã– +2C=㘠+2D=ãš +2E=㜠+2F=ãž +30=ã  +31=㢠+32=㥠+33=㧠+34=ã© +3A=ã° +3B=ã³ +3C=㶠+3D=ã¹ +3E=ã¼ +40=パ +41=ピ +42=プ +43=ム+44=ã± +45=ã´ +46=ã· +47=㺠+48=ã½ +54=ãƒã‚±ãƒ¢ãƒ³ +7F= +80=ã‚¢ +81=イ +82=ウ +83=エ +84=ã‚© +85=ã‚« +86=ã‚­ +87=ク +88=ケ +89=コ +8A=サ +8B=ã‚· +8C=ス +8D=ã‚» +8E=ソ +8F=ã‚¿ +90=ム+91=ツ +92=テ +93=ト +94=ナ +95=ニ +96=ヌ +97=ム+98=ノ +99=ム+9A=ヒ +9B=フ +9C=ホ +9D=マ +9E=ミ +9F=ム +A0=メ +A1=モ +A2=ヤ +A3=ユ +A4=ヨ +A5=ラ +A6=ル +A7=レ +A8=ロ +A9=ワ +AA=ヲ +AB=ン +AC=ッ +AD=ャ +AE=ュ +AF=ョ +B0=ã‚£ +B1=ã‚ +B2=ã„ +B3=ㆠ+B4=㈠+B5=㊠+B6=ã‹ +B7=ã +B8=ã +B9=ã‘ +BA=ã“ +BB=ã• +BC=ã— +BD=ã™ +BE=ã› +BF=ã +C0=㟠+C1=ã¡ +C2=㤠+C3=㦠+C4=㨠+C5=㪠+C6=ã« +C7=㬠+C8=ã­ +C9=ã® +CA=㯠+CB=ã² +CC=ãµ +CD=㸠+CE=ã» +CF=ã¾ +D0=ã¿ +D1=ã‚€ +D2=ã‚ +D3=ã‚‚ +D4=ã‚„ +D5=ゆ +D6=よ +D7=ら +D8=ã‚Š +D9=ã‚‹ +DA=ã‚Œ +DB=ã‚ +DC=ã‚ +DD=ã‚’ +DE=ã‚“ +DF=㣠+E0=ゃ +E1=ã‚… +E2=ょ +E3=ー +E4=。 +E4=ã‚š +E5=゙​ +E6=? +E7=! +E8=。 +E9=ã‚¡ +EB=ェ +EF=♂ +F0=円 +F1=× +F2=[.] +F3=/ +F4=ã‚© +F5=♀ +F6=0 +F7=1 +F8=2 +F9=3 +FA=4 +FB=5 +FC=6 +FD=7 +FE=8 +FF=9 +4F=\n +51=\p +55=\l +57=\e \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gba_english.tbl b/src/com/dabomstew/pkrandom/config/gba_english.tbl new file mode 100755 index 000000000..82f4e3af7 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gba_english.tbl @@ -0,0 +1,159 @@ +00= +01=À +02=à +03= +04=Ç +05=È +06=É +07=Ê +08=Ë +09=ÃŒ +0B=ÃŽ +0C=à +0D=Ã’ +0E=Ó +0F=Ô +10=Æ +11=Ù +12=Ú +13=Û +14=Ñ +15=ß +16=à +17=á +19=ç +1A=è +1B=é +1C=ê +1D=ë +1E=ì +20=î +21=ï +22=ò +23=ó +24=ô +25=æ +26=ù +27=ú +28=û +29=ñ +2A=º +2B=ª +2C=· +2D=& +2E=+ +34=[Lv] +35== +36=; +51=¿ +52=¡ +53=[PK] +54=[MN] +55=[PO] +56=[Ke] +57=[BL] +58=[OC] +59=[K] +5A=à +5B=% +5C=( +5D=) +68=â +6F=í +79=[U] +7A=[D] +7B=[L] +7C=[R] +A1=0 +A2=1 +A3=2 +A4=3 +A5=4 +A6=5 +A7=6 +A8=7 +A9=8 +AA=9 +AB=! +AC=? +AD=. +AE=- +AF=· +B0=… +B1=“ +B2=†+B3=‘ +B4=’ +B5=♂ +B6=♀ +B7=$ +B8=, +B9=[x] +BA=/ +BB=A +BC=B +BD=C +BE=D +BF=E +C0=F +C1=G +C2=H +C3=I +C4=J +C5=K +C6=L +C7=M +C8=N +C9=O +CA=P +CB=Q +CC=R +CD=S +CE=T +CF=U +D0=V +D1=W +D2=X +D3=Y +D4=Z +D5=a +D6=b +D7=c +D8=d +D9=e +DA=f +DB=g +DC=h +DD=i +DE=j +DF=k +E0=l +E1=m +E2=n +E3=o +E4=p +E5=q +E6=r +E7=s +E8=t +E9=u +EA=v +EB=w +EC=x +ED=y +EE=z +EF=[>] +F0=: +F1=Ä +F2=Ö +F3=Ãœ +F4=ä +F5=ö +F6=ü +F7=[u] +F8=[d] +F9=[l] +FA=\l +FB=\p +FC=\c +FE=\n diff --git a/src/com/dabomstew/pkrandom/config/gba_jap.tbl b/src/com/dabomstew/pkrandom/config/gba_jap.tbl new file mode 100755 index 000000000..4a873bda1 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gba_jap.tbl @@ -0,0 +1,254 @@ +00= +01=ã‚ +02=ã„ +03=ㆠ+04=㈠+05=㊠+06=ã‹ +07=ã +08=ã +09=ã‘ +0A=ã“ +0B=ã• +0C=ã— +0D=ã™ +0E=ã› +0F=ã +10=㟠+11=ã¡ +12=㤠+13=㦠+14=㨠+15=㪠+16=ã« +17=㬠+18=ã­ +19=ã® +1A=㯠+1B=ã² +1C=ãµ +1D=㸠+1E=ã» +1F=ã¾ +20=ã¿ +21=ã‚€ +22=ã‚ +23=ã‚‚ +24=ã‚„ +25=ゆ +26=よ +27=ら +28=ã‚Š +29=ã‚‹ +2A=ã‚Œ +2B=ã‚ +2C=ã‚ +2D=ã‚’ +2E=ã‚“ +2F=ã +30=ム+31=ã… +32=㇠+33=㉠+34=ゃ +35=ã‚… +36=ょ +37=㌠+38=㎠+39=ã +3A=ã’ +3B=ã” +3C=ã– +3D=㘠+3E=ãš +3F=㜠+40=ãž +41=ã  +42=㢠+43=㥠+44=㧠+45=ã© +46=ã° +47=ã³ +48=㶠+49=ã¹ +4A=ã¼ +4B=ã± +4C=ã´ +4D=ã· +4E=㺠+4F=ã½ +50=㣠+51=ã‚¢ +52=イ +53=ウ +54=エ +55=オ +56=ã‚« +57=ã‚­ +58=ク +59=ケ +5A=コ +5B=サ +5C=ã‚· +5D=ス +5E=ã‚» +5F=ソ +60=ã‚¿ +61=ム+62=ツ +63=テ +64=ト +65=ナ +66=ニ +67=ヌ +68=ム+69=ノ +6A=ム+6B=ヒ +6C=フ +6D=ヘ +6E=ホ +6F=マ +70=ミ +71=ム +72=メ +73=モ +74=ヤ +75=ユ +76=ヨ +77=ラ +78=リ +79=ル +7A=レ +7B=ロ +7C=ワ +7D=ヲ +7E=ン +7F=ã‚¡ +80=ã‚£ +81=ã‚¥ +82=ェ +83=ã‚© +84=ャ +85=ュ +86=ョ +87=ガ +88=ã‚® +89=ã‚° +8A=ゲ +8B=ã‚´ +8C=ザ +8D=ジ +8E=ズ +8F=ゼ +90=ゾ +91=ダ +92=ヂ +93=ヅ +94=デ +95=ド +96=ム+97=ビ +98=ブ +99=ベ +9A=ボ +9B=パ +9C=ピ +9D=プ +9E=ペ +9F=ム+A0=ッ +A1=0 +A2=1 +A3=2 +A4=3 +A5=4 +A6=5 +A7=6 +A8=7 +A9=8 +AA=9 +AB=! +AC=? +AD=. +AE=ー +AF=ï½· +B0=å° +B1=ォ +B2=ï½» +B3=< +B4=> +B5=♂ +B6=♀ +B7=$ +B8=, +B9=* +BA=/ +BB=A +BC=B +BD=C +BE=D +BF=E +C0=F +C1=G +C2=H +C3=I +C4=J +C5=K +C6=L +C7=M +C8=N +C9=O +CA=P +CB=Q +CC=R +CD=S +CE=T +CF=U +D0=V +D1=W +D2=X +D3=Y +D4=Z +D5=a +D6=b +D7=c +D8=d +D9=e +DA=f +DB=g +DC=h +DD=i +DE=j +DF=k +E0=l +E1=m +E2=n +E3=o +E4=p +E5=q +E6=r +E7=s +E8=t +E9=u +EA=v +EB=w +EC=x +ED=y +EE=z +EF=[>] +F0=: +F1=ト +F2=ï¾– +F3=ワ +F4=ä +F5=ö +F6=ü +F7=[u] +F8=[d] +F9=[l] +FA=\l +FB=\p +FC=\c +FE=\n diff --git a/src/com/dabomstew/pkrandom/config/gen1_offsets.ini b/src/com/dabomstew/pkrandom/config/gen1_offsets.ini new file mode 100755 index 000000000..b87b5d2fb --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gen1_offsets.ini @@ -0,0 +1,707 @@ +[Red (U)] +Game=POKEMON RED +Version=0 +NonJapanese=1 +Type=RB +ExtraTableFile=rby_english +BWXPTweak=rb_en_bwxp +XAccNerfTweak=rb_en_xaccnerf +CritRateTweak=rb_en_critrate +InternalPokemonCount=190 +PokedexOrder=0x41024 +PokemonNamesOffset=0x1C21E +PokemonNamesLength=10 +PokemonStatsOffset=0x383DE +MewStatsOffset=0x425B +WildPokemonTableOffset=0xCEEB +OldRodOffset=0xE252 +GoodRodOffset=0xE27F +SuperRodTableOffset=0xE919 +MapNameTableOffset=0x71313 +MoveCount=165 +MoveDataOffset=0x38000 +MoveNamesOffset=0xB0000 +ItemNamesOffset=0x472B +TypeEffectivenessOffset=0x3E474 +PokemonMovesetsTableOffset=0x3B05C +StarterOffsets1=[0x1D126, 0x1CC84, 0x1D10E, 0x39CF8, 0x50FB3, 0x510DD] +StarterOffsets2=[0x1D104, 0x19591, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CAF, 0x6060E, 0x61450, 0x75F9E] +StarterOffsets3=[0x1D115, 0x19599, 0x1CDD0, 0x1D130, 0x39CF2, 0x50FB1, 0x510DB, 0x51CB7, 0x60616, 0x61458, 0x75FA6] +PatchPokedex=1 +CanChangeStarterText=1 +CanChangeTrainerText=1 +StarterPokedexOnOffset=0x5C0DC +StarterPokedexOffOffset=0x5C0E6 +StarterPokedexBranchOffset=0x5E000 +PokedexRamOffset=0xD2F7 +TrainerDataTableOffset=0x39D3B +TrainerDataClassCounts=[0, 13, 14, 18, 8, 9, 24, 7, 12, 14, 15, 9, 3, 0, 11, 15, 9, 7, 15, 4, 2, 8, 6, 17, 9, 9, 3, 0, 13, 3, 41, 10, 8, 1, 1, 1, 1, 1, 1, 1, 1, 5, 12, 3, 1, 24, 1, 1] +ExtraTrainerMovesTableOffset=0x39D32 +GymLeaderMovesTableOffset=0x39D23 +TMMovesOffset=0x13773 +TrainerClassNamesOffsets=[0x27EC2, 0x399FF] +IntroPokemonOffset=0x616D +IntroCryOffset=0x1C73 +MapBanks=0xC23D +MapAddresses=0x01AE +SpecialMapList=0x46A40 +SpecialMapPointerTable=0x46A96 +HiddenItemRoutine=0x76688 +TradeTableOffset=0x71B7B +TradeTableSize=10 +TradeNameLength=11 +TradesUnused=[2] +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1DD49, 0x5CF5F, 0x5CF17] +StaticPokemonOverworlds=[0x1E3D5, 0x1E3DD, 0x1E3E5, 0x1E3ED, 0x1E3F5, 0x1E3FD, 0x1E405, 0x1E40D, 0x1E415, 0x468E8, 0x51963, 0x45F44, 0x59630, 0x59970] +StaticPokemonFossils=[0x61064, 0x61068, 0x6106C] +StaticPokemonGifts=[0x51DAD, 0x49320] +StaticPokemonGameCorner[]=[0x52859,0x5298A] // Abra +StaticPokemonGameCorner[]=[0x5285A,0x5298C] // Clefairy +StaticPokemonGameCorner[]=[0x5285B,0x5298E] // Nidorina/Nidorino +StaticPokemonGameCorner[]=[0x52864,0x52990] // Dratini/Pinsir +StaticPokemonGameCorner[]=[0x52865,0x52992] // Scyther/Dratini +StaticPokemonGameCorner[]=[0x52866,0x52994] // Porygon +TMText[]=[6,0x0A0100,\pTM06 contains\n%m!\e] +TMText[]=[11,0x98A7C,TM11 teaches\n%m!\e] +TMText[]=[13,0x9CC22,contains\n%m!\e] +TMText[]=[18,0x9C86F,TM18 is\n%m!\e] +TMText[]=[21,0x9D521,\pTM21 contains\n%m.\e] +TMText[]=[24,0x9C0F6,\pTM24 contains\n%m!\e] +TMText[]=[27,0x96096,\pTM27 is\n%m!\e] +TMText[]=[28,0x9875D,Those miserable\nROCKETs!\pLook what they\ndid here!\pThey stole a TM\nfor teaching\l[POKé]MON how to\l%m!\e] +TMText[]=[28,0x987E3,I figure what's\nlost is lost!\pI decided to get\n%m\lwithout a TM!\e] +TMText[]=[29,0xA253F,TM29 is\n%m!\e] +TMText[]=[31,0xA168A,\pTM31 contains my\nfavorite,\l%m!\e] +TMText[]=[34,0x980C1,\pA TM contains a\ntechnique that\lcan be taught to\l[POKé]MON!\pA TM is good only\nonce! So when you\luse one to teach\la new technique,\lpick the [POKé]MON\lcarefully!\pTM34 contains\n%m!\e] +TMText[]=[36,0x824CA,TM36 is\n%m!\e] +TMText[]=[38,0xA09BD,\pTM38 contains\n%m!\e] +TMText[]=[39,0x8C8DA,TM39 is the move\n%m.\e] +TMText[]=[41,0xA5B6F,TM41 teaches\n%m!\pMany [POKé]MON\ncan use it!\e] +TMText[]=[42,0xA46AE,TM42 contains\n%m...\e] +TMText[]=[46,0xA1DE1,\pTM46 is\n%m!\e] +TMText[]=[48,0x9CCAD,contains\n%m!\e] +TMText[]=[49,0x9CD31,\pTM49 is\n%m!\e] + +[Blue (U)] +Game=POKEMON BLUE +Version=0 +NonJapanese=1 +Type=RB +CopyTMText=1 +CopyStaticPokemon=1 +CopyFrom=Red (U) +BWXPTweak=rb_en_bwxp +XAccNerfTweak=rb_en_xaccnerf +CritRateTweak=rb_en_critrate +HiddenItemRoutine=0x76689 +StarterOffsets2=[0x1D104, 0x19591, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CAF, 0x6060E, 0x61450, 0x75F9F] +StarterOffsets3=[0x1D115, 0x19599, 0x1CDD0, 0x1D130, 0x39CF2, 0x50FB1, 0x510DB, 0x51CB7, 0x60616, 0x61458, 0x75FA7] + +[Yellow (U)] +Game=POKEMON YELLOW +Version=0 +NonJapanese=1 +Type=Yellow +CopyFrom=Red (U) +BWXPTweak=yellow_en_bwxp +XAccNerfTweak=yellow_en_xaccnerf +CritRateTweak=yellow_en_critrate +PokedexOrder=0x410B1 +PokemonNamesOffset=0xE8000 +MewStatsOffset=0 +MoveNamesOffset=0xBC000 +ItemNamesOffset=0x45B7 +WildPokemonTableOffset=0xCB95 +OldRodOffset=0xE0FF +GoodRodOffset=0xE12C +SuperRodTableOffset=0xF5EDA +MapNameTableOffset=0x7139C +TypeEffectivenessOffset=0x3E5FA +PokemonMovesetsTableOffset=0x3B1E5 +StarterOffsets1=[0x18F19, 0x1CB41, 0x1CB66] +StarterOffsets2=[0x3A28A] +TrainerDataTableOffset=0x39DD1 +TrainerDataClassCounts=[0, 14, 15, 19, 8, 10, 25, 7, 12, 14, 15, 9, 3, 0, 11, 15, 9, 7, 15, 4, 2, 8, 6, 17, 9, 3, 3, 0, 13, 3, 49, 10, 8, 1, 1, 1, 1, 1, 1, 1, 1, 5, 10, 3, 1, 24, 1, 1] +ExtraTrainerMovesTableOffset=0x39C6B +GymLeaderMovesTableOffset=0 +TMMovesOffset=0x1232D +TrainerClassNamesOffsets=[0x27E77, 0x3997E] +IntroPokemonOffset=0x5EDB +IntroCryOffset=0x1A4C +MapBanks=0xFC3E4 +MapAddresses=0xFC1F2 +SpecialMapPointerTable=0xF268D +HiddenItemRoutine=0x75F74 +TradeTableOffset=0x71C1D +TradeTableSize=10 +TradeNameLength=11 +TradesUnused=[2,4,6] +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1D652, 0x5CE55, 0x5CE0D] +StaticPokemonOverworlds=[0x1DCDF, 0x1DCE7, 0x1DCEF, 0x1DCF7, 0x1DCFF, 0x1DD07, 0x1DD0F, 0x1DD17, 0x1DD1F, 0x46B5A, 0x519A5, 0x461A5, 0x594CC, 0x5980C] +StaticPokemonFossils=[0x61050, 0x61054, 0x61058] +StaticPokemonGifts=[0x51DD6, 0xF21C0, 0x1CF8C, 0x515AF, 0xF1A45] +StaticPokemonGameCorner[] = [0x527BA,0x528EA] // Abra +StaticPokemonGameCorner[] = [0x527BB,0x528EC] // Vulpix +StaticPokemonGameCorner[] = [0x527BC,0x528EE] // Wigglytuff +StaticPokemonGameCorner[] = [0x527C5,0x528F0] // Scyther +StaticPokemonGameCorner[] = [0x527C6,0x528F2] // Pinsir +StaticPokemonGameCorner[] = [0x527C7,0x528F4] // Porygon +TMText[]=[6,0x0B0EE4,\pTM06 contains\n%m!\e] +TMText[]=[11,0xAB2D6,TM11 teaches\n%m!\e] +TMText[]=[13,0xAE5CA,contains\n%m!\e] +TMText[]=[18,0xAE3F3,TM18 is\n%m!\e] +TMText[]=[21,0xAF141,\pTM21 contains\n%m.\e] +TMText[]=[24,0xAD9FE,\pTM24 contains\n%m!\e] +TMText[]=[27,0xA9BEC,\pTM27 is\n%m!\e] +TMText[]=[28,0xAAEC4,Those miserable\nROCKETs!\pLook what they\ndid here!\pThey stole a TM\nfor teaching\l[POKé]MON how to\l%m!\e] +TMText[]=[28,0xAAF4A,I figure what's\nlost is lost!\pI decided to get\n%m\lwithout a TM!\e] +TMText[]=[29,0xB343D,TM29 is\n%m!\e] +TMText[]=[31,0xB2593,\pTM31 contains my\nfavorite,\l%m!\e] +TMText[]=[34,0xAA729,\pA TM contains a\ntechnique that\lcan be taught to\l[POKé]MON!\pA TM is good only\nonce! So when you\luse one to teach\la new technique,\lpick the [POKé]MON\lcarefully!\pTM34 contains\n%m!\e] +TMText[]=[36,0x9A5B0,TM36 is\n%m!\e] +TMText[]=[38,0xB17A1,\pTM38 contains\n%m!\e] +TMText[]=[39,0xA1BDE,TM39 is the move\n%m.\e] +TMText[]=[41,0xB5E1F,TM41 teaches\n%m!\pMany [POKé]MON\ncan use it!\e] +TMText[]=[42,0xB48A0,TM42 contains\n%m...\e] +TMText[]=[46,0xB2CEA,\pTM46 is\n%m!\e] +TMText[]=[48,0xAE655,contains\n%m!\e] +TMText[]=[49,0xAE6B7,\pTM49 is\n%m!\e] + +[Red (J)] +Game=POKEMON RED +Version=0 +NonJapanese=0 +Type=RB +InternalPokemonCount=190 +PokedexOrder=0x4279A +PokemonNamesOffset=0x39068 +PokemonNamesLength=5 +PokemonStatsOffset=0x38000 +MewStatsOffset=0x4200 +WildPokemonTableOffset=0xCF61 +OldRodOffset=0xE3A1 +GoodRodOffset=0xE3CE +SuperRodTableOffset=0xEC24 +MapNameTableOffset=0x718AF +MoveCount=165 +MoveNamesOffset=0x10000 +MoveDataOffset=0x39658 +ItemNamesOffset=0x433F +TypeEffectivenessOffset=0x3E756 +PokemonMovesetsTableOffset=0x3B427 +StarterOffsets1=[0x1CBBF, 0x1C6C2, 0x1CBA7, 0x3A069, 0x514A1, 0x515CB] +StarterOffsets2=[0x1CB9D, 0x19C66, 0x1C6C6, 0x1C806, 0x1CBB8, 0x5149D, 0x515C7, 0x52A1D, 0x606AD, 0x61F2D, 0x77003] +StarterOffsets3=[0x1CBAE, 0x19C6E, 0x1C80E, 0x1CBC9, 0x3A063, 0x5149F, 0x515C9, 0x52A25, 0x606B5, 0x61F35, 0x7700B] +PatchPokedex=0 +CanChangeStarterText=0 +CanChangeTrainerText=0 +TrainerDataTableOffset=0x3A0AC +TrainerDataClassCounts=[0, 13, 14, 18, 8, 9, 24, 7, 12, 14, 15, 9, 3, 0, 11, 15, 9, 7, 15, 4, 2, 8, 6, 17, 9, 9, 3, 0, 13, 3, 41, 10, 8, 1, 1, 1, 1, 1, 1, 1, 1, 5, 12, 3, 1, 24, 1, 1] +ExtraTrainerMovesTableOffset=0x3A0A3 +GymLeaderMovesTableOffset=0x3A094 +TMMovesOffset=0x12276 +TrainerClassNamesOffsets=[0x27F2A, 0x39D1C] +IntroPokemonOffset=0x5FB1 +IntroCryOffset=0x716 +MapBanks=0xC883 +MapAddresses=0x1BCB +SpecialMapList=0x47965 +SpecialMapPointerTable=0x479BB +HiddenItemRoutine=0x77D78 +TradeTableOffset=0x72043 +TradeTableSize=10 +TradeNameLength=5 +TradesUnused=[2] +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1E771, 0x5E390, 0x5E330] +StaticPokemonOverworlds=[0x1F0A7, 0x1F0AF, 0x1F0B7, 0x1F0BF, 0x1F0C7, 0x1F0CF, 0x1F0D7, 0x1F0DF, 0x1F0E7, 0x4780D, 0x526D4, 0x46C57, 0x59E34, 0x5A684] +StaticPokemonFossils=[0x617D7, 0x617DB, 0x617DF] +StaticPokemonGifts=[0x52B1B, 0x4A557] +StaticPokemonGameCorner[]=[0x53C10,0x53D6A] // Abra +StaticPokemonGameCorner[]=[0x53C11,0x53D6C] // Clefairy +StaticPokemonGameCorner[]=[0x53C12,0x53D6E] // Nidorina/Nidorino +StaticPokemonGameCorner[]=[0x53C1B,0x53D70] // Dratini/Pinsir +StaticPokemonGameCorner[]=[0x53C1C,0x53D72] // Scyther/Dratini +StaticPokemonGameCorner[]=[0x53C1D,0x53D74] // Porygon + +[Green (J)] +Game=POKEMON GREEN +Version=0 +NonJapanese=0 +Type=RB +CopyStaticPokemon=1 +CopyFrom=Red (J) +IntroPokemonOffset=0x5FB2 + +[Green (J)(T-Eng)] +Game=POKEMON GREEN +Version=0 +NonJapanese=0 +CRCInHeader=0xF57E +Type=RB +CopyStaticPokemon=1 +CopyFrom=Green (J) +ExtraTableFile=green_translation +CanChangeTrainerText=1 +TrainerClassNamesOffsets=[0xB21BF] +ItemNamesOffset=0xB1DB1 +MoveNamesOffset=0xB1657 + +[Blue (J)] +Game=POKEMON BLUE +Version=0 +NonJapanese=0 +Type=RB +CopyFrom=Red (J) +PokedexOrder=0x42784 +PokemonNamesOffset=0x39446 +PokemonStatsOffset=0x383DE +MewStatsOffset=0x425B +OldRodOffset=0xE3C6 +GoodRodOffset=0xE3F3 +SuperRodTableOffset=0xEC49 +MapNameTableOffset=0x7189E +MoveDataOffset=0x38000 +TypeEffectivenessOffset=0x3E75B +ItemNamesOffset=0x4733 +StarterOffsets1=[0x1CBBF, 0x1C6C2, 0x1CBA7, 0x3A069, 0x514A1, 0x515CB] +StarterOffsets2=[0x1CB9D, 0x19C5E, 0x1C6C6, 0x1C806, 0x1CBB8, 0x5149D, 0x515C7, 0x52A1F, 0x606AD, 0x61F2D, 0x77006] +StarterOffsets3=[0x1CBAE, 0x19C66, 0x1C80E, 0x1CBC9, 0x3A063, 0x5149F, 0x515C9, 0x52A27, 0x606B5, 0x61F35, 0x7700E] +TMMovesOffset=0x13C93 +TrainerClassNamesOffsets=[0x27EA1, 0x39DB5] +IntroPokemonOffset=0x60C4 +IntroCryOffset=0x1C77 +MapBanks=0xC275 +MapAddresses=0x0167 +SpecialMapList=0x47965 +SpecialMapPointerTable=0x479BB +HiddenItemRoutine=0x77D7B +TradeTableOffset=0x72033 +TradeTableSize=10 +TradeNameLength=4 +TradesUnused=[2] +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1E771, 0x5E38F, 0x5E32F] +StaticPokemonOverworlds=[0x1F0A7, 0x1F0AF, 0x1F0B7, 0x1F0BF, 0x1F0C7, 0x1F0CF, 0x1F0D7, 0x1F0DF, 0x1F0E7, 0x4780D, 0x526D6, 0x46C57, 0x5A079, 0x5A8C9] +StaticPokemonFossils=[0x617D7, 0x617DB, 0x617DF] +StaticPokemonGifts=[0x52B1D, 0x4A553] +StaticPokemonGameCorner[]=[0x53C12,0x53D6C] // Abra +StaticPokemonGameCorner[]=[0x53C13,0x53D6E] // Clefairy +StaticPokemonGameCorner[]=[0x53C14,0x53D70] // Nidorina/Nidorino +StaticPokemonGameCorner[]=[0x53C1D,0x53D72] // Dratini/Pinsir +StaticPokemonGameCorner[]=[0x53C1E,0x53D74] // Scyther/Dratini +StaticPokemonGameCorner[]=[0x53C1F,0x53D76] // Porygon + +[Yellow (J)] +Game=POKEMON YELLOW +Version=0 +NonJapanese=0 +Type=Yellow +CopyFrom=Red (J) +PokedexOrder=0x4282D +PokemonNamesOffset=0x39462 +PokemonNamesLength=5 +PokemonStatsOffset=0x383DE +MewStatsOffset=0 +WildPokemonTableOffset=0xCB91 +OldRodOffset=0xE214 +GoodRodOffset=0xE241 +SuperRodTableOffset=0xA4C7D +MapNameTableOffset=0x713CA +MoveDataOffset=0x38000 +TypeEffectivenessOffset=0x3E8EA +PokemonMovesetsTableOffset=0x3B59C +StarterOffsets1=[0x18F19, 0x1D0A7, 0x1D0CC] +StarterOffsets2=[0x3A5FB] +ItemNamesOffset=0x45C4 +CanChangeTrainerText=0 +TrainerDataTableOffset=0x3A142 +TrainerDataClassCounts=[0, 14, 15, 19, 8, 10, 25, 7, 12, 14, 15, 9, 3, 0, 11, 15, 9, 7, 15, 4, 2, 8, 6, 17, 9, 3, 3, 0, 13, 3, 49, 10, 8, 1, 1, 1, 1, 1, 1, 1, 1, 5, 10, 3, 1, 24, 1, 1] +ExtraTrainerMovesTableOffset=0x39FDC +GymLeaderMovesTableOffset=0 +TMMovesOffset=0x1286C +TrainerClassNamesOffsets=[0x27E56, 0x39D34] +IntroPokemonOffset=0x5E3A +IntroCryOffset=0x1ABF +MapBanks=0xFC3E4 +MapAddresses=0xFC1F2 +SpecialMapPointerTable=0xF2B7F +HiddenItemRoutine=0x77C4D +TradeTableOffset=0x71B77 +TradeTableSize=10 +TradeNameLength=5 +TradesUnused=[2,4,6] +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1E81D, 0x5E146, 0x5E0E6] +StaticPokemonOverworlds=[0x1F155, 0x1F15D, 0x1F165, 0x1F16D, 0x1F175, 0x1F17D, 0x1F185, 0x1F18D, 0x1F195, 0x47AEB, 0x525B9, 0x46F24, 0x59E7A, 0x5A6CA] +StaticPokemonFossils=[0x616A5, 0x616A9, 0x616AD] +StaticPokemonGifts=[0x529E7, 0xF20D5, 0x1DB40, 0x51DFE, 0xF0C1F] +StaticPokemonGameCorner[] = [0x53A1B,0x53B74] // Abra +StaticPokemonGameCorner[] = [0x53A1C,0x53B76] // Vulpix +StaticPokemonGameCorner[] = [0x53A1D,0x53B78] // Wigglytuff +StaticPokemonGameCorner[] = [0x53A26,0x53B7A] // Scyther +StaticPokemonGameCorner[] = [0x53A27,0x53B7C] // Pinsir +StaticPokemonGameCorner[] = [0x53A28,0x53B7E] // Porygon + +[Red (F)] +Game=POKEMON RED +Version=0 +NonJapanese=1 +Type=RB +CRCInHeader=0x7AFC +CopyFrom=Red (U) +ExtraTableFile=rby_freger +OldRodOffset=0xE242 +GoodRodOffset=0xE26F +SuperRodTableOffset=0xE909 +MapNameTableOffset=0x71317 +PokedexOrder=0x40FAA +TypeEffectivenessOffset=0x3E489 +PokemonMovesetsTableOffset=0x3B05F +ItemNamesOffset=0x472D +StarterOffsets1=[0x1D126, 0x1CC84, 0x1D10E, 0x39CFB, 0x50FB3, 0x510DD] +StarterOffsets2=[0x1D104, 0x19596, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CB2, 0x6060E, 0x61450, 0x75FE0] +StarterOffsets3=[0x1D115, 0x1959E, 0x1CDD0, 0x1D130, 0x39CF5, 0x50FB1, 0x510DB, 0x51CBA, 0x60616, 0x61458, 0x75FE8] +CanChangeStarterText=0 +PokedexRamOffset=0xD2FC +TrainerDataTableOffset=0x39D3E +ExtraTrainerMovesTableOffset=0x39D35 +GymLeaderMovesTableOffset=0x39D26 +TMMovesOffset=0x13782 +TrainerClassNamesOffsets=[0x27EBF, 0x399FF] +IntroPokemonOffset=0x6208 +IntroCryOffset=0x1C6F +HiddenItemRoutine=0x766CA +TradeTableOffset=0x71B4C +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1DD4C, 0x5CF57, 0x5CF0F] +StaticPokemonOverworlds=[0x1E3D8, 0x1E3E0, 0x1E3E8, 0x1E3F0, 0x1E3F8, 0x1E400, 0x1E408, 0x1E410, 0x1E418, 0x468E8, 0x51966, 0x45F44, 0x59630, 0x59970] +StaticPokemonFossils=[0x61064, 0x61068, 0x6106C] +StaticPokemonGifts=[0x51DB0, 0x4931F] +StaticPokemonGameCorner[]=[0x5285C,0x5298F] // Abra +StaticPokemonGameCorner[]=[0x5285D,0x52991] // Clefairy +StaticPokemonGameCorner[]=[0x5285E,0x52993] // Nidorina/Nidorino +StaticPokemonGameCorner[]=[0x52867,0x52995] // Dratini/Pinsir +StaticPokemonGameCorner[]=[0x52868,0x52997] // Scyther/Dratini +StaticPokemonGameCorner[]=[0x52869,0x52999] // Porygon + +[Blue (F)] +Game=POKEMON BLUE +Version=0 +NonJapanese=1 +Type=RB +CopyStaticPokemon=1 +CRCInHeader=0x56A4 +CopyFrom=Red (F) + +[Red (S)] +Game=POKEMON RED +Version=0 +NonJapanese=1 +Type=RB +CRCInHeader=0x384A +CopyFrom=Red (U) +ExtraTableFile=rby_espita +OldRodOffset=0xE254 +GoodRodOffset=0xE281 +SuperRodTableOffset=0xE91B +MapNameTableOffset=0x71316 +PokedexOrder=0x40FB4 +TypeEffectivenessOffset=0x3E483 +PokemonMovesetsTableOffset=0x3B069 +ItemNamesOffset=0x472B +StarterOffsets1=[0x1D126, 0x1CC84, 0x1D10E, 0x39D05, 0x50FB3, 0x510DD] +StarterOffsets2=[0x1D104, 0x19596, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CAB, 0x6060E, 0x61450, 0x76026] +StarterOffsets3=[0x1D115, 0x1959E, 0x1CDD0, 0x1D130, 0x39CFF, 0x50FB1, 0x510DB, 0x51CB3, 0x60616, 0x61458, 0x7602E] +CanChangeStarterText=0 +PokedexRamOffset=0xD2FC +TrainerDataTableOffset=0x39D48 +ExtraTrainerMovesTableOffset=0x39D3F +GymLeaderMovesTableOffset=0x39D30 +TMMovesOffset=0x13798 +TrainerClassNamesOffsets=[0x27ECB, 0x399FF] +IntroPokemonOffset=0x61CC +IntroCryOffset=0x1C72 +HiddenItemRoutine=0x76710 +TradeTableOffset=0x71B6B +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1DD4A, 0x5CF63, 0x5CF1B] +StaticPokemonOverworlds=[0x1E3D6, 0x1E3DE, 0x1E3E6, 0x1E3EE, 0x1E3F6, 0x1E3FE, 0x1E406, 0x1E40E, 0x1E416, 0x468E8, 0x5195F, 0x45F44, 0x59630, 0x59970] +StaticPokemonFossils=[0x61064, 0x61068, 0x6106C] +StaticPokemonGifts=[0x51DA9, 0x49323] +StaticPokemonGameCorner[]=[0x52856,0x52989] // Abra +StaticPokemonGameCorner[]=[0x52857,0x5298B] // Clefairy +StaticPokemonGameCorner[]=[0x52858,0x5298D] // Nidorina/Nidorino +StaticPokemonGameCorner[]=[0x52861,0x5298F] // Dratini/Pinsir +StaticPokemonGameCorner[]=[0x52862,0x52991] // Scyther/Dratini +StaticPokemonGameCorner[]=[0x52863,0x52993] // Porygon + +[Blue (S)] +Game=POKEMON BLUE +Version=0 +NonJapanese=1 +Type=RB +CopyStaticPokemon=1 +CRCInHeader=0x14D7 +CopyFrom=Red (S) + + +[Red (G)] +Game=POKEMON RED +Version=0 +NonJapanese=1 +Type=RB +CRCInHeader=0x5CDC +CopyFrom=Red (U) +ExtraTableFile=rby_freger +OldRodOffset=0xE24B +GoodRodOffset=0xE278 +SuperRodTableOffset=0xE912 +MapNameTableOffset=0x71310 +PokedexOrder=0x40F96 +TypeEffectivenessOffset=0x3E482 +PokemonMovesetsTableOffset=0x3B064 +ItemNamesOffset=0x472D +StarterOffsets1=[0x1D126, 0x1CC84, 0x1D10E, 0x39D00, 0x50FB3, 0x510DD] +StarterOffsets2=[0x1D104, 0x19596, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CA8, 0x6060E, 0x61450, 0x75FF1] +StarterOffsets3=[0x1D115, 0x1959E, 0x1CDD0, 0x1D130, 0x39CFA, 0x50FB1, 0x510DB, 0x51CB0, 0x60616, 0x61458, 0x75FF9] +CanChangeStarterText=0 +PokedexRamOffset=0xD2FC +TrainerDataTableOffset=0x39D43 +TrainerDataClassCounts=[0, 13, 14, 18, 8, 9, 24, 7, 12, 14, 15, 9, 3, 0, 11, 15, 9, 7, 15, 4, 2, 8, 6, 17, 9, 9, 3, 0, 13, 3, 41, 10, 8, 1, 1, 1, 1, 1, 1, 1, 1, 5, 12, 3, 1, 24, 1, 1] +ExtraTrainerMovesTableOffset=0x39D3A +GymLeaderMovesTableOffset=0x39D2B +TMMovesOffset=0x13774 +TrainerClassNamesOffsets=[0x27EC3, 0x399FF] +IntroPokemonOffset=0x6194 +IntroCryOffset=0x1C73 +HiddenItemRoutine=0x766D8 +TradeTableOffset=0x71B55 +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1DD49, 0x5CF5D, 0x5CF15] +StaticPokemonOverworlds=[0x1E3D5, 0x1E3DD, 0x1E3E5, 0x1E3ED, 0x1E3F5, 0x1E3FD, 0x1E405, 0x1E40D, 0x1E415, 0x468E8, 0x5195C, 0x45F44, 0x59630, 0x59970] +StaticPokemonFossils=[0x61064, 0x61068, 0x6106C] +StaticPokemonGifts=[0x51DA6, 0x49323] +StaticPokemonGameCorner[]=[0x52851,0x52984] // Abra +StaticPokemonGameCorner[]=[0x52852,0x52986] // Clefairy +StaticPokemonGameCorner[]=[0x52853,0x52988] // Nidorina/Nidorino +StaticPokemonGameCorner[]=[0x5285C,0x5298A] // Dratini/Pinsir +StaticPokemonGameCorner[]=[0x5285D,0x5298C] // Scyther/Dratini +StaticPokemonGameCorner[]=[0x5285E,0x5298E] // Porygon + +[Blue (G)] +Game=POKEMON BLUE +Version=0 +NonJapanese=1 +Type=RB +CopyStaticPokemon=1 +CRCInHeader=0x2EBC +CopyFrom=Red (G) +HiddenItemRoutine=0x766D9 +StarterOffsets2=[0x1D104, 0x19596, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CA8, 0x6060E, 0x61450, 0x75FF2] +StarterOffsets3=[0x1D115, 0x1959E, 0x1CDD0, 0x1D130, 0x39CFA, 0x50FB1, 0x510DB, 0x51CB0, 0x60616, 0x61458, 0x75FFA] + +[Red (I)] +Game=POKEMON RED +Version=0 +NonJapanese=1 +Type=RB +CRCInHeader=0x89D2 +CopyFrom=Red (U) +ExtraTableFile=rby_espita +OldRodOffset=0xE24C +GoodRodOffset=0xE279 +SuperRodTableOffset=0xE913 +MapNameTableOffset=0x71313 +PokedexOrder=0x40FB6 +TypeEffectivenessOffset=0x3E477 +PokemonMovesetsTableOffset=0x3B096 +ItemNamesOffset=0x472D +StarterOffsets1=[0x1D126, 0x1CC84, 0x1D10E, 0x39D20, 0x50FB3, 0x510DD] +StarterOffsets2=[0x1D104, 0x19596, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CAE, 0x6060E, 0x61450, 0x76015] +StarterOffsets3=[0x1D115, 0x1959E, 0x1CDD0, 0x1D130, 0x39D1A, 0x50FB1, 0x510DB, 0x51CB6, 0x60616, 0x61458, 0x7601D] +CanChangeStarterText=0 +PokedexRamOffset=0xD2FC +TrainerDataTableOffset=0x39D63 +ExtraTrainerMovesTableOffset=0x39D5A +GymLeaderMovesTableOffset=0x39D4B +TMMovesOffset=0x13798 +TrainerClassNamesOffsets=[0x27ECD, 0x399FF] +IntroPokemonOffset=0x61CC +IntroCryOffset=0x1C73 +HiddenItemRoutine=0x766FF +TradeTableOffset=0x71BBB +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1DD4A, 0x5CF5A, 0x5CF12] +StaticPokemonOverworlds=[0x1E3D6, 0x1E3DE, 0x1E3E6, 0x1E3EE, 0x1E3F6, 0x1E3FE, 0x1E406, 0x1E40E, 0x1E416, 0x468E8, 0x51962, 0x45F44, 0x59630, 0x59970] +StaticPokemonFossils=[0x61064, 0x61068, 0x6106C] +StaticPokemonGifts=[0x51DAC, 0x49322] +StaticPokemonGameCorner[]=[0x52858,0x5298C] // Abra +StaticPokemonGameCorner[]=[0x52859,0x5298E] // Clefairy +StaticPokemonGameCorner[]=[0x5285A,0x52990] // Nidorina/Nidorino +StaticPokemonGameCorner[]=[0x52863,0x52992] // Dratini/Pinsir +StaticPokemonGameCorner[]=[0x52864,0x52994] // Scyther/Dratini +StaticPokemonGameCorner[]=[0x52865,0x52996] // Porygon + +[Blue (I)] +Game=POKEMON BLUE +Version=0 +NonJapanese=1 +Type=RB +CopyStaticPokemon=1 +CRCInHeader=0x5E9C +CopyFrom=Red (I) +HiddenItemRoutine=0x766FD +StarterOffsets2=[0x1D104, 0x19596, 0x1CC88, 0x1CDC8, 0x1D11F, 0x50FAF, 0x510D9, 0x51CAE, 0x6060E, 0x61450, 0x76013] +StarterOffsets3=[0x1D115, 0x1959E, 0x1CDD0, 0x1D130, 0x39D1A, 0x50FB1, 0x510DB, 0x51CB6, 0x60616, 0x61458, 0x7601B] +IntroPokemonOffset=0x61CB + +[Yellow (F)] +Game=POKEMON YELAPSF +Version=0 +NonJapanese=1 +Type=Yellow +CopyFrom=Yellow (U) +ExtraTableFile=rby_freger +OldRodOffset=0xE0EF +GoodRodOffset=0xE11C +SuperRodTableOffset=0xF5ED4 +MapNameTableOffset=0x713A0 +PokedexOrder=0x41036 +TypeEffectivenessOffset=0x3E610 +PokemonMovesetsTableOffset=0x3B1E8 +ItemNamesOffset=0x45B8 +StarterOffsets2=[0x3A28D] +TrainerDataTableOffset=0x39DD4 +ExtraTrainerMovesTableOffset=0x39C6E +TMMovesOffset=0x1233C +TrainerClassNamesOffsets=[0x27E74, 0x3997E] +IntroPokemonOffset=0x5F64 +IntroCryOffset=0x1A48 +SpecialMapPointerTable=0xF25CF +HiddenItemRoutine=0x75F69 +TradeTableOffset=0x71BEE +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1D655, 0x5CE4D, 0x5CE05] +StaticPokemonOverworlds=[0x1DCE2, 0x1DCEA, 0x1DCF2, 0x1DCFA, 0x1DD02, 0x1DD0A, 0x1DD12, 0x1DD1A, 0x1DD22, 0x46B5A, 0x519A8, 0x461A5, 0x594CC, 0x5980C] +StaticPokemonFossils=[0x61050, 0x61054, 0x61058] +StaticPokemonGifts=[0x51DD9, 0xF2102, 0x1CF8C, 0x515B2, 0xF1987] +StaticPokemonGameCorner[] = [0x527BD,0x528EF] // Abra +StaticPokemonGameCorner[] = [0x527BE,0x528F1] // Vulpix +StaticPokemonGameCorner[] = [0x527BF,0x528F3] // Wigglytuff +StaticPokemonGameCorner[] = [0x527C8,0x528F5] // Scyther +StaticPokemonGameCorner[] = [0x527C9,0x528F7] // Pinsir +StaticPokemonGameCorner[] = [0x527CA,0x528F9] // Porygon + +[Yellow (G)] +Game=POKEMON YELAPSD +Version=0 +NonJapanese=1 +Type=Yellow +CopyFrom=Yellow (U) +ExtraTableFile=rby_freger +OldRodOffset=0xE0F4 +GoodRodOffset=0xE121 +SuperRodTableOffset=0xF5ED3 +MapNameTableOffset=0x71399 +PokedexOrder=0x41023 +TypeEffectivenessOffset=0x3E609 +PokemonMovesetsTableOffset=0x3B1F2 +ItemNamesOffset=0x45B8 +StarterOffsets2=[0x3A297] +TrainerDataTableOffset=0x39DDE +ExtraTrainerMovesTableOffset=0x39C78 +TMMovesOffset=0x1232E +TrainerClassNamesOffsets=[0x27E78, 0x3997E] +IntroPokemonOffset=0x5EF0 +IntroCryOffset=0x1A51 +SpecialMapPointerTable=0xF25EE +HiddenItemRoutine=0x75F6E +TradeTableOffset=0x71BFC +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1D652, 0x5CE53, 0x5CE0B] +StaticPokemonOverworlds=[0x1DCDF, 0x1DCE7, 0x1DCEF, 0x1DCF7, 0x1DCFF, 0x1DD07, 0x1DD0F, 0x1DD17, 0x1DD1F, 0x46B5A, 0x5199E, 0x461A5, 0x594CC, 0x5980C] +StaticPokemonFossils=[0x61050, 0x61054, 0x61058] +StaticPokemonGifts=[0x51DCF, 0xF2121, 0x1CF8C, 0x515A8, 0xF19A6] +StaticPokemonGameCorner[] = [0x527B2,0x528E4] // Abra +StaticPokemonGameCorner[] = [0x527B3,0x528E6] // Vulpix +StaticPokemonGameCorner[] = [0x527B4,0x528E8] // Wigglytuff +StaticPokemonGameCorner[] = [0x527BD,0x528EA] // Scyther +StaticPokemonGameCorner[] = [0x527BE,0x528EC] // Pinsir +StaticPokemonGameCorner[] = [0x527BF,0x528EE] // Porygon + +[Yellow (I)] +Game=POKEMON YELAPSI +Version=0 +NonJapanese=1 +Type=Yellow +CopyFrom=Yellow (U) +ExtraTableFile=rby_espita +OldRodOffset=0xE103 +GoodRodOffset=0xE130 +SuperRodTableOffset=0xF5ECE +MapNameTableOffset=0x7139C +PokedexOrder=0x41043 +TypeEffectivenessOffset=0x3E60C +PokemonMovesetsTableOffset=0x3B20D +ItemNamesOffset=0x45B8 +StarterOffsets2=[0x3A2B2] +TrainerDataTableOffset=0x39DF9 +ExtraTrainerMovesTableOffset=0x39C93 +TMMovesOffset=0x12352 +TrainerClassNamesOffsets=[0x27E82, 0x3997E] +IntroPokemonOffset=0x5F2A +IntroCryOffset=0x1A4C +SpecialMapPointerTable=0xF2609 +HiddenItemRoutine=0x75F70 +TradeTableOffset=0x71C5D +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1D653, 0x5CE50, 0x5CE08] +StaticPokemonOverworlds=[0x1DCE0, 0x1DCE8, 0x1DCF0, 0x1DCF8, 0x1DD00, 0x1DD08, 0x1DD10, 0x1DD18, 0x1DD20, 0x46B5A, 0x519A4, 0x461A5, 0x594CC, 0x5980C] +StaticPokemonFossils=[0x61050, 0x61054, 0x61058] +StaticPokemonGifts=[0x51DD5, 0xF213C, 0x1CF8C, 0x515AE, 0xF19C1] +StaticPokemonGameCorner[] = [0x527B9,0x528EC] // Abra +StaticPokemonGameCorner[] = [0x527BA,0x528EE] // Vulpix +StaticPokemonGameCorner[] = [0x527BB,0x528F0] // Wigglytuff +StaticPokemonGameCorner[] = [0x527C4,0x528F2] // Scyther +StaticPokemonGameCorner[] = [0x527C5,0x528F4] // Pinsir +StaticPokemonGameCorner[] = [0x527C6,0x528F6] // Porygon + +[Yellow (S)] +Game=POKEMON YELAPSS +Version=0 +NonJapanese=1 +Type=Yellow +CopyFrom=Yellow (U) +ExtraTableFile=rby_espita +OldRodOffset=0xE102 +GoodRodOffset=0xE12F +SuperRodTableOffset=0xF5ED2 +MapNameTableOffset=0x7139F +PokedexOrder=0x41041 +TypeEffectivenessOffset=0x3E60A +PokemonMovesetsTableOffset=0x3B1F2 +ItemNamesOffset=0x45B8 +StarterOffsets2=[0x3A297] +TrainerDataTableOffset=0x39DDE +ExtraTrainerMovesTableOffset=0x39C78 +TMMovesOffset=0x12352 +TrainerClassNamesOffsets=[0x27E80, 0x3997E] +IntroPokemonOffset=0x5F22 +IntroCryOffset=0x1A4B +SpecialMapPointerTable=0xF2609 +HiddenItemRoutine=0x75F6F +TradeTableOffset=0x71C0D +StaticPokemonSupport=1 +StaticPokemonPokeballs=[0x1D653, 0x5CE59, 0x5CE11] +StaticPokemonOverworlds=[0x1DCE0, 0x1DCE8, 0x1DCF0, 0x1DCF8, 0x1DD00, 0x1DD08, 0x1DD10, 0x1DD18, 0x1DD20, 0x46B5A, 0x519A1, 0x461A5, 0x594CC, 0x5980C] +StaticPokemonFossils=[0x61050, 0x61054, 0x61058] +StaticPokemonGifts=[0x51DD2, 0xF213C, 0x1CF8C, 0x515AB, 0xF19C1] +StaticPokemonGameCorner[] = [0x527B7,0x528E9] // Abra +StaticPokemonGameCorner[] = [0x527B8,0x528EB] // Vulpix +StaticPokemonGameCorner[] = [0x527B9,0x528ED] // Wigglytuff +StaticPokemonGameCorner[] = [0x527C2,0x528EF] // Scyther +StaticPokemonGameCorner[] = [0x527C3,0x528F1] // Pinsir +StaticPokemonGameCorner[] = [0x527C4,0x528F3] // Porygon \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gen2_offsets.ini b/src/com/dabomstew/pkrandom/config/gen2_offsets.ini new file mode 100755 index 000000000..c4fb04608 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gen2_offsets.ini @@ -0,0 +1,604 @@ +[Gold (U)] +Game=AAUE +Version=0 +NonJapanese=1 +Type=GS +ExtraTableFile=gsc_english +BWXPTweak=gs_en_bwxp +PokemonNamesOffset=0x1B0B74 +PokemonNamesLength=10 +PokemonStatsOffset=0x51B0B +WildPokemonOffset=0x2AB35 +FishingWildsOffset=0x92A52 +HeadbuttWildsOffset=0xBA47C +HeadbuttTableSize=7 +BCCWildsOffset=0x97BB8 +FleeingDataOffset=0x3C551 +MoveDataOffset=0x41AFF +MoveNamesOffset=0x1B1574 +ItemNamesOffset=0x1B0000 +PokemonMovesetsTableOffset=0x427BD +SupportsFourStartingMoves=0 +StarterOffsets1=[0x1800D2, 0x1800D4, 0x1800EB, 0x1800F6] +StarterOffsets2=[0x180114, 0x180116, 0x18012D, 0x180138] +StarterOffsets3=[0x180150, 0x180152, 0x180169, 0x180174] +StarterHeldItems=[0x1800F8, 0x18013A, 0x180176] +CanChangeStarterText=1 +CanChangeTrainerText=1 +TrainerClassAmount=0x42 +TrainerDataTableOffset=0x3993E +TrainerDataClassCounts=[0, 1, 1, 1, 1, 1, 1, 1, 1, 15, 0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 5, 1, 12, 18, 19, 15, 1, 19, 20, 16, 13, 31, 5, 2, 3, 1, 14, 22, 21, 19, 12, 12, 6, 2, 20, 9, 1, 3, 8, 5, 9, 4, 12, 21, 19, 2, 9, 7, 3, 12, 6, 8, 5, 1, 1, 2, 5] +TMMovesOffset=0x11A66 +TrainerClassNamesOffset=0x1B0955 +IntroSpriteOffset=0x5FDE +IntroCryOffset=0x6061 +MapHeaders=0x940ED +LandmarkTableOffset=0x92382 +LandmarkCount=95 +TradeTableOffset=0xFCC24 +TradeTableSize=6 +TradeNameLength=11 +TradeOTLength=11 +TradesUnused=[] +StaticPokemonSupport=1 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x111775, 0x114DBD, 0x114DE8, 0x114E13, 0x11C1B6, 0x124F7A, 0x12E1D6, 0x13D2AB, 0x16E929] +StaticPokemonRocketBase=[0x1146FE, 0x114711, 0x114724] +StaticPokemonGifts=[0x73E6, 0x119F20, 0x15924F, 0x1599FC, 0x15CC10] +StaticPokemonRoamers=[0x2A7D8, 0x2A7DD, 0x2A7E2] +StaticPokemonGameCorner[]=[0x15E8B7, 0x15E93D] // Abra +StaticPokemonGameCorner[]=[0x15E8E5, 0x15E94D] // Ekans +StaticPokemonGameCorner[]=[0x15E913, 0x15E95D] // Dratini +StaticPokemonGameCorner[]=[0x15E99C, 0x15EA22] // Abra +StaticPokemonGameCorner[]=[0x15E9CA, 0x15EA32] // Sandshrew +StaticPokemonGameCorner[]=[0x15E9F8, 0x15EA42] // Dratini +StaticPokemonGameCorner[]=[0x179B9C, 0x179C22] // Mr.Mime +StaticPokemonGameCorner[]=[0x179BCA, 0x179C32] // Eevee +StaticPokemonGameCorner[]=[0x179BF8, 0x179C42] // Porygon +StaticPokemonCopy[]=[0x11C1B6,0x11C1C1] // Silver Lugia +StaticPokemonCopy[]=[0x16E929,0x16E934] // Silver Ho-Oh +TMText[]=[1,0x1755D0,That is\n%m.\e] +TMText[]=[3,0x17933A,TM03 is\n%m.\pIt's a terrifying\nmove!\e] +TMText[]=[5,0x12CC1A,WROOOAR!\nIT'S %m!\e] +TMText[]=[6,0x17031D,JANINE: You're so\ntough! I have a \lspecial gift!\pIt's %m!\e] +TMText[]=[7,0x151346,MANAGER: TM07 is\nmy %m.\pIt's a powerful\ntechnique!\e] +TMText[]=[8,0x12E465,That happens to be\n%m.\pIf any rocks are\nin your way, find\lROCK SMASH!\e] +TMText[]=[10,0x14D5B5,Do you see it? It\n is %m!\e] +TMText[]=[11,0x10DEC0,It's %m.\nUse it wisely.\e] +TMText[]=[12,0x15F058,It's %m.\pUse it on\nenemy [POKé]MON.\e] +TMText[]=[13,0x14519F,That there's\n%m.\pIt's a rare move.\e] +TMText[]=[16,0x1456C0,That TM contains\n%m.\pIt demonstrates\nthe harshness of\lwinter.\e] +TMText[]=[19,0x17A052,ERIKA: That was a\ndelightful match.\pI felt inspired.\nPlease, I wish you\lto have this TM.\pIt's %m.\pIt is a wonderful\nmove!\pPlease use it if\nit pleases you…\e] +TMText[]=[23,0x144387,…That teaches\n%m.\e] +TMText[]=[24,0x11C6D7,That contains\n%m.\pIf you don't want\nit, you don't have\lto take it.\e] +TMText[]=[24,0x14C4A8,That contains\n%m.\pIf you don't want\nit, you don't have\lto take it.\e] +TMText[]=[29,0x184B57,TM29 is\n%m.\pIt may be\nuseful.\e] +TMText[]=[30,0x1493D7,It's %m.\pUse it if it\nappeals to you.\e] +TMText[]=[31,0x1583B6,By using a TM, a\n[POKé]MON will\pinstantly learn a\nnew move.\pThink before you\nact--a TM can be\lused only once.\pTM31 contains\n%m.\e] +TMText[]=[37,0x18244E,TM37 happens to be\n%m.\pIt's for advanced\ntrainers only.\pUse it if you\ndare. Good luck!\e] +TMText[]=[42,0x138344,TM42 contains\n%m…\p…Zzz…\e] +TMText[]=[45,0x15C308,It's %m!\pIsn't it just per-\nfect for a cutie\llike me?\e] +TMText[]=[49,0x155061,TM49 contains\n%m.\pIsn't it great?\nI discovered it!\e] +TMText[]=[50,0x129DBC,TM50 is\n%m.\pOoooh…\nIt's scary…\pI don't want to\nhave bad dreams.\e] + +[Silver (U)] +Game=AAXE +Version=0 +NonJapanese=1 +Type=GS +CopyTMText=1 +CopyFrom=Gold (U) +BWXPTweak=gs_en_bwxp +StaticPokemonSupport=1 +StaticPokemonOverworlds=[0x111775, 0x114DBD, 0x114DE8, 0x114E13, 0x11C1B6, 0x124F7A, 0x12E1D6, 0x13D2AB, 0x16E929] +StaticPokemonRocketBase=[0x1146FE, 0x114711, 0x114724] +StaticPokemonGifts=[0x73AC, 0x119F20, 0x15924F, 0x1599FC, 0x15CC10] +StaticPokemonRoamers=[0x2A7D8, 0x2A7DD, 0x2A7E2] +StaticPokemonGameCorner[]=[0x15E8B7, 0x15E93D] // Abra +StaticPokemonGameCorner[]=[0x15E8E5, 0x15E94D] // Ekans +StaticPokemonGameCorner[]=[0x15E913, 0x15E95D] // Dratini +StaticPokemonGameCorner[]=[0x15E99C, 0x15EA22] // Abra +StaticPokemonGameCorner[]=[0x15E9CA, 0x15EA32] // Sandshrew +StaticPokemonGameCorner[]=[0x15E9F8, 0x15EA42] // Dratini +StaticPokemonGameCorner[]=[0x179B9C, 0x179C22] // Mr.Mime +StaticPokemonGameCorner[]=[0x179BCA, 0x179C32] // Eevee +StaticPokemonGameCorner[]=[0x179BF8, 0x179C42] // Porygon +StaticPokemonCopy[]=[0x11C1B6,0x11C1C1] // Silver Lugia +StaticPokemonCopy[]=[0x16E929,0x16E934] // Silver Ho-Oh + +[Crystal (U)] +Game=BYTE +Version=0 +NonJapanese=1 +Type=Crystal +ExtraTableFile=gsc_english +BWXPTweak=crystal_en_bwxp +PokemonNamesOffset=0x53384 +PokemonNamesLength=10 +PokemonStatsOffset=0x51424 +WildPokemonOffset=0x2A5E9 +FishingWildsOffset=0x924E3 +HeadbuttWildsOffset=0xB82FA +HeadbuttTableSize=13 +BCCWildsOffset=0x97D87 +FleeingDataOffset=0x3C59A +MoveDataOffset=0x41AFC +MoveNamesOffset=0x1C9F29 +ItemNamesOffset=0x1C8000 +PokemonMovesetsTableOffset=0x425B1 +SupportsFourStartingMoves=1 +StarterOffsets1=[0x78C7F, 0x78C81, 0x78C98, 0x78CA3] +StarterOffsets2=[0x78CC1, 0x78CC3, 0x78CDA, 0x78CE5] +StarterOffsets3=[0x78CFD, 0x78CFF, 0x78D16, 0x78D21] +StarterHeldItems=[0x78CA5, 0x78CE7, 0x78D23] +CanChangeStarterText=1 +CanChangeTrainerText=1 +TrainerClassAmount=0x43 +TrainerDataTableOffset=0x39999 +TrainerDataClassCounts=[0, 1, 1, 1, 1, 1, 1, 1, 1, 15, 0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 5, 1, 14, 24, 19, 17, 1, 20, 21, 17, 15, 31, 5, 2, 3, 1, 19, 25, 21, 19, 13, 14, 6, 2, 22, 9, 1, 3, 8, 6, 9, 4, 12, 26, 22, 2, 12, 7, 3, 14, 6, 10, 6, 1, 1, 2, 5, 1] +TMMovesOffset=0x1167A +TrainerClassNamesOffset=0x2C1EF +IntroSpriteOffset=0x5FD2 +IntroCryOffset=0x6050 +MapHeaders=0x94000 +LandmarkTableOffset=0x1CA8C3 +LandmarkCount=96 +TradeTableOffset=0xFCE58 +TradeTableSize=7 +TradeNameLength=11 +TradeOTLength=11 +TradesUnused=[] +StaticPokemonSupport=1 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x5A324, 0x6D105, 0x6D130, 0x6D15B, 0x18C52A, 0x7006E, 0x194068, 0x1AA9B8, 0x77256, 0x1850EA] +StaticPokemonRocketBase=[0x6CA43, 0x6CA56, 0x6CA69] +StaticPokemonGifts=[0x730A, 0x7E22A, 0x694E2, 0x69D65, 0x54C06, 0x18D1D7] +StaticPokemonOddEgg=[0x1FB56E, 0x1FB5A9, 0x1FB5E4, 0x1FB61F, 0x1FB65A, 0x1FB695, 0x1FB6D0, 0x1FB70B, 0x1FB746, 0x1FB781, 0x1FB7BC, 0x1FB7F7, 0x1FB832, 0x1FB86D] +StaticPokemonRoamers=[0x2A2A1, 0x2A2A6] +StaticPokemonGameCorner[]=[0x56D34, 0x56DBA] // Abra +StaticPokemonGameCorner[]=[0x56D62, 0x56DCA] // Cubone +StaticPokemonGameCorner[]=[0x56D90, 0x56DDA] // Wobbuffet +StaticPokemonGameCorner[]=[0x727FB, 0x72881] // Pikachu +StaticPokemonGameCorner[]=[0x72829, 0x72891] // Porygon +StaticPokemonGameCorner[]=[0x72857, 0x728A1] // Larvitar +MoveTutorMoves=[0x492B4, 0x492B7, 0x492B1] +MoveTutorMenuOffset=0x19896C +MoveTutorMenuNewSpace=0x19BB00 +TMText[]=[1,0x9D8DB,That is\n%m.\e] +TMText[]=[3,0x71DB4,TM03 is\n%m.\pIt's a terrifying\nmove!\e] +TMText[]=[5,0x19118D,WROOOAR!\nIT'S %m!\e] +TMText[]=[6,0x196003,JANINE: You're so\ntough! I have a \lspecial gift!\pIt's %m!\e] +TMText[]=[7,0x1893F5,MANAGER: TM07 is\nmy %m.\pIt's a powerful\ntechnique!\e] +TMText[]=[8,0x19452D,That happens to be\n%m.\pIf any rocks are\nin your way, find\lROCK SMASH!\e] +TMText[]=[10,0x19A5DF,Do you see it? It\n is %m!\e] +TMText[]=[11,0x5E822,It's %m.\nUse it wisely.\e] +TMText[]=[12,0x62DF7,It's %m.\pUse it on\nenemy [POKé]MON.\e] +TMText[]=[13,0x9D1C8,That there's\n%m.\pIt's a rare move.\e] +TMText[]=[16,0x199DF0,That TM contains\n%m.\pIt demonstrates\nthe harshness of\lwinter.\e] +TMText[]=[19,0x72CB1,ERIKA: That was a\ndelightful match.\pI felt inspired.\nPlease, I wish you\lto have this TM.\pIt's %m.\pIt is a wonderful\nmove!\pPlease use it if\nit pleases you…\e] +TMText[]=[23,0x9C3A6,…That teaches\n%m.\e] +TMText[]=[24,0x18CA0E,That contains\n%m.\pIf you don't want\nit, you don't have\lto take it.\e] +TMText[]=[24,0x1951D2,That contains\n%m.\pIf you don't want\nit, you don't have\lto take it.\e] +TMText[]=[29,0x18A7BC,TM29 is\n%m.\pIt may be\nuseful.\e] +TMText[]=[30,0x9A0ED,It's %m.\pUse it if it\nappeals to you.\e] +TMText[]=[31,0x68649,By using a TM, a\n[POKé]MON will\pinstantly learn a\nnew move.\pThink before you\nact--a TM can be\lused only once.\pTM31 contains\n%m.\e] +TMText[]=[37,0x7B490,TM37 happens to be\n%m.\pIt's for advanced\ntrainers only.\pUse it if you\ndare. Good luck!\e] +TMText[]=[42,0x1A9D87,TM42 contains\n%m…\p…Zzz…\e] +TMText[]=[45,0x54303,It's %m!\pIsn't it just per-\nfect for a cutie\llike me?\e] +TMText[]=[49,0x18EEFB,TM49 contains\n%m.\pIsn't it great?\nI discovered it!\e] +TMText[]=[50,0x1A5897,TM50 is\n%m.\pOoooh…\nIt's scary…\pI don't want to\nhave bad dreams.\e] + +[Crystal (U 1.1)] +Game=BYTE +Version=1 +NonJapanese=1 +Type=GS +CopyStaticPokemon=1 +CopyFrom=Crystal (U) +BWXPTweak=crystal_en_bwxp + +[Gold (J 1.0)] +Game=AAUJ +Version=0 +NonJapanese=0 +Type=GS +PokemonNamesOffset=0x53A09 +PokemonNamesLength=5 +PokemonStatsOffset=0x51AA9 +WildPokemonOffset=0x2AC1B +FishingWildsOffset=0x929A1 +HeadbuttWildsOffset=0xBA47C +HeadbuttTableSize=7 +BCCWildsOffset=0x97BDC +FleeingDataOffset=0x3C551 +MoveDataOffset=0x41C6D +MoveNamesOffset=0x4163E +ItemNamesOffset=0x7293 +PokemonMovesetsTableOffset=0x4295F +SupportsFourStartingMoves=0 +StarterOffsets1=[0x4E598, 0x4E59A, 0x4E5B1, 0x4E5BC] +StarterOffsets2=[0x4E5DA, 0x4E5DC, 0x4E5F3, 0x4E5FE] +StarterOffsets3=[0x4E616, 0x4E618, 0x4E62F, 0x4E63A] +StarterHeldItems=[0x4E5BE, 0x4E600, 0x4E63C] +CanChangeStarterText=0 +CanChangeTrainerText=0 +TrainerClassAmount=0x42 +TrainerDataTableOffset=0x3995C +TrainerDataClassCounts=[0, 1, 1, 1, 1, 1, 1, 1, 1, 15, 0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 5, 1, 12, 18, 19, 15, 1, 19, 20, 16, 13, 31, 5, 2, 3, 1, 14, 22, 21, 19, 12, 12, 6, 2, 20, 9, 1, 3, 8, 5, 9, 4, 12, 21, 19, 2, 9, 7, 3, 12, 6, 8, 5, 1, 1, 2, 5] +TMMovesOffset=0x11A00 +TrainerClassNamesOffset=0x2D2D6 +IntroSpriteOffset=0x5FEC +IntroCryOffset=0x60ED +MapHeaders=0x940ED +LandmarkTableOffset=0x924B6 +LandmarkCount=95 +TradeTableOffset=0xFCC23 +TradeTableSize=6 +TradeNameLength=4 +TradeOTLength=3 +TradesUnused=[] +StaticPokemonSupport=0 + +[Gold (J 1.1)] +Game=AAUJ +Version=1 +NonJapanese=0 +Type=GS +CopyFrom=Gold (J 1.0) + +[Silver (J 1.0)] +Game=AAXJ +Version=0 +NonJapanese=0 +Type=GS +CopyFrom=Gold (J 1.0) + +[Silver (J 1.1)] +Game=AAXJ +Version=1 +NonJapanese=0 +Type=GS +CopyFrom=Silver (J 1.0) + +[Crystal (J)] +Game=BXTJ +Version=0 +NonJapanese=0 +Type=Crystal +PokemonNamesOffset=0x5341A +PokemonNamesLength=5 +PokemonStatsOffset=0x514BA +WildPokemonOffset=0x2A60C +FishingWildsOffset=0x92A56 +HeadbuttWildsOffset=0xB82DA +HeadbuttTableSize=13 +BCCWildsOffset=0x97D49 +FleeingDataOffset=0x3C599 +MoveDataOffset=0x41C6A +MoveNamesOffset=0x4163B +ItemNamesOffset=0x70FA +PokemonMovesetsTableOffset=0x42753 +SupportsFourStartingMoves=0 +StarterOffsets1=[0x6E294, 0x6E296, 0x6E2AD, 0x6E2B8] +StarterOffsets2=[0x6E2D6, 0x6E2D8, 0x6E2EF, 0x6E2FA] +StarterOffsets3=[0x6E312, 0x6E314, 0x6E32B, 0x6E336] +StarterHeldItems=[0x6E2BA, 0x6E2FC, 0x6E338] +CanChangeStarterText=0 +CanChangeTrainerText=0 +TrainerClassAmount=0x43 +TrainerDataTableOffset=0x399BA +TrainerDataClassCounts=[0, 1, 1, 1, 1, 1, 1, 1, 1, 15, 0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 5, 1, 14, 24, 19, 17, 1, 20, 21, 17, 15, 31, 5, 2, 3, 1, 19, 25, 21, 19, 13, 14, 6, 2, 22, 9, 1, 3, 8, 6, 9, 4, 12, 26, 22, 2, 12, 7, 3, 14, 6, 10, 6, 1, 1, 2, 5, 1] +TMMovesOffset=0x11614 +TrainerClassNamesOffset=0x2D319 +MapHeaders=0x94000 +LandmarkTableOffset=0x92557 +LandmarkCount=96 +TradeTableOffset=0xFCE57 +TradeTableSize=7 +TradeNameLength=4 +TradeOTLength=3 +TradesUnused=[] +StaticPokemonSupport=0 +IntroSpriteOffset=0x5FC2 +IntroCryOffset=0x60BE +MoveTutorMoves=[0x49206, 0x49209, 0x49203] + +[Gold (F)] +Game=AAUF +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (U) +ExtraTableFile=gsc_freger +PokemonNamesOffset=0x1B0BC5 +PokemonStatsOffset=0x51B10 +WildPokemonOffset=0x2ABB2 +FishingWildsOffset=0x92454 +BCCWildsOffset=0x97BD7 +MoveDataOffset=0x41B0A +MoveNamesOffset=0x1B15C5 +PokemonMovesetsTableOffset=0x427C8 +TMMovesOffset=0x11A65 +TrainerClassNamesOffset=0x1B0995 +IntroSpriteOffset=0x6013 +IntroCryOffset=0x6096 +LandmarkTableOffset=0x9C02D +LandmarkCount=95 +StaticPokemonSupport=1 +CanChangeStarterText=0 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x1117EA, 0x114DA7, 0x114DD2, 0x114DFD, 0x11C1B6, 0x125055, 0x12E13B, 0x13D1AF, 0x16E929] +StaticPokemonRocketBase=[0x11470A, 0x11471D, 0x114730] +StaticPokemonGifts=[0x741B, 0x119EC2, 0x159572, 0x16035E, 0x15CEB9] +StaticPokemonRoamers=[0x2A855, 0x2A85A, 0x2A85F] +StaticPokemonCopy[]=[0x11C1B6,0x11C1C1] // Silver Lugia +StaticPokemonCopy[]=[0x16E929,0x16E934] // Silver Ho-Oh + +[Silver (F)] +Game=AAXF +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (F) + +[Gold (G)] +Game=AAUD +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (U) +ExtraTableFile=gsc_freger +PokemonNamesOffset=0x1B0B6B +PokemonStatsOffset=0x51B00 +WildPokemonOffset=0x2ABB5 +FishingWildsOffset=0x9245F +BCCWildsOffset=0x97BD7 +MoveDataOffset=0x41AF2 +MoveNamesOffset=0x1B156B +PokemonMovesetsTableOffset=0x427B0 +TMMovesOffset=0x11A5D +TrainerClassNamesOffset=0x1B0946 +IntroSpriteOffset=0x6016 +IntroCryOffset=0x6099 +LandmarkTableOffset=0x9C02D +LandmarkCount=95 +StaticPokemonSupport=1 +CanChangeStarterText=0 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x111A2C, 0x114E62, 0x114E8D, 0x114EB8, 0x11C1B6, 0x125104, 0x12E5FE, 0x13D4EE, 0x16EC32] +StaticPokemonRocketBase=[0x114719, 0x11472C, 0x11473F] +StaticPokemonGifts=[0x741D, 0x11A249, 0x159680, 0x1603CA, 0x15CE51] +StaticPokemonRoamers=[0x2A858, 0x2A85D, 0x2A862] +StaticPokemonCopy[]=[0x11C1B6,0x11C1C1] // Silver Lugia +StaticPokemonCopy[]=[0x16EC32,0x16EC3D] // Silver Ho-Oh + +[Silver (G)] +Game=AAXD +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (G) + +[Gold (S)] +Game=AAUS +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (U) +ExtraTableFile=gsc_espita +PokemonNamesOffset=0x1B0BB8 +PokemonStatsOffset=0x51B19 +WildPokemonOffset=0x2ABA0 +FishingWildsOffset=0x92464 +BCCWildsOffset=0x97BCF +MoveDataOffset=0x41B10 +MoveNamesOffset=0x1B15B8 +PokemonMovesetsTableOffset=0x427CE +TMMovesOffset=0x11A79 +TrainerClassNamesOffset=0x1B098B +StaticPokemonSupport=1 +CanChangeStarterText=0 +IntroSpriteOffset=0x6022 +IntroCryOffset=0x60A5 +LandmarkTableOffset=0x9C02D +LandmarkCount=95 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x111717, 0x114DD9, 0x114E04, 0x114E2F, 0x11C1B6, 0x124F53, 0x12E1D3, 0x13D2BE, 0x16E98D] +StaticPokemonRocketBase=[0x11470D, 0x114720, 0x114733] +StaticPokemonGifts=[0x742E, 0x119F0B, 0x15935C, 0x160329, 0x15CC68] +StaticPokemonRoamers=[0x2A843, 0x2A848, 0x2A84D] +StaticPokemonCopy[]=[0x11C1B6,0x11C1C1] // Silver Lugia +StaticPokemonCopy[]=[0x16E98D,0x16E998] // Silver Ho-Oh + +[Silver (S)] +Game=AAXS +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (S) + +[Gold (I)] +Game=AAUI +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (U) +ExtraTableFile=gsc_espita +PokemonNamesOffset=0x1B0BD2 +PokemonStatsOffset=0x51B19 +WildPokemonOffset=0x2AB99 +FishingWildsOffset=0x92447 +BCCWildsOffset=0x97BD5 +MoveDataOffset=0x41AFE +MoveNamesOffset=0x1B15D2 +PokemonMovesetsTableOffset=0x427BC +TMMovesOffset=0x11A65 +TrainerClassNamesOffset=0x1B099A +IntroSpriteOffset=0x6011 +IntroCryOffset=0x6094 +LandmarkTableOffset=0x9C02D +LandmarkCount=95 +StaticPokemonSupport=1 +CanChangeStarterText=0 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x1117CF, 0x114DF8, 0x114E23, 0x114E4E, 0x11C1B6, 0x124F0F, 0x12E26A, 0x13D2D9, 0x16E958] +StaticPokemonRocketBase=[0x1146DA, 0x1146ED, 0x114700] +StaticPokemonGifts=[0x7418, 0x119EF3, 0x1593B0, 0x160332, 0x15CC91] +StaticPokemonRoamers=[0x2A83C, 0x2A841, 0x2A846] +StaticPokemonCopy[]=[0x11C1B6,0x11C1C1] // Silver Lugia +StaticPokemonCopy[]=[0x16E958,0x16E963] // Silver Ho-Oh + +[Silver (I)] +Game=AAXI +Version=0 +NonJapanese=1 +Type=GS +CopyFrom=Gold (I) + +[Crystal (F)] +Game=BYTF +Version=0 +NonJapanese=1 +Type=Crystal +CopyFrom=Crystal (U) +ExtraTableFile=gsc_freger +PokemonNamesOffset=0x53377 +PokemonStatsOffset=0x51417 +WildPokemonOffset=0x2A5F2 +FishingWildsOffset=0x924FA +HeadbuttWildsOffset=0xB830C +BCCWildsOffset=0x97D88 +MoveDataOffset=0x41B07 +MoveNamesOffset=0x1C9F96 +PokemonMovesetsTableOffset=0x425BC +SupportsFourStartingMoves=1 +StarterOffsets1=[0x78C67, 0x78C69, 0x78C80, 0x78C8B] +StarterOffsets2=[0x78CA9, 0x78CAB, 0x78CC2, 0x78CCD] +StarterOffsets3=[0x78CE5, 0x78CE7, 0x78CFE, 0x78D09] +StarterHeldItems=[0x78C8D, 0x78CCF, 0x78D0B] +TMMovesOffset=0x11679 +TrainerClassNamesOffset=0x2C1EF +IntroSpriteOffset=0x5FEC +IntroCryOffset=0x606A +LandmarkTableOffset=0x1CA97F +LandmarkCount=96 +StaticPokemonSupport=1 +CanChangeStarterText=0 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x5A403, 0x6D111, 0x6D13C, 0x6D167, 0x18C521, 0x7006E, 0x194068, 0x1AA8ED, 0x7727F, 0x1851CF] +StaticPokemonRocketBase=[0x6CA7F, 0x6CA92, 0x6CAA5] +StaticPokemonGifts=[0x7324, 0x7E1C2, 0x697B9, 0x69FF3, 0x54E6C, 0x18D1A6] +StaticPokemonOddEgg=[0x1FB56E, 0x1FB5A9, 0x1FB5E4, 0x1FB61F, 0x1FB65A, 0x1FB695, 0x1FB6D0, 0x1FB70B, 0x1FB746, 0x1FB781, 0x1FB7BC, 0x1FB7F7, 0x1FB832, 0x1FB86D] +StaticPokemonRoamers=[0x2A2AA, 0x2A2AF] +MoveTutorMoves=[0x49212, 0x49215, 0x4920F] + +[Crystal (G)] +Game=BYTD +Version=0 +NonJapanese=1 +Type=Crystal +CopyFrom=Crystal (U) +ExtraTableFile=gsc_freger +PokemonNamesOffset=0x5336E +PokemonStatsOffset=0x5140E +WildPokemonOffset=0x2A5FE +FishingWildsOffset=0x92502 +HeadbuttWildsOffset=0xB830C +BCCWildsOffset=0x97D88 +MoveDataOffset=0x41AEC +MoveNamesOffset=0x1C9E9D +PokemonMovesetsTableOffset=0x425A1 +SupportsFourStartingMoves=1 +StarterOffsets1=[0x78DFA, 0x78DFC, 0x78E13, 0x78E1E] +StarterOffsets2=[0x78E3C, 0x78E3E, 0x78E55, 0x78E60] +StarterOffsets3=[0x78E78, 0x78E7A, 0x78E91, 0x78E9C] +StarterHeldItems=[0x78E20, 0x78E62, 0x78E9E] +TMMovesOffset=0x11671 +TrainerClassNamesOffset=0x2C1EF +IntroSpriteOffset=0x5FF3 +IntroCryOffset=0x6071 +LandmarkTableOffset=0x1CA8E5 +LandmarkCount=96 +StaticPokemonSupport=1 +CanChangeStarterText=0 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x5A6DB, 0x6D1FC, 0x6D227, 0x6D252, 0x18C5C5, 0x7006E, 0x194068, 0x1AAEC2, 0x77626, 0x185310] +StaticPokemonRocketBase=[0x6CAB0, 0x6CAC3, 0x6CAD6] +StaticPokemonGifts=[0x732B, 0x7E585, 0x69927, 0x6A2C8, 0x54E5A, 0x18D34F] +StaticPokemonOddEgg=[0x1FB56E, 0x1FB5A9, 0x1FB5E4, 0x1FB61F, 0x1FB65A, 0x1FB695, 0x1FB6D0, 0x1FB70B, 0x1FB746, 0x1FB781, 0x1FB7BC, 0x1FB7F7, 0x1FB832, 0x1FB86D] +StaticPokemonRoamers=[0x2A2B6, 0x2A2BB] +MoveTutorMoves=[0x49213, 0x49216, 0x49210] + +[Crystal (S)] +Game=BYTS +Version=0 +NonJapanese=1 +Type=Crystal +CopyFrom=Crystal (U) +ExtraTableFile=gsc_espita +PokemonNamesOffset=0x5338D +PokemonStatsOffset=0x5142D +WildPokemonOffset=0x2A5F0 +FishingWildsOffset=0x9250C +HeadbuttWildsOffset=0xB830C +BCCWildsOffset=0x97D80 +MoveDataOffset=0x41B0F +MoveNamesOffset=0x1CA045 +PokemonMovesetsTableOffset=0x425C4 +SupportsFourStartingMoves=1 +StarterOffsets1=[0x78C70, 0x78C72, 0x78C89, 0x78C94] +StarterOffsets2=[0x78CB2, 0x78CB4, 0x78CCB, 0x78CD6] +StarterOffsets3=[0x78CEE, 0x78CF0, 0x78D07, 0x78D12] +StarterHeldItems=[0x78C96, 0x78CD8, 0x78D14] +TMMovesOffset=0x1168D +TrainerClassNamesOffset=0x2C1EF +IntroSpriteOffset=0x5FF1 +IntroCryOffset=0x606F +LandmarkTableOffset=0x1CAAB4 +LandmarkCount=96 +StaticPokemonSupport=1 +CanChangeStarterText=0 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x5A3F1, 0x6D158, 0x6D183, 0x6D1AE, 0x18C54D, 0x7006E, 0x194068, 0x1AAA6F, 0x77257, 0x185139] +StaticPokemonRocketBase=[0x6CA79, 0x6CA8C, 0x6CA9F] +StaticPokemonGifts=[0x7329, 0x7E1F7, 0x695F2, 0x69E73, 0x54C5B, 0x18D224] +StaticPokemonOddEgg=[0x1FB56D, 0x1FB5A8, 0x1FB5E3, 0x1FB61E, 0x1FB659, 0x1FB694, 0x1FB6CF, 0x1FB70A, 0x1FB745, 0x1FB780, 0x1FB7BB, 0x1FB7F6, 0x1FB831, 0x1FB86C] +StaticPokemonRoamers=[0x2A2A8, 0x2A2AD] +MoveTutorMoves=[0x49211, 0x49214, 0x4920E] + +[Crystal (I)] +Game=BYTI +Version=0 +NonJapanese=1 +Type=Crystal +CopyFrom=Crystal (U) +ExtraTableFile=gsc_espita +PokemonNamesOffset=0x53393 +PokemonStatsOffset=0x51433 +WildPokemonOffset=0x2A5E0 +FishingWildsOffset=0x924F6 +HeadbuttWildsOffset=0xB830C +BCCWildsOffset=0x97D86 +MoveDataOffset=0x41AFB +MoveNamesOffset=0x1C9E86 +PokemonMovesetsTableOffset=0x425B0 +SupportsFourStartingMoves=1 +StarterOffsets1=[0x78CD4, 0x78CD6, 0x78CED, 0x78CF8] +StarterOffsets2=[0x78D16, 0x78D18, 0x78D2F, 0x78D3A] +StarterOffsets3=[0x78D52, 0x78D54, 0x78D6B, 0x78D76] +StarterHeldItems=[0x78CFA, 0x78D3C, 0x78D78] +TMMovesOffset=0x11679 +TrainerClassNamesOffset=0x2C1EF +IntroSpriteOffset=0x5FEB +IntroCryOffset=0x6069 +LandmarkTableOffset=0x1CA8B4 +LandmarkCount=96 +StaticPokemonSupport=1 +CanChangeStarterText=0 +GameCornerPokemonNameLength=11 +StaticPokemonOverworlds=[0x5A361, 0x6D16F, 0x6D19A, 0x6D1C5, 0x18C534, 0x7006E, 0x194068, 0x1AAA77, 0x772B8, 0x185187] +StaticPokemonRocketBase=[0x6CA47, 0x6CA5A, 0x6CA6D] +StaticPokemonGifts=[0x7323, 0x7E217, 0x69651, 0x69E5A, 0x54CA2, 0x18D1C3] +StaticPokemonOddEgg=[0x1FB56E, 0x1FB5A9, 0x1FB5E4, 0x1FB61F, 0x1FB65A, 0x1FB695, 0x1FB6D0, 0x1FB70B, 0x1FB746, 0x1FB781, 0x1FB7BC, 0x1FB7F7, 0x1FB832, 0x1FB86D] +StaticPokemonRoamers=[0x2A298, 0x2A29D] +MoveTutorMoves=[0x49215, 0x49218, 0x49212] \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gen3_offsets.ini b/src/com/dabomstew/pkrandom/config/gen3_offsets.ini new file mode 100755 index 000000000..ab14b6c47 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gen3_offsets.ini @@ -0,0 +1,1806 @@ +[Ruby (U)] +Game=AXVE +Version=0 +Type=Ruby +TableFile=gba_english +FreeSpace=0x700000 +PokemonNameLength=11 +PokemonStats=0x1FEC34 +PokemonMovesets=0x207BCC +PokemonTMHMCompat=0x1FD0F8 +PokemonEvolutions=0x203B90 +StarterPokemon=0x3F76C4 +StarterItems=0x821AA +TrainerData=0x1F04FC +TrainerEntrySize=40 +TrainerCount=0x2B6 +TrainerClassNames=0x1F0208 +TrainerClassCount=58 +TrainerClassNameLength=13 +TrainerNameLength=12 +ItemData=0x3C5564 +ItemCount=348 +ItemEntrySize=44 +MoveData=0x1FB138 +MoveDescriptions=0x3C09D8 +MoveNameLength=13 +MoveNames=0x1F8320 +AbilityNameLength=13 +AbilityNames=0x1FA248 +TmMoves=0x376504 +IntroCryOffset=0xA506 +IntroSpriteOffset=0xB2B8 +IntroPaletteOffset=0xB2C4 +IntroOtherOffset=0xB286 +PokemonFrontSprites=0x1E8354 +PokemonNormalPalettes=0x1EA5B4 +MapBankCount=0x22 +MapBankSizes=[54,5,5,6,7,7,8,7,7,13,8,17,10,24,13,13,14,2,2,2,3,1,1,1,86,44,12,2,1,13,1,1,3,1] +ItemBallPic=59 +TradeTableOffset=0x215AC4 +TradeTableSize=3 +TradesUnused=[] +StaticPokemonSupport=1 +StaticPokemon[]=[0x157663, 0x157691] // Lileep +StaticPokemon[]=[0x1576B6, 0x1576E4] // Anorith +StaticPokemon[]=[0x1A04A3, 0x15DE26, 0x15DE2D] // Groudon +StaticPokemon[]=[0x15CB89, 0x15CB92] // Regirock +StaticPokemon[]=[0x15EFA1, 0x15EFAA] // Regice +StaticPokemon[]=[0x15F054, 0x15F05D] // Registeel +StaticPokemon[]=[0x160BCE, 0x160BF4] // Latias (Southern Island) +StaticPokemon[]=[0x15F319, 0x15F320] // Rayquaza +StaticPokemon[]=[0x1A05E2, 0x1A05EB] // Kecleons on OW (7) +StaticPokemon[]=[0x1518E8, 0x1518F1] // Kecleon w/ Steven +StaticPokemon[]=[0x15E903, 0x15E90A] // Voltorb 1 +StaticPokemon[]=[0x15E921, 0x15E928] // Voltorb 2 +StaticPokemon[]=[0x15E93F, 0x15E946] // Voltorb 3 +StaticPokemon[]=[0x1A0500, 0x1A0507] // Electrode 1 +StaticPokemon[]=[0x1A051E, 0x1A0525] // Electrode 2 +StaticPokemon[]=[0x14E79A] // Wynaut Egg +StaticPokemon[]=[0x15AAAF, 0x15AABF] // Beldum +StaticPokemon[]=[0x163D99] // Castform + +[Ruby (E)] +Game=AXVE +Version=1 +Type=Ruby +CopyFrom=Ruby (U) +PokemonStats=0x1FEC4C +PokemonMovesets=0x207BE4 +PokemonTMHMCompat=0x1FD110 +PokemonEvolutions=0x203BA8 +StarterPokemon=0x3F76E0 +StarterItems=0x821CA +TrainerData=0x1F0514 +TrainerClassNames=0x1F0220 +ItemData=0x3C5580 +MoveData=0x1FB150 +MoveDescriptions=0x3C09F4 +MoveNames=0x1F8338 +AbilityNames=0x1FA260 +TmMoves=0x37651C +PokemonFrontSprites=0x1E836C +PokemonNormalPalettes=0x1EA5CC +TradeTableOffset=0x215ADC +StaticPokemonSupport=1 +StaticPokemon[]=[0x157683, 0x1576B1] +StaticPokemon[]=[0x1576D6, 0x157704] +StaticPokemon[]=[0x1A04C3, 0x15DE46, 0x15DE4D] +StaticPokemon[]=[0x15CBA9, 0x15CBB2] +StaticPokemon[]=[0x15EFC1, 0x15EFCA] +StaticPokemon[]=[0x15F074, 0x15F07D] +StaticPokemon[]=[0x160BEE, 0x160C14] +StaticPokemon[]=[0x15F339, 0x15F340] +StaticPokemon[]=[0x1A0602, 0x1A060B] +StaticPokemon[]=[0x151908, 0x151911] +StaticPokemon[]=[0x15E923, 0x15E92A] +StaticPokemon[]=[0x15E941, 0x15E948] +StaticPokemon[]=[0x15E95F, 0x15E966] +StaticPokemon[]=[0x1A0520, 0x1A0527] +StaticPokemon[]=[0x1A053E, 0x1A0545] +StaticPokemon[]=[0x14E7BA] +StaticPokemon[]=[0x15AACF, 0x15AADF] +StaticPokemon[]=[0x163DB9] + +[Ruby (U/E) 1.2] +Game=AXVE +Version=2 +Type=Ruby +CopyStaticPokemon=1 +CopyFrom=Ruby (E) + +[Sapphire (U)] +Game=AXPE +Version=0 +Type=Sapp +CopyFrom=Ruby (U) +PokemonStats=0x1FEBC4 +PokemonMovesets=0x207B5C +PokemonTMHMCompat=0x1FD088 +PokemonEvolutions=0x203B20 +StarterPokemon=0x3F771C +TrainerData=0x1F048C +TrainerClassNames=0x1F0198 +ItemData=0x3C55BC +MoveData=0x1FB0C8 +MoveDescriptions=0x3C0A30 +MoveNames=0x1F82B0 +AbilityNames=0x1FA1D8 +TmMoves=0x376494 +PokemonFrontSprites=0x1E82E4 +PokemonNormalPalettes=0x1EA544 +TradeTableOffset=0x215A54 +StaticPokemonSupport=1 +StaticPokemon[]=[0x1575F3, 0x157621] // Lileep +StaticPokemon[]=[0x157646, 0x157674] // Anorith +StaticPokemon[]=[0x1A0433, 0x15DDB6, 0x15DDBD] // Kyogre +StaticPokemon[]=[0x15CB19, 0x15CB22] // Regirock +StaticPokemon[]=[0x15EF31, 0x15EF3A] // Regice +StaticPokemon[]=[0x15EFE4, 0x15EFED] // Registeel +StaticPokemon[]=[0x160B5E, 0x160B84] // Latios (Southern Island) +StaticPokemon[]=[0x15F2A9, 0x15F2B0] // Rayquaza +StaticPokemon[]=[0x1A0572, 0x1A057B] // Kecleons on OW (7) +StaticPokemon[]=[0x15187C, 0x151885] // Kecleon w/ Steven +StaticPokemon[]=[0x15E893, 0x15E89A] // Voltorb 1 +StaticPokemon[]=[0x15E8B1, 0x15E8B8] // Voltorb 2 +StaticPokemon[]=[0x15E8CF, 0x15E8D6] // Voltorb 3 +StaticPokemon[]=[0x1A0490, 0x1A0497] // Electrode 1 +StaticPokemon[]=[0x1A04AE, 0x1A04B5] // Electrode 2 +StaticPokemon[]=[0x14E72E] // Wynaut Egg +StaticPokemon[]=[0x15AA3F, 0x15AA4F] // Beldum +StaticPokemon[]=[0x163D29] // Castform + +[Sapphire (E)] +Game=AXPE +Version=1 +Type=Sapp +CopyFrom=Sapphire (U) +PokemonStats=0x1FEBDC +PokemonMovesets=0x207B74 +PokemonTMHMCompat=0x1FD0A0 +PokemonEvolutions=0x203B38 +StarterPokemon=0x3F773C +StarterItems=0x821CA +TrainerData=0x1F04A4 +TrainerClassNames=0x1F01B0 +ItemData=0x3C55DC +MoveData=0x1FB0E0 +MoveDescriptions=0x3C0A50 +MoveNames=0x1F82C8 +AbilityNames=0x1FA1F0 +TmMoves=0x3764AC +PokemonFrontSprites=0x1E82FC +PokemonNormalPalettes=0x1EA55C +TradeTableOffset=0x215A6C +StaticPokemonSupport=1 +StaticPokemon[]=[0x157613, 0x157641] +StaticPokemon[]=[0x157666, 0x157694] +StaticPokemon[]=[0x1A0453, 0x15DDD6, 0x15DDDD] +StaticPokemon[]=[0x15CB39, 0x15CB42] +StaticPokemon[]=[0x15EF51, 0x15EF5A] +StaticPokemon[]=[0x15F004, 0x15F00D] +StaticPokemon[]=[0x160B7E, 0x160BA4] +StaticPokemon[]=[0x15F2C9, 0x15F2D0] +StaticPokemon[]=[0x1A0592, 0x1A059B] +StaticPokemon[]=[0x15189C, 0x1518A5] +StaticPokemon[]=[0x15E8B3, 0x15E8BA] +StaticPokemon[]=[0x15E8D1, 0x15E8D8] +StaticPokemon[]=[0x15E8EF, 0x15E8F6] +StaticPokemon[]=[0x1A04B0, 0x1A04B7] +StaticPokemon[]=[0x1A04CE, 0x1A04D5] +StaticPokemon[]=[0x14E74E] +StaticPokemon[]=[0x15AA5F, 0x15AA6F] +StaticPokemon[]=[0x163D49] + +[Sapphire (U/E) 1.2] +Game=AXPE +Version=2 +Type=Sapp +CopyStaticPokemon=1 +CopyFrom=Sapphire (E) + +[Emerald (U)] +Game=BPEE +Version=0 +Type=Em +TableFile=gba_english +FreeSpace=0xE40000 +PokemonNameLength=11 +PokemonStats=0x3203E8 +PokemonMovesets=0x329380 +PokemonTMHMCompat=0x31E8A0 +PokemonEvolutions=0x325344 +StarterPokemon=0x5B1DF8 +StarterItems=0xB117A +TrainerData=0x310030 +TrainerEntrySize=40 +TrainerCount=0x357 +TrainerClassNames=0x30FCD4 +TrainerClassCount=66 +TrainerClassNameLength=13 +TrainerNameLength=12 +ItemEntrySize=44 +ItemCount=376 +MoveDescriptions=0x61C524 +MoveNameLength=13 +AbilityNameLength=13 +TmMoves=0x615B94 +TmMovesDuplicate=0x616040 +MoveTutorData=0x61500C +MoveTutorMoves=30 +ItemImages=0x614410 +TmPals=[0xDB5E94, 0xDB5DF4, 0xDB604C, 0xDB5EBC, 0xDB5FD4, 0xDB6024, 0xDB5F0C, 0xDB5FFC, 0xDB5F84, 0xDB5FFC, 0xDB5F34, 0xDB5E44, 0xDB5F0C, 0xDB5FAC, 0xDB5E6C, 0xDB5EE4, 0xDB5E1C, 0xDB5F5C] +IntroCryOffset=0x30B0C +IntroSpriteOffset=0x31924 +MapBankCount=0x22 +MapBankSizes=[57,5,5,6,7,8,9,7,7,14,8,17,10,23,13,15,15,2,2,2,3,1,1,1,108,61,89,2,1,13,1,1,2,1] +ItemBallPic=59 +TradeTableOffset=0x338ED0 +TradeTableSize=4 +TradesUnused=[] +StaticPokemonSupport=1 +StaticPokemon[]=[0x211A1C, 0x211A41, 0x211A44, 0x211AC6, 0x211AD4] // Lileep +StaticPokemon[]=[0x211A2E, 0x211AE4, 0x211AE7, 0x211B69, 0x211B77] // Anorith +StaticPokemon[]=[0x23B032, 0x23B040, 0x23B095] // Kyogre +StaticPokemon[]=[0x23B103, 0x23B111, 0x23B166] // Groudon +StaticPokemon[]=[0x22DA06, 0x22DA0F, 0x22DA55] // Regirock +StaticPokemon[]=[0x238F5C, 0x238F65, 0x238FAB] // Regice +StaticPokemon[]=[0x23905E, 0x239067, 0x2390AD] // Registeel +StaticPokemon[]=[0x239725, 0x23972E, 0x239774] // Rayquaza +StaticPokemon[]=[0x272384, 0x27238D] // Kecleons on OW (7) +StaticPokemon[]=[0x1F56D6, 0x1F56DF] // Kecleon w/ Steven +StaticPokemon[]=[0x2377B2, 0x2377B9] // Voltorb 1 +StaticPokemon[]=[0x2377FF, 0x237806] // Voltorb 2 +StaticPokemon[]=[0x23784C, 0x237853] // Voltorb 3 +StaticPokemon[]=[0x2339EE, 0x2339F5] // Electrode 1 +StaticPokemon[]=[0x233A3B, 0x233A42] // Electrode 2 +StaticPokemon[]=[0x242D1B, 0x242D29] // Sudowoodo in Battle Frontier +StaticPokemon[]=[0x242BA7] +StaticPokemon[]=[0x242BBA] // Latias/Latios on Southern Island +StaticPokemon[]=[0x267FE7, 0x267FF7, 0x268041, 0x26804C] // Deoxys on Birth Island +StaticPokemon[]=[0x267E0D, 0x267E47, 0x267E9C, 0x267EA7] // Mew on Faraway Island +StaticPokemon[]=[0x26919F, 0x2691CE, 0x26921D, 0x269228] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x2692E7, 0x2692F2, 0x26933C, 0x269347] // Lugia on Navel Rock +StaticPokemon[]=[0x1EA783] // Wynaut Egg +StaticPokemon[]=[0x222868, 0x22286B, 0x2228ED, 0x2228FE] // Beldum +StaticPokemon[]=[0x270058, 0x27005B] // Castform + +[Fire Red (U) 1.0] +Game=BPRE +Version=0 +Type=FRLG +TableFile=gba_english +FreeSpace=0x800000 +PokemonNameLength=11 +PokemonStats=0x2547A0 +PokemonMovesets=0x25D7B8 +PokemonTMHMCompat=0x252BD0 +PokemonEvolutions=0x25977C +BattleTrappersBanned=[55,56,57,58,59] +StarterPokemon=0x169BB5 +TrainerData=0x23EAC8 +TrainerEntrySize=40 +TrainerCount=0x2E7 +TrainerClassNames=0x23E558 +TrainerClassCount=107 +TrainerClassNameLength=13 +TrainerNameLength=12 +ItemEntrySize=44 +ItemCount=374 +MoveDescriptions=0x4886E8 +MoveNameLength=13 +AbilityNameLength=13 +TmMoves=0x45A5A4 +TmMovesDuplicate=0x45A80C +MoveTutorData=0x459B60 +MoveTutorMoves=15 +ItemImages=0x3D4294 +TmPals=[0xE91E64, 0xE91DC4, 0xE9201C, 0xE91E8C, 0xE91FA4, 0xE91FF4, 0xE91EDC, 0xE91FCC, 0xE91F54, 0xE91FCC, 0xE91F04, 0xE91E14, 0xE91EDC, 0xE91F7C, 0xE91E3C, 0xE91EB4, 0xE91DEC, 0xE91F2C] +IntroCryOffset=0x12FB38 +IntroSpriteOffset=0x130FA0 +IntroOtherOffset=0x130F4C +MapBankCount=0x2B +MapBankSizes=[5,123,60,66,4,6,8,10,6,8,20,10,8,2,10,4,2,2,2,1,1,2,2,3,2,3,2,1,1,1,1,7,5,5,8,8,5,5,1,1,1,2,1] +ItemBallPic=92 +TradeTableOffset=0x26CF8C +TradeTableSize=9 +TradesUnused=[] +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C472, 0x16C475, 0x16C4B5, 0x16C4E9] // Eevee in Celadon Mansion +StaticPokemon[]=[0x16EC0C, 0x16EC13] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x16EC52, 0x16EC59] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x163840, 0x163847] // Electrode in The Power Plant +StaticPokemon[]=[0x16389E, 0x1638A5] // Electrode in The Power Plant +StaticPokemon[]=[0x1637CC, 0x1637D3, 0x163827] // Zapdos in the Power Plant +StaticPokemon[]=[0x1631C0, 0x1631C7, 0x16321B] // Articuno in the Seafoams +StaticPokemon[]=[0x163B47, 0x163B4E, 0x163BA2] // Moltres in Mt.Ember +StaticPokemon[]=[0x16250A, 0x16251E, 0x162564] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x168049, 0x168050] // Sleeping Snorlax (12) +StaticPokemon[]=[0x168156, 0x16815D] // Sleeping Snorlax (16) +StaticPokemon[]=[0x163CBC, 0x163CC2] // Hypno in Berry Forest +StaticPokemon[]=[0x1652E6, 0x1652F6, 0x165340, 0x16534B] // Deoxys on Birth Island +StaticPokemon[]=[0x16503C, 0x16506B, 0x1650BA, 0x1650C5] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x16518A, 0x165195, 0x1651DF, 0x1651EA] // Lugia on Navel Rock +StaticPokemon[]=[0x16E7E5, 0x16E7E9, 0x16E7F4, 0x16E6E6] // Old Amber +StaticPokemon[]=[0x16E75B, 0x16E75F, 0x16E76A, 0x16E66A] // Helix Fossil +StaticPokemon[]=[0x16E7A0, 0x16E7A4, 0x16E7AF, 0x16E6A8] // Dome Fossil +StaticPokemon[]=[0x161AE1, 0x161B20, 0x161B53] // Lapras in Silph. Co +StaticPokemon[]=[0x16F7C6, 0x16F885] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x16CC18, 0x16CC94] // Abra +StaticPokemon[]=[0x16CC28, 0x16CC9F] // Clefairy +StaticPokemon[]=[0x16CC48, 0x16CCB5] // Scyther +StaticPokemon[]=[0x16CC38, 0x16CCAA] // Dratini +StaticPokemon[]=[0x16CC58, 0x16CCC0] // Porygon + +[Leaf Green (U) 1.0] +Game=BPGE +Version=0 +Type=FRLG +CopyFrom=Fire Red (U) 1.0 +PokemonStats=0x25477C +PokemonMovesets=0x25D798 +PokemonTMHMCompat=0x252BAC +PokemonEvolutions=0x25975C +StarterPokemon=0x169B91 +TrainerData=0x23EAA4 +TrainerClassNames=0x23E534 +MoveDescriptions=0x487FC4 +TmMoves=0x459FC4 +TmMovesDuplicate=0x45A22C +MoveTutorData=0x459580 +ItemImages=0x3D40D0 +TmPals=[0xE91EE4, 0xE91E44, 0xE9209C, 0xE91F0C, 0xE92024, 0xE92074, 0xE91F5C, 0xE9204C, 0xE91FD4, 0xE9204C, 0xE91F84, 0xE91E94, 0xE91F5C, 0xE91FFC, 0xE91EBC, 0xE91F34, 0xE91E6C, 0xE91FAC] +IntroCryOffset=0x12FB10 +IntroSpriteOffset=0x130F78 +IntroOtherOffset=0x130F24 +TradeTableOffset=0x26CF6C +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C44E, 0x16C451, 0x16C491, 0x16C4C5] // Eevee in Celadon Mansion +StaticPokemon[]=[0x16EBE8, 0x16EBEF] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x16EC2E, 0x16EC35] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x16381C, 0x163823] // Electrode in The Power Plant +StaticPokemon[]=[0x16387A, 0x163881] // Electrode in The Power Plant +StaticPokemon[]=[0x1637A8, 0x1637AF, 0x163803] // Zapdos in the Power Plant +StaticPokemon[]=[0x16319C, 0x1631A3, 0x1631F7] // Articuno in the Seafoams +StaticPokemon[]=[0x163B23, 0x163B2A, 0x163B7E] // Moltres in Mt.Ember +StaticPokemon[]=[0x1624E6, 0x1624FA, 0x162540] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x168025, 0x16802C] // Sleeping Snorlax (12) +StaticPokemon[]=[0x168132, 0x168139] // Sleeping Snorlax (16) +StaticPokemon[]=[0x163C98, 0x163C9E] // Hypno in Berry Forest +StaticPokemon[]=[0x1652C2, 0x1652D2, 0x16531C, 0x165327] // Deoxys on Birth Island +StaticPokemon[]=[0x165018, 0x165047, 0x165096, 0x1650A1] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x165166, 0x165171, 0x1651BB, 0x1651C6] // Lugia on Navel Rock +StaticPokemon[]=[0x16E7C1, 0x16E7C5, 0x16E7D0, 0x16E6C2] // Old Amber +StaticPokemon[]=[0x16E737, 0x16E73B, 0x16E746, 0x16E646] // Helix Fossil +StaticPokemon[]=[0x16E77C, 0x16E780, 0x16E78B, 0x16E684] // Dome Fossil +StaticPokemon[]=[0x161ABD, 0x161AFC, 0x161B2F] // Lapras in Silph. Co +StaticPokemon[]=[0x16F7A2, 0x16F861] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x16CBF4, 0x16CC70] // Abra +StaticPokemon[]=[0x16CC04, 0x16CC7B] // Clefairy +StaticPokemon[]=[0x16CC14, 0x16CCA7] // Pinsir +StaticPokemon[]=[0x16CC24, 0x16CC86] // Dratini +StaticPokemon[]=[0x16CC34, 0x16CC9C] // Porygon + +[Fire Red (U) 1.1] +Game=BPRE +Version=1 +Type=FRLG +CopyFrom=Fire Red (U) 1.0 +PokemonStats=0x254810 +PokemonMovesets=0x25D828 +PokemonTMHMCompat=0x252C40 +PokemonEvolutions=0x2597EC +StarterPokemon=0x169C2D +TrainerData=0x23EB38 +TrainerClassNames=0x23E5C8 +MoveDescriptions=0x488748 +TmMoves=0x45A604 +TmMovesDuplicate=0x45A86C +MoveTutorData=0x459BC0 +ItemImages=0x3D4304 +TmPals=[0xE91E64, 0xE91DC4, 0xE9201C, 0xE91E8C, 0xE91FA4, 0xE91FF4, 0xE91EDC, 0xE91FCC, 0xE91F54, 0xE91FCC, 0xE91F04, 0xE91E14, 0xE91EDC, 0xE91F7C, 0xE91E3C, 0xE91EB4, 0xE91DEC, 0xE91F2C] +IntroCryOffset=0x12FBB0 +IntroSpriteOffset=0x131018 +IntroOtherOffset=0x130FC4 +TradeTableOffset=0x26CFFC +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C4EA, 0x16C4ED, 0x16C52D, 0x16C561] +StaticPokemon[]=[0x16EC84, 0x16EC8B] +StaticPokemon[]=[0x16ECCA, 0x16ECD1] +StaticPokemon[]=[0x1638B8, 0x1638BF] +StaticPokemon[]=[0x163916, 0x16391D] +StaticPokemon[]=[0x163844, 0x16384B, 0x16389F] +StaticPokemon[]=[0x163238, 0x16323F, 0x163293] +StaticPokemon[]=[0x163BBF, 0x163BC6, 0x163C1A] +StaticPokemon[]=[0x162582, 0x162596, 0x1625DC] +StaticPokemon[]=[0x1680C1, 0x1680C8] +StaticPokemon[]=[0x1681CE, 0x1681D5] +StaticPokemon[]=[0x163D34, 0x163D3A] +StaticPokemon[]=[0x16535E, 0x16536E, 0x1653B8, 0x1653C3] +StaticPokemon[]=[0x1650B4, 0x1650E3, 0x165132, 0x16513D] +StaticPokemon[]=[0x165202, 0x16520D, 0x165257, 0x165262] +StaticPokemon[]=[0x16E85D, 0x16E861, 0x16E86C, 0x16E75E] +StaticPokemon[]=[0x16E7D3, 0x16E7D7, 0x16E7E2, 0x16E6E2] +StaticPokemon[]=[0x16E818, 0x16E81C, 0x16E827, 0x16E720] +StaticPokemon[]=[0x161B59, 0x161B98, 0x161BCB] +StaticPokemon[]=[0x16F83E, 0x16F8FD] +StaticPokemon[]=[0x16CC90, 0x16CD0C] +StaticPokemon[]=[0x16CCA0, 0x16CD17] +StaticPokemon[]=[0x16CCC0, 0x16CD2D] +StaticPokemon[]=[0x16CCB0, 0x16CD22] +StaticPokemon[]=[0x16CCD0, 0x16CD38] + +[Leaf Green (U) 1.1] +Game=BPGE +Version=1 +Type=FRLG +CopyFrom=Leaf Green (U) 1.0 +PokemonStats=0x2547EC +PokemonMovesets=0x25D808 +PokemonTMHMCompat=0x252C1C +PokemonEvolutions=0x2597CC +StarterPokemon=0x169C09 +TrainerData=0x23EB14 +TrainerClassNames=0x23E5A4 +MoveDescriptions=0x488034 +TmMoves=0x45A034 +TmMovesDuplicate=0x45A29C +MoveTutorData=0x4595F0 +ItemImages=0x3D4140 +TmPals=[0xE91EE4, 0xE91E44, 0xE9209C, 0xE91F0C, 0xE92024, 0xE92074, 0xE91F5C, 0xE9204C, 0xE91FD4, 0xE9204C, 0xE91F84, 0xE91E94, 0xE91F5C, 0xE91FFC, 0xE91EBC, 0xE91F34, 0xE91E6C, 0xE91FAC] +IntroCryOffset=0x12FB88 +IntroSpriteOffset=0x130FF0 +IntroOtherOffset=0x130F9C +TradeTableOffset=0x26CFDC +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C4C6, 0x16C4C9, 0x16C509, 0x16C53D] +StaticPokemon[]=[0x16EC60, 0x16EC67] +StaticPokemon[]=[0x16ECA6, 0x16ECAD] +StaticPokemon[]=[0x163894, 0x16389B] +StaticPokemon[]=[0x1638F2, 0x1638F9] +StaticPokemon[]=[0x163820, 0x163827, 0x16387B] +StaticPokemon[]=[0x163214, 0x16321B, 0x16326F] +StaticPokemon[]=[0x163B9B, 0x163BA2, 0x163BF6] +StaticPokemon[]=[0x16255E, 0x162572, 0x1625B8] +StaticPokemon[]=[0x16809D, 0x1680A4] +StaticPokemon[]=[0x1681AA, 0x1681B1] +StaticPokemon[]=[0x163D10, 0x163D16] +StaticPokemon[]=[0x16533A, 0x16534A, 0x165394, 0x16539F] +StaticPokemon[]=[0x165090, 0x1650BF, 0x16510E, 0x165119] +StaticPokemon[]=[0x1651DE, 0x1651E9, 0x165233, 0x16523E] +StaticPokemon[]=[0x16E839, 0x16E83D, 0x16E848, 0x16E73A] +StaticPokemon[]=[0x16E7AF, 0x16E7B3, 0x16E7BE, 0x16E6BE] +StaticPokemon[]=[0x16E7F4, 0x16E7F8, 0x16E803, 0x16E6FC] +StaticPokemon[]=[0x161B35, 0x161B74, 0x161BA7] +StaticPokemon[]=[0x16F81A, 0x16F8D9] +StaticPokemon[]=[0x16CC6C, 0x16CCE8] +StaticPokemon[]=[0x16CC7C, 0x16CCF3] +StaticPokemon[]=[0x16CC8C, 0x16CD1F] +StaticPokemon[]=[0x16CC9C, 0x16CCFE] +StaticPokemon[]=[0x16CCAC, 0x16CD14] + +[Ruby (F)] +Game=AXVF +Version=0 +Type=Ruby +CopyFrom=Ruby (U) +PokemonStats=0x207080 +PokemonMovesets=0x210018 +PokemonTMHMCompat=0x205544 +PokemonEvolutions=0x20BFDC +StarterPokemon=0x3FF3F4 +StarterItems=0x826C6 +TrainerData=0x1F8904 +TrainerClassNames=0x1F8610 +ItemData=0x3CCFC4 +MoveData=0x203584 +MoveDescriptions=0x3C8434 +MoveNames=0x200728 +AbilityNames=0x202694 +TmMoves=0x37D168 +PokemonFrontSprites=0x1F075C +PokemonNormalPalettes=0x1F29BC +IntroCryOffset=0xA6DA +IntroSpriteOffset=0xB48C +IntroPaletteOffset=0xB498 +IntroOtherOffset=0xB45A +TradeTableOffset=0x21DF10 +StaticPokemonSupport=1 +StaticPokemon[]=[0x157B23, 0x157B51] +StaticPokemon[]=[0x157B76, 0x157BA4] +StaticPokemon[]=[0x1A58A8, 0x15E2E6, 0x15E2ED] +StaticPokemon[]=[0x15D049, 0x15D052] +StaticPokemon[]=[0x15F461, 0x15F46A] +StaticPokemon[]=[0x15F514, 0x15F51D] +StaticPokemon[]=[0x16108E, 0x1610B4] +StaticPokemon[]=[0x15F7D9, 0x15F7E0] +StaticPokemon[]=[0x1A59E7, 0x1A59F0] +StaticPokemon[]=[0x151DA8, 0x151DB1] +StaticPokemon[]=[0x15EDC3, 0x15EDCA] +StaticPokemon[]=[0x15EDE1, 0x15EDE8] +StaticPokemon[]=[0x15EDFF, 0x15EE06] +StaticPokemon[]=[0x1A5905, 0x1A590C] +StaticPokemon[]=[0x1A5923, 0x1A592A] +StaticPokemon[]=[0x14EC5A] +StaticPokemon[]=[0x15AF6F, 0x15AF7F] +StaticPokemon[]=[0x164259] + +[Ruby (F) 1.1] +Game=AXVF +Version=1 +Type=Ruby +CopyStaticPokemon=1 +CopyFrom=Ruby (F) + +[Sapphire (F)] +Game=AXPF +Version=0 +Type=Sapp +CopyFrom=Ruby (F) +PokemonStats=0x207010 +PokemonMovesets=0x20FFA8 +PokemonTMHMCompat=0x2054D4 +PokemonEvolutions=0x20BF6C +StarterPokemon=0x3FEF24 +TrainerData=0x1F8894 +TrainerClassNames=0x1F85A0 +ItemData=0x3CCAF4 +MoveData=0x203514 +MoveDescriptions=0x3C7F64 +MoveNames=0x2006B8 +AbilityNames=0x202624 +TmMoves=0x37D0F8 +PokemonFrontSprites=0x1F06EC +PokemonNormalPalettes=0x1F294C +TradeTableOffset=0x21DEA0 +StaticPokemonSupport=1 +StaticPokemon[]=[0x157AB3, 0x157AE1] +StaticPokemon[]=[0x157B06, 0x157B34] +StaticPokemon[]=[0x1A5838, 0x15E276, 0x15E27D] +StaticPokemon[]=[0x15CFD9, 0x15CFE2] +StaticPokemon[]=[0x15F3F1, 0x15F3FA] +StaticPokemon[]=[0x15F4A4, 0x15F4AD] +StaticPokemon[]=[0x16101E, 0x161044] +StaticPokemon[]=[0x15F769, 0x15F770] +StaticPokemon[]=[0x1A5977, 0x1A5980] +StaticPokemon[]=[0x151D3C, 0x151D45] +StaticPokemon[]=[0x15ED53, 0x15ED5A] +StaticPokemon[]=[0x15ED71, 0x15ED78] +StaticPokemon[]=[0x15ED8F, 0x15ED96] +StaticPokemon[]=[0x1A5895, 0x1A589C] +StaticPokemon[]=[0x1A58B3, 0x1A58BA] +StaticPokemon[]=[0x14EBEE] +StaticPokemon[]=[0x15AEFF, 0x15AF0F] +StaticPokemon[]=[0x1641E9] + +[Sapphire (F) 1.1] +Game=AXPF +Version=1 +Type=Sapp +CopyStaticPokemon=1 +CopyFrom=Sapphire (F) + +[Emerald (F)] +Game=BPEF +Version=0 +Type=Em +CopyFrom=Emerald (U) +PokemonStats=0x327F58 +PokemonMovesets=0x330EF0 +PokemonTMHMCompat=0x326410 +PokemonEvolutions=0x32CEB4 +StarterPokemon=0x5B63E4 +StarterItems=0xB118E +TrainerData=0x317B60 +TrainerClassNames=0x317804 +MoveDescriptions=0x620920 +TmMoves=0x619F1C +TmMovesDuplicate=0x61A3C8 +MoveTutorData=0x619394 +ItemImages=0x618798 +TmPals=[0xDB5F6C, 0xDB5ECC, 0xDB6124, 0xDB5F94, 0xDB60AC, 0xDB60FC, 0xDB5FE4, 0xDB60D4, 0xDB605C, 0xDB60D4, 0xDB600C, 0xDB5F1C, 0xDB5FE4, 0xDB6084, 0xDB5F44, 0xDB5FBC, 0xDB5EF4, 0xDB6034] +IntroCryOffset=0x30B0C +IntroSpriteOffset=0x31924 +TradeTableOffset=0x340A54 +StaticPokemonSupport=1 +StaticPokemon[]=[0x2142C0, 0x2142E5, 0x2142E8, 0x21436A, 0x214378] +StaticPokemon[]=[0x2142D2, 0x214388, 0x21438B, 0x21440D, 0x21441B] +StaticPokemon[]=[0x23FEE1, 0x23FEEF, 0x23FF44] +StaticPokemon[]=[0x23FFB2, 0x23FFC0, 0x240015] +StaticPokemon[]=[0x231B1F, 0x231B28, 0x231B6E] +StaticPokemon[]=[0x23DC62, 0x23DC6B, 0x23DCB1] +StaticPokemon[]=[0x23DD64, 0x23DD6D, 0x23DDB3] +StaticPokemon[]=[0x23E449, 0x23E452, 0x23E498] +StaticPokemon[]=[0x277645, 0x27764E] +StaticPokemon[]=[0x1F68F4, 0x1F68FD] +StaticPokemon[]=[0x23C389, 0x23C390] +StaticPokemon[]=[0x23C3D6, 0x23C3DD] +StaticPokemon[]=[0x23C423, 0x23C42A] +StaticPokemon[]=[0x23827E, 0x238285] +StaticPokemon[]=[0x2382CB, 0x2382D2] +StaticPokemon[]=[0x247FC7, 0x247FD5] +StaticPokemon[]=[0x247E53] +StaticPokemon[]=[0x247E66] +StaticPokemon[]=[0x26CE61, 0x26CE71, 0x26CEBB, 0x26CEC6] +StaticPokemon[]=[0x26CC78, 0x26CCB2, 0x26CD07, 0x26CD12] +StaticPokemon[]=[0x26E00D, 0x26E03C, 0x26E08B, 0x26E096] +StaticPokemon[]=[0x26E155, 0x26E160, 0x26E1AA, 0x26E1B5] +StaticPokemon[]=[0x1EB1A5] +StaticPokemon[]=[0x22607E, 0x226081, 0x226103, 0x226114] +StaticPokemon[]=[0x275296, 0x275299] + +[Ruby (G)] +Game=AXVD +Version=0 +Type=Ruby +CopyFrom=Ruby (U) +PokemonStats=0x20BC04 +PokemonMovesets=0x214B9C +PokemonTMHMCompat=0x20A0C8 +PokemonEvolutions=0x210B60 +StarterPokemon=0x403BF0 +StarterItems=0x825DE +TrainerData=0x1FD478 +TrainerClassNames=0x1FD184 +ItemData=0x3D13DC +MoveData=0x208108 +MoveDescriptions=0x3CC978 +MoveNames=0x20529C +AbilityNames=0x207218 +TmMoves=0x381CEC +PokemonFrontSprites=0x1F52D0 +PokemonNormalPalettes=0x1F7530 +IntroCryOffset=0xA6DA +IntroSpriteOffset=0xB48C +IntroPaletteOffset=0xB498 +IntroOtherOffset=0xB45A +TradeTableOffset=0x222A94 +StaticPokemonSupport=1 +StaticPokemon[]=[0x157A3B, 0x157A69] +StaticPokemon[]=[0x157A8E, 0x157ABC] +StaticPokemon[]=[0x1A8695, 0x15E1FE, 0x15E205] +StaticPokemon[]=[0x15CF61, 0x15CF6A] +StaticPokemon[]=[0x15F379, 0x15F382] +StaticPokemon[]=[0x15F42C, 0x15F435] +StaticPokemon[]=[0x160FA6, 0x160FCC] +StaticPokemon[]=[0x15F6F1, 0x15F6F8] +StaticPokemon[]=[0x1A87D4, 0x1A87DD] +StaticPokemon[]=[0x151CC0, 0x151CC9] +StaticPokemon[]=[0x15ECDB, 0x15ECE2] +StaticPokemon[]=[0x15ECF9, 0x15ED00] +StaticPokemon[]=[0x15ED17, 0x15ED1E] +StaticPokemon[]=[0x1A86F2, 0x1A86F9] +StaticPokemon[]=[0x1A8710, 0x1A8717] +StaticPokemon[]=[0x14EB72] +StaticPokemon[]=[0x15AE87, 0x15AE97] +StaticPokemon[]=[0x164171] + +[Ruby (G) 1.1] +Game=AXVD +Version=1 +Type=Ruby +CopyStaticPokemon=1 +CopyFrom=Ruby (G) + +[Sapphire (G)] +Game=AXPD +Version=0 +Type=Sapp +CopyFrom=Ruby (G) +PokemonStats=0x20BB98 +PokemonMovesets=0x214B30 +PokemonTMHMCompat=0x20A05C +PokemonEvolutions=0x210AF4 +StarterPokemon=0x403B5C +TrainerData=0x1FD40C +TrainerClassNames=0x1FD118 +ItemData=0x3D1348 +MoveData=0x20809C +MoveDescriptions=0x3CC8E4 +MoveNames=0x20523D +AbilityNames=0x2071AC +TmMoves=0x381C80 +PokemonFrontSprites=0x1F5264 +PokemonNormalPalettes=0x1F74C4 +TradeTableOffset=0x222A28 +StaticPokemonSupport=1 +StaticPokemon[]=[0x1579CF, 0x1579FD] +StaticPokemon[]=[0x157A22, 0x157A50] +StaticPokemon[]=[0x1A8629, 0x15E192, 0x15E199] +StaticPokemon[]=[0x15CEF5, 0x15CEFE] +StaticPokemon[]=[0x15F30D, 0x15F316] +StaticPokemon[]=[0x15F3C0, 0x15F3C9] +StaticPokemon[]=[0x160F3A, 0x160F60] +StaticPokemon[]=[0x15F685, 0x15F68C] +StaticPokemon[]=[0x1A8768, 0x1A8771] +StaticPokemon[]=[0x151C58, 0x151C61] +StaticPokemon[]=[0x15EC6F, 0x15EC76] +StaticPokemon[]=[0x15EC8D, 0x15EC94] +StaticPokemon[]=[0x15ECAB, 0x15ECB2] +StaticPokemon[]=[0x1A8686, 0x1A868D] +StaticPokemon[]=[0x1A86A4, 0x1A86AB] +StaticPokemon[]=[0x14EB0A] +StaticPokemon[]=[0x15AE1B, 0x15AE2B] +StaticPokemon[]=[0x164105] + +[Sapphire (G) 1.1] +Game=AXPD +Version=1 +Type=Sapp +CopyStaticPokemon=1 +CopyFrom=Sapphire (G) + +[Emerald (G)] +Game=BPED +Version=0 +Type=Em +CopyFrom=Emerald (U) +PokemonStats=0x334DA8 +PokemonMovesets=0x33DD40 +PokemonTMHMCompat=0x333260 +PokemonEvolutions=0x339D04 +StarterPokemon=0x5C2FB0 +StarterItems=0xBE59E +TrainerData=0x3249A0 +TrainerClassNames=0x324644 +MoveDescriptions=0x62DA80 +TmMoves=0x62705C +TmMovesDuplicate=0x627508 +MoveTutorData=0x6264D4 +ItemImages=0x6258D8 +TmPals=[0xDB5FA4, 0xDB5F04, 0xDB615C, 0xDB5FCC, 0xDB60E4, 0xDB6134, 0xDB601C, 0xDB610C, 0xDB6094, 0xDB610C, 0xDB6044, 0xDB5F54, 0xDB601C, 0xDB60BC, 0xDB5F7C, 0xDB5FF4, 0xDB5F2C, 0xDB606C] +IntroCryOffset=0x30B10 +IntroSpriteOffset=0x31928 +TradeTableOffset=0x34D89C +StaticPokemonSupport=1 +StaticPokemon[]=[0x215CD3, 0x215CF8, 0x215CFB, 0x215D7D, 0x215D8B] +StaticPokemon[]=[0x215CE5, 0x215D9B, 0x215D9E, 0x215E20, 0x215E2E] +StaticPokemon[]=[0x24354E, 0x24355C, 0x2435B1] +StaticPokemon[]=[0x24361F, 0x24362D, 0x243682] +StaticPokemon[]=[0x234815, 0x23481E, 0x234864] +StaticPokemon[]=[0x2411BD, 0x2411C6, 0x24120C] +StaticPokemon[]=[0x2412BF, 0x2412C8, 0x24130E] +StaticPokemon[]=[0x2419BC, 0x2419C5, 0x241A0B] +StaticPokemon[]=[0x27DA85, 0x27DA8E] +StaticPokemon[]=[0x1F7227, 0x1F7230] +StaticPokemon[]=[0x23F82B, 0x23F832] +StaticPokemon[]=[0x23F878, 0x23F87F] +StaticPokemon[]=[0x23F8C5, 0x23F8CC] +StaticPokemon[]=[0x23B4C0, 0x23B4C7] +StaticPokemon[]=[0x23B50D, 0x23B514] +StaticPokemon[]=[0x24B918, 0x24B926] +StaticPokemon[]=[0x24B7A4] +StaticPokemon[]=[0x24B7B7] +StaticPokemon[]=[0x272CA4, 0x272CB4, 0x272CFE, 0x272D09] +StaticPokemon[]=[0x272AB8, 0x272AF2, 0x272B47, 0x272B52] +StaticPokemon[]=[0x273F63, 0x273F92, 0x273FE1, 0x273FEC] +StaticPokemon[]=[0x2740AB, 0x2740B6, 0x274100, 0x27410B] +StaticPokemon[]=[0x1EB5E8] +StaticPokemon[]=[0x228813, 0x228816, 0x228898, 0x2288A9] +StaticPokemon[]=[0x27B588, 0x27B58B] + +[Ruby (S)] +Game=AXVS +Version=0 +Type=Ruby +CopyFrom=Ruby (U) +PokemonStats=0x2039B0 +PokemonMovesets=0x20C948 +PokemonTMHMCompat=0x201E74 +PokemonEvolutions=0x20890C +StarterPokemon=0x3FB50C +StarterItems=0x82666 +TrainerData=0x1F521C +TrainerClassNames=0x1F4F28 +ItemData=0x3C8FFC +MoveData=0x1FFEB4 +MoveDescriptions=0x3C4504 +MoveNames=0x1FD040 +AbilityNames=0x1FEFC4 +TmMoves=0x379A9C +PokemonFrontSprites=0x1ED074 +PokemonNormalPalettes=0x1EF2D4 +IntroCryOffset=0xA6D2 +IntroSpriteOffset=0xB484 +IntroPaletteOffset=0xB490 +IntroOtherOffset=0xB452 +TradeTableOffset=0x21A840 +StaticPokemonSupport=1 +StaticPokemon[]=[0x157B5F, 0x157B8D] +StaticPokemon[]=[0x157BB2, 0x157BE0] +StaticPokemon[]=[0x1A376D, 0x15E322, 0x15E329] +StaticPokemon[]=[0x15D085, 0x15D08E] +StaticPokemon[]=[0x15F49D, 0x15F4A6] +StaticPokemon[]=[0x15F550, 0x15F559] +StaticPokemon[]=[0x1610CA, 0x1610F0] +StaticPokemon[]=[0x15F815, 0x15F81C] +StaticPokemon[]=[0x1A38AC, 0x1A38B5] +StaticPokemon[]=[0x151DE4, 0x151DED] +StaticPokemon[]=[0x15EDFF, 0x15EE06] +StaticPokemon[]=[0x15EE1D, 0x15EE24] +StaticPokemon[]=[0x15EE3B, 0x15EE42] +StaticPokemon[]=[0x1A37CA, 0x1A37D1] +StaticPokemon[]=[0x1A37E8, 0x1A37EF] +StaticPokemon[]=[0x14EC96] +StaticPokemon[]=[0x15AFAB, 0x15AFBB] +StaticPokemon[]=[0x164295] + +[Ruby (S) 1.1] +Game=AXVS +Version=1 +Type=Ruby +CopyStaticPokemon=1 +CopyFrom=Ruby (S) + +[Sapphire (S)] +Game=AXPS +Version=0 +Type=Sapp +CopyFrom=Ruby (S) +PokemonStats=0x203940 +PokemonMovesets=0x20C8D8 +PokemonTMHMCompat=0x201E04 +PokemonEvolutions=0x20889C +StarterPokemon=0x3FB248 +TrainerData=0x1F51AC +TrainerClassNames=0x1F4EB8 +ItemData=0x3C8D38 +MoveData=0x1FFE44 +MoveDescriptions=0x3C4240 +MoveNames=0x1FCFD0 +AbilityNames=0x1FEF54 +TmMoves=0x379A2C +PokemonFrontSprites=0x1ED004 +PokemonNormalPalettes=0x1EF264 +TradeTableOffset=0x21A7D0 +StaticPokemonSupport=1 +StaticPokemon[]=[0x157AEF, 0x157B1D] +StaticPokemon[]=[0x157B42, 0x157B70] +StaticPokemon[]=[0x1A36FD, 0x15E2B2, 0x15E2B9] +StaticPokemon[]=[0x15D015, 0x15D01E] +StaticPokemon[]=[0x15F42D, 0x15F436] +StaticPokemon[]=[0x15F4E0, 0x15F4E9] +StaticPokemon[]=[0x16105A, 0x161080] +StaticPokemon[]=[0x15F7A5, 0x15F7AC] +StaticPokemon[]=[0x1A383C, 0x1A3845] +StaticPokemon[]=[0x151D78, 0x151D81] +StaticPokemon[]=[0x15ED8F, 0x15ED96] +StaticPokemon[]=[0x15EDAD, 0x15EDB4] +StaticPokemon[]=[0x15EDCB, 0x15EDD2] +StaticPokemon[]=[0x1A375A, 0x1A3761] +StaticPokemon[]=[0x1A3778, 0x1A377F] +StaticPokemon[]=[0x14EC2A] +StaticPokemon[]=[0x15AF3B, 0x15AF4B] +StaticPokemon[]=[0x164225] + +[Sapphire (S) 1.1] +Game=AXPS +Version=1 +Type=Sapp +CopyStaticPokemon=1 +CopyFrom=Sapphire (S) + +[Emerald (S)] +Game=BPES +Version=0 +Type=Em +CopyFrom=Emerald (U) +PokemonStats=0x3266A4 +PokemonMovesets=0x32F63C +PokemonTMHMCompat=0x324B5C +PokemonEvolutions=0x32B600 +StarterPokemon=0x5B49FC +StarterItems=0xBE596 +TrainerData=0x316294 +TrainerClassNames=0x315F38 +MoveDescriptions=0x61EE38 +TmMoves=0x6189D4 +TmMovesDuplicate=0x618E80 +MoveTutorData=0x617E4C +ItemImages=0x617250 +TmPals=[0xDB5F00, 0xDB5E60, 0xDB60B8, 0xDB5F28, 0xDB6040, 0xDB6090, 0xDB5F78, 0xDB6068, 0xDB5FF0, 0xDB6068, 0xDB5FA0, 0xDB5EB0, 0xDB5F78, 0xDB6018, 0xDB5ED8, 0xDB5F50, 0xDB5E88, 0xDB5FC8] +IntroCryOffset=0x30B0C +IntroSpriteOffset=0x31924 +TradeTableOffset=0x33F1AC +StaticPokemonSupport=1 +StaticPokemon[]=[0x21307F, 0x2130A4, 0x2130A7, 0x213129, 0x213137] +StaticPokemon[]=[0x213091, 0x213147, 0x21314A, 0x2131CC, 0x2131DA] +StaticPokemon[]=[0x23DCC5, 0x23DCD3, 0x23DD28] +StaticPokemon[]=[0x23DD96, 0x23DDA4, 0x23DDF9] +StaticPokemon[]=[0x22FD3F, 0x22FD48, 0x22FD8E] +StaticPokemon[]=[0x23BB32, 0x23BB3B, 0x23BB81] +StaticPokemon[]=[0x23BC34, 0x23BC3D, 0x23BC83] +StaticPokemon[]=[0x23C305, 0x23C30E, 0x23C354] +StaticPokemon[]=[0x276188, 0x276191] +StaticPokemon[]=[0x1F5CDA, 0x1F5CE3] +StaticPokemon[]=[0x23A38F, 0x23A396] +StaticPokemon[]=[0x23A3DC, 0x23A3E3] +StaticPokemon[]=[0x23A429, 0x23A430] +StaticPokemon[]=[0x236418, 0x23641F] +StaticPokemon[]=[0x236465, 0x23646C] +StaticPokemon[]=[0x245C8C, 0x245C9A] +StaticPokemon[]=[0x245B18] +StaticPokemon[]=[0x245B2B] +StaticPokemon[]=[0x26B948, 0x26B958, 0x26B9A2, 0x26B9AD] +StaticPokemon[]=[0x26B75A, 0x26B794, 0x26B7E9, 0x26B7F4] +StaticPokemon[]=[0x26CBEA, 0x26CC19, 0x26CC68, 0x26CC73] +StaticPokemon[]=[0x26CD32, 0x26CD3D, 0x26CD87, 0x26CD92] +StaticPokemon[]=[0x1EA97A] +StaticPokemon[]=[0x22477B, 0x22477E, 0x224800, 0x224811] +StaticPokemon[]=[0x273D60, 0x273D63] + +[Ruby (I)] +Game=AXVI +Version=0 +Type=Ruby +CopyFrom=Ruby (U) +PokemonStats=0x20090C +PokemonMovesets=0x2098A4 +PokemonTMHMCompat=0x1FEDD0 +PokemonEvolutions=0x205868 +StarterPokemon=0x3F8714 +StarterItems=0x8257E +TrainerData=0x1F2198 +TrainerClassNames=0x1F1EA4 +ItemData=0x3C5FF8 +MoveData=0x1FCE10 +MoveDescriptions=0x3C158C +MoveNames=0x1F9FBC +AbilityNames=0x1FBF20 +TmMoves=0x3769F4 +PokemonFrontSprites=0x1E9FF0 +PokemonNormalPalettes=0x1EC250 +IntroCryOffset=0xA6D2 +IntroSpriteOffset=0xB484 +IntroPaletteOffset=0xB490 +IntroOtherOffset=0xB452 +TradeTableOffset=0x21779C +StaticPokemonSupport=1 +StaticPokemon[]=[0x157A87, 0x157AB5] +StaticPokemon[]=[0x157ADA, 0x157B08] +StaticPokemon[]=[0x1A0E4D, 0x15E24A, 0x15E251] +StaticPokemon[]=[0x15CFAD, 0x15CFB6] +StaticPokemon[]=[0x15F3C5, 0x15F3CE] +StaticPokemon[]=[0x15F478, 0x15F481] +StaticPokemon[]=[0x160FF2, 0x161018] +StaticPokemon[]=[0x15F73D, 0x15F744] +StaticPokemon[]=[0x1A0F8C, 0x1A0F95] +StaticPokemon[]=[0x151D0C, 0x151D15] +StaticPokemon[]=[0x15ED27, 0x15ED2E] +StaticPokemon[]=[0x15ED45, 0x15ED4C] +StaticPokemon[]=[0x15ED63, 0x15ED6A] +StaticPokemon[]=[0x1A0EAA, 0x1A0EB1] +StaticPokemon[]=[0x1A0EC8, 0x1A0ECF] +StaticPokemon[]=[0x14EBBE] +StaticPokemon[]=[0x15AED3, 0x15AEE3] +StaticPokemon[]=[0x1641BD] + +[Ruby (I) 1.1] +Game=AXVI +Version=1 +Type=Ruby +CopyStaticPokemon=1 +CopyFrom=Ruby (I) + +[Sapphire (I)] +Game=AXPI +Version=0 +Type=Sapp +CopyFrom=Ruby (I) +PokemonStats=0x20089C +PokemonMovesets=0x209834 +PokemonTMHMCompat=0x1FED60 +PokemonEvolutions=0x2057F8 +StarterPokemon=0x3F83B8 +TrainerData=0x1F2128 +TrainerClassNames=0x1F1E34 +ItemData=0x3C5C9C +MoveData=0x1FCDA0 +MoveDescriptions=0x3C1230 +MoveNames=0x1F9F4C +AbilityNames=0x1FBEB0 +TmMoves=0x376984 +PokemonFrontSprites=0x1E9F80 +PokemonNormalPalettes=0x1EC1E0 +TradeTableOffset=0x21772C +StaticPokemonSupport=1 +StaticPokemon[]=[0x157A17, 0x157A45] +StaticPokemon[]=[0x157A6A, 0x157A98] +StaticPokemon[]=[0x1A0DDD, 0x15E1DA, 0x15E1E1] +StaticPokemon[]=[0x15CF3D, 0x15CF46] +StaticPokemon[]=[0x15F355, 0x15F35E] +StaticPokemon[]=[0x15F408, 0x15F411] +StaticPokemon[]=[0x160F82, 0x160FA8] +StaticPokemon[]=[0x15F6CD, 0x15F6D4] +StaticPokemon[]=[0x1A0F1C, 0x1A0F25] +StaticPokemon[]=[0x151CA0, 0x151CA9] +StaticPokemon[]=[0x15ECB7, 0x15ECBE] +StaticPokemon[]=[0x15ECD5, 0x15ECDC] +StaticPokemon[]=[0x15ECF3, 0x15ECFA] +StaticPokemon[]=[0x1A0E3A, 0x1A0E41] +StaticPokemon[]=[0x1A0E58, 0x1A0E5F] +StaticPokemon[]=[0x14EB52] +StaticPokemon[]=[0x15AE63, 0x15AE73] +StaticPokemon[]=[0x16414D] + +[Sapphire (I) 1.1] +Game=AXPI +Version=1 +Type=Sapp +CopyStaticPokemon=1 +CopyFrom=Sapphire (I) + +[Emerald (I)] +Game=BPEI +Version=0 +Type=Em +CopyFrom=Emerald (U) +PokemonStats=0x31FDE8 +PokemonMovesets=0x328D80 +PokemonTMHMCompat=0x31E2A0 +PokemonEvolutions=0x324D44 +StarterPokemon=0x5AE994 +StarterItems=0xBE596 +TrainerData=0x30F9F4 +TrainerClassNames=0x30F698 +MoveDescriptions=0x619038 +TmMoves=0x612730 +TmMovesDuplicate=0x612BDC +MoveTutorData=0x611BA8 +ItemImages=0x610FAC +TmPals=[0xDB5FA4, 0xDB5F04, 0xDB615C, 0xDB5FCC, 0xDB60E4, 0xDB6134, 0xDB601C, 0xDB610C, 0xDB6094, 0xDB610C, 0xDB6044, 0xDB5F54, 0xDB601C, 0xDB60BC, 0xDB5F7C, 0xDB5FF4, 0xDB5F2C, 0xDB606C] +IntroCryOffset=0x30B10 +IntroSpriteOffset=0x31928 +TradeTableOffset=0x3388CC +StaticPokemonSupport=1 +StaticPokemon[]=[0x211669, 0x21168E, 0x211691, 0x211713, 0x211721] +StaticPokemon[]=[0x21167B, 0x211731, 0x211734, 0x2117B6, 0x2117C4] +StaticPokemon[]=[0x23B0B6, 0x23B0C4, 0x23B119] +StaticPokemon[]=[0x23B187, 0x23B195, 0x23B1EA] +StaticPokemon[]=[0x22D8D2, 0x22D8DB, 0x22D921] +StaticPokemon[]=[0x239092, 0x23909B, 0x2390E1] +StaticPokemon[]=[0x239194, 0x23919D, 0x2391E3] +StaticPokemon[]=[0x23983F, 0x239848, 0x23988E] +StaticPokemon[]=[0x271FDB, 0x271FE4] +StaticPokemon[]=[0x1F51E3, 0x1F51EC] +StaticPokemon[]=[0x23794B, 0x237952] +StaticPokemon[]=[0x237998, 0x23799F] +StaticPokemon[]=[0x2379E5, 0x2379EC] +StaticPokemon[]=[0x233AB2, 0x233AB9] +StaticPokemon[]=[0x233AFF, 0x233B06] +StaticPokemon[]=[0x242D25, 0x242D33] +StaticPokemon[]=[0x242BB1] +StaticPokemon[]=[0x242BC4] +StaticPokemon[]=[0x267A89, 0x267A99, 0x267AE3, 0x267AEE] +StaticPokemon[]=[0x2678A1, 0x2678DB, 0x267930, 0x26793B] +StaticPokemon[]=[0x268C5D, 0x268C8C, 0x268CDB, 0x268CE6] +StaticPokemon[]=[0x268DA5, 0x268DB0, 0x268DFA, 0x268E05] +StaticPokemon[]=[0x1EA261] +StaticPokemon[]=[0x222657, 0x22265A, 0x2226DC, 0x2226ED] +StaticPokemon[]=[0x26FC1D, 0x26FC20] + +[Fire Red (F)] +Game=BPRF +Version=0 +Type=FRLG +CopyFrom=Fire Red (U) 1.0 +PokemonStats=0x24EBF0 +PokemonMovesets=0x257C08 +PokemonTMHMCompat=0x24D020 +PokemonEvolutions=0x253BCC +StarterPokemon=0x169BDC +TrainerData=0x238ED4 +TrainerClassNames=0x238964 +MoveDescriptions=0x47E7DC +TmMoves=0x453BA8 +TmMovesDuplicate=0x453E10 +MoveTutorData=0x453164 +ItemImages=0x3CE114 +TmPals=[0xE91DB8, 0xE91D18, 0xE91F70, 0xE91DE0, 0xE91EF8, 0xE91F48, 0xE91E30, 0xE91F20, 0xE91EA8, 0xE91F20, 0xE91E58, 0xE91D68, 0xE91E30, 0xE91ED0, 0xE91D90, 0xE91E08, 0xE91D40, 0xE91E80] +IntroCryOffset=0x12FC44 +IntroSpriteOffset=0x1310AC +IntroOtherOffset=0x131058 +TradeTableOffset=0x2673DC +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C49A, 0x16C49D, 0x16C4DD, 0x16C511] +StaticPokemon[]=[0x16EC34, 0x16EC3B] +StaticPokemon[]=[0x16EC7A, 0x16EC81] +StaticPokemon[]=[0x163862, 0x163869] +StaticPokemon[]=[0x1638C0, 0x1638C7] +StaticPokemon[]=[0x1637EE, 0x1637F5, 0x163849] +StaticPokemon[]=[0x1631E2, 0x1631E9, 0x16323D] +StaticPokemon[]=[0x163B69, 0x163B70, 0x163BC4] +StaticPokemon[]=[0x16252C, 0x162540, 0x162586] +StaticPokemon[]=[0x16806D, 0x168073] +StaticPokemon[]=[0x168179, 0x16817F] +StaticPokemon[]=[0x163CDE, 0x163CE4] +StaticPokemon[]=[0x16530A, 0x16531A, 0x165364, 0x16536F] +StaticPokemon[]=[0x165060, 0x16508F, 0x1650DE, 0x1650E9] +StaticPokemon[]=[0x1651AE, 0x1651B9, 0x165203, 0x16520E] +StaticPokemon[]=[0x16E80D, 0x16E811, 0x16E81C, 0x16E70E] +StaticPokemon[]=[0x16E783, 0x16E787, 0x16E792, 0x16E692] +StaticPokemon[]=[0x16E7C8, 0x16E7CC, 0x16E7D7, 0x16E6D0] +StaticPokemon[]=[0x161AFD, 0x161B3F, 0x161B75] +StaticPokemon[]=[0x16F7EE, 0x16F8B0] +StaticPokemon[]=[0x16CC40, 0x16CCBC] +StaticPokemon[]=[0x16CC50, 0x16CCC7] +StaticPokemon[]=[0x16CC70, 0x16CCDD] +StaticPokemon[]=[0x16CC60, 0x16CCD2] +StaticPokemon[]=[0x16CC80, 0x16CCE8] + +[Leaf Green (F)] +Game=BPGF +Version=0 +Type=FRLG +CopyFrom=Fire Red (F) +PokemonStats=0x24EBCC +PokemonMovesets=0x257BE8 +PokemonTMHMCompat=0x24CFFC +PokemonEvolutions=0x253BAC +StarterPokemon=0x169BB8 +TrainerData=0x238EB0 +TrainerClassNames=0x238940 +MoveDescriptions=0x47D504 +TmMoves=0x452968 +TmMovesDuplicate=0x452BD0 +MoveTutorData=0x451F24 +ItemImages=0x3CDF50 +TmPals=[0xE91E38, 0xE91D98, 0xE91FF0, 0xE91E60, 0xE91F78, 0xE91FC8, 0xE91EB0, 0xE91FA0, 0xE91F28, 0xE91FA0, 0xE91ED8, 0xE91DE8, 0xE91EB0, 0xE91F50, 0xE91E10, 0xE91E88, 0xE91DC0, 0xE91F00] +IntroCryOffset=0x12FC1C +IntroSpriteOffset=0x131084 +IntroOtherOffset=0x131030 +TradeTableOffset=0x2673BC +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C476, 0x16C479, 0x16C4B9, 0x16C4ED] // Eevee in Celadon Mansion +StaticPokemon[]=[0x16EC10, 0x16EC17] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x16EC56, 0x16EC5D] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x16383E, 0x163845] // Electrode in The Power Plant +StaticPokemon[]=[0x16389C, 0x1638A3] // Electrode in The Power Plant +StaticPokemon[]=[0x1637CA, 0x1637D1, 0x163825] // Zapdos in the Power Plant +StaticPokemon[]=[0x1631BE, 0x1631C5, 0x163219] // Articuno in the Seafoams +StaticPokemon[]=[0x163B45, 0x163B4C, 0x163BA0] // Moltres in Mt.Ember +StaticPokemon[]=[0x162508, 0x16251C, 0x162562] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x168049, 0x16804F] // Sleeping Snorlax (12) +StaticPokemon[]=[0x168155, 0x16815B] // Sleeping Snorlax (16) +StaticPokemon[]=[0x163CBA, 0x163CC0] // Hypno in Berry Forest +StaticPokemon[]=[0x1652E6, 0x1652F6, 0x165340, 0x16534B] // Deoxys on Birth Island +StaticPokemon[]=[0x16503C, 0x16506B, 0x1650BA, 0x1650C5] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x16518A, 0x165195, 0x1651DF, 0x1651EA] // Lugia on Navel Rock +StaticPokemon[]=[0x16E7E9, 0x16E7ED, 0x16E7F8, 0x16E6EA] // Old Amber +StaticPokemon[]=[0x16E75F, 0x16E763, 0x16E76E, 0x16E66E] // Helix Fossil +StaticPokemon[]=[0x16E7A4, 0x16E7A8, 0x16E7B3, 0x16E6AC] // Dome Fossil +StaticPokemon[]=[0x161AD9, 0x161B1B, 0x161B51] // Lapras in Silph. Co +StaticPokemon[]=[0x16F7CA, 0x16F88C] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x16CC1C, 0x16CC98] // Abra +StaticPokemon[]=[0x16CC2C, 0x16CCA3] // Clefairy +StaticPokemon[]=[0x16CC3C, 0x16CCCF] // Pinsir +StaticPokemon[]=[0x16CC4C, 0x16CCAE] // Dratini +StaticPokemon[]=[0x16CC5C, 0x16CCC4] // Porygon + +[Fire Red (G)] +Game=BPRD +Version=0 +Type=FRLG +CopyFrom=Fire Red (U) 1.0 +PokemonStats=0x2546C4 +PokemonMovesets=0x25D6DC +PokemonTMHMCompat=0x252AF4 +PokemonEvolutions=0x2596A0 +StarterPokemon=0x169B20 +TrainerData=0x23E998 +TrainerClassNames=0x23E428 +MoveDescriptions=0x486BEC +TmMoves=0x45B670 +TmMovesDuplicate=0x45B8D8 +MoveTutorData=0x45AC2C +ItemImages=0x3D3BE8 +TmPals=[0xE91E84, 0xE91DE4, 0xE9203C, 0xE91EAC, 0xE91FC4, 0xE92014, 0xE91EFC, 0xE91FEC, 0xE91F74, 0xE91FEC, 0xE91F24, 0xE91E34, 0xE91EFC, 0xE91F9C, 0xE91E5C, 0xE91ED4, 0xE91E0C, 0xE91F4C] +IntroCryOffset=0x12FB88 +IntroSpriteOffset=0x130FF0 +IntroOtherOffset=0x130F9C +TradeTableOffset=0x26CEB0 +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C3DE, 0x16C3E1, 0x16C421, 0x16C455] +StaticPokemon[]=[0x16EB78, 0x16EB7F] +StaticPokemon[]=[0x16EBBE, 0x16EBC5] +StaticPokemon[]=[0x1637A6, 0x1637AD] +StaticPokemon[]=[0x163804, 0x16380B] +StaticPokemon[]=[0x163732, 0x163739, 0x16378D] +StaticPokemon[]=[0x163126, 0x16312D, 0x163181] +StaticPokemon[]=[0x163AAD, 0x163AB4, 0x163B08] +StaticPokemon[]=[0x162470, 0x162484, 0x1624CA] +StaticPokemon[]=[0x167FB1, 0x167FB7] +StaticPokemon[]=[0x1680BD, 0x1680C3] +StaticPokemon[]=[0x163C22, 0x163C28] +StaticPokemon[]=[0x16524E, 0x16525E, 0x1652A8, 0x1652B3] +StaticPokemon[]=[0x164FA4, 0x164FD3, 0x165022, 0x16502D] +StaticPokemon[]=[0x1650F2, 0x1650FD, 0x165147, 0x165152] +StaticPokemon[]=[0x16E751, 0x16E755, 0x16E760, 0x16E652] +StaticPokemon[]=[0x16E6C7, 0x16E6CB, 0x16E6D6, 0x16E5D6] +StaticPokemon[]=[0x16E70C, 0x16E710, 0x16E71B, 0x16E614] +StaticPokemon[]=[0x161A41, 0x161A83, 0x161AB9] +StaticPokemon[]=[0x16F732, 0x16F7F4] +StaticPokemon[]=[0x16CB84, 0x16CC00] +StaticPokemon[]=[0x16CB94, 0x16CC0B] +StaticPokemon[]=[0x16CBB4, 0x16CC21] +StaticPokemon[]=[0x16CBA4, 0x16CC16] +StaticPokemon[]=[0x16CBC4, 0x16CC2C] + +[Leaf Green (G)] +Game=BPGD +Version=0 +Type=FRLG +CopyFrom=Fire Red (G) +PokemonStats=0x2546A0 +PokemonMovesets=0x25D6BC +PokemonTMHMCompat=0x252AD0 +PokemonEvolutions=0x259680 +StarterPokemon=0x169AFC +TrainerData=0x23E974 +TrainerClassNames=0x23E404 +MoveDescriptions=0x485D58 +TmMoves=0x45A874 +TmMovesDuplicate=0x45AADC +MoveTutorData=0x459E30 +ItemImages=0x3D3A24 +TmPals=[0xE91F04, 0xE91E64, 0xE920BC, 0xE91F2C, 0xE92044, 0xE92094, 0xE91F7C, 0xE9206C, 0xE91FF4, 0xE9206C, 0xE91FA4, 0xE91EB4, 0xE91F7C, 0xE9201C, 0xE91EDC, 0xE91F54, 0xE91E8C, 0xE91FCC] +IntroCryOffset=0x12FB60 +IntroSpriteOffset=0x130FC8 +IntroOtherOffset=0x130F74 +TradeTableOffset=0x26CE90 +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C3BA, 0x16C3BD, 0x16C3FD, 0x16C431] // Eevee in Celadon Mansion +StaticPokemon[]=[0x16EB54, 0x16EB5B] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x16EB9A, 0x16EBA1] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x163782, 0x163789] // Electrode in The Power Plant +StaticPokemon[]=[0x1637E0, 0x1637E7] // Electrode in The Power Plant +StaticPokemon[]=[0x16370E, 0x163715, 0x163769] // Zapdos in the Power Plant +StaticPokemon[]=[0x163102, 0x163109, 0x16315D] // Articuno in the Seafoams +StaticPokemon[]=[0x163A89, 0x163A90, 0x163AE4] // Moltres in Mt.Ember +StaticPokemon[]=[0x16244C, 0x162460, 0x1624A6] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x167F8D, 0x167F93] // Sleeping Snorlax (12) +StaticPokemon[]=[0x168099, 0x16809F] // Sleeping Snorlax (16) +StaticPokemon[]=[0x163BFE, 0x163C04] // Hypno in Berry Forest +StaticPokemon[]=[0x16522A, 0x16523A, 0x165284, 0x16528F] // Deoxys on Birth Island +StaticPokemon[]=[0x164F80, 0x164FAF, 0x164FFE, 0x165009] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x1650CE, 0x1650D9, 0x165123, 0x16512E] // Lugia on Navel Rock +StaticPokemon[]=[0x16E72D, 0x16E731, 0x16E73C, 0x16E62E] // Old Amber +StaticPokemon[]=[0x16E6A3, 0x16E6A7, 0x16E6B2, 0x16E5B2] // Helix Fossil +StaticPokemon[]=[0x16E6E8, 0x16E6EC, 0x16E6F7, 0x16E5F0] // Dome Fossil +StaticPokemon[]=[0x161A1D, 0x161A5F, 0x161A95] // Lapras in Silph. Co +StaticPokemon[]=[0x16F70E, 0x16F7D0] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x16CB60, 0x16CBDC] // Abra +StaticPokemon[]=[0x16CB70, 0x16CBE7] // Clefairy +StaticPokemon[]=[0x16CB80, 0x16CC13] // Pinsir +StaticPokemon[]=[0x16CB90, 0x16CBF2] // Dratini +StaticPokemon[]=[0x16CBA0, 0x16CC08] // Porygon + +[Fire Red (S)] +Game=BPRS +Version=0 +Type=FRLG +CopyFrom=Fire Red (U) 1.0 +PokemonStats=0x24FF68 +PokemonMovesets=0x258F80 +PokemonTMHMCompat=0x24E398 +PokemonEvolutions=0x254F44 +StarterPokemon=0x169C4C +TrainerData=0x23A234 +TrainerClassNames=0x239CC4 +MoveDescriptions=0x47EF78 +TmMoves=0x454C1C +TmMovesDuplicate=0x454E84 +MoveTutorData=0x4541D8 +ItemImages=0x3CF48C +TmPals=[0xE91D98, 0xE91CF8, 0xE91F50, 0xE91DC0, 0xE91ED8, 0xE91F28, 0xE91E10, 0xE91F00, 0xE91E88, 0xE91F00, 0xE91E38, 0xE91D48, 0xE91E10, 0xE91EB0, 0xE91D70, 0xE91DE8, 0xE91D20, 0xE91E60] +IntroCryOffset=0x12FCB4 +IntroSpriteOffset=0x13111C +IntroOtherOffset=0x1310C8 +TradeTableOffset=0x268754 +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C50A, 0x16C50D, 0x16C54D, 0x16C581] +StaticPokemon[]=[0x16ECA4, 0x16ECAB] +StaticPokemon[]=[0x16ECEA, 0x16ECF1] +StaticPokemon[]=[0x1638D2, 0x1638D9] +StaticPokemon[]=[0x163930, 0x163937] +StaticPokemon[]=[0x16385E, 0x163865, 0x1638B9] +StaticPokemon[]=[0x163252, 0x163259, 0x1632AD] +StaticPokemon[]=[0x163BD9, 0x163BE0, 0x163C34] +StaticPokemon[]=[0x16259C, 0x1625B0, 0x1625F6] +StaticPokemon[]=[0x1680DD, 0x1680E3] +StaticPokemon[]=[0x1681E9, 0x1681EF] +StaticPokemon[]=[0x163D4E, 0x163D54] +StaticPokemon[]=[0x16537A, 0x16538A, 0x1653D4, 0x1653DF] +StaticPokemon[]=[0x1650D0, 0x1650FF, 0x16514E, 0x165159] +StaticPokemon[]=[0x16521E, 0x165229, 0x165273, 0x16527E] +StaticPokemon[]=[0x16E87D, 0x16E881, 0x16E88C, 0x16E77E] +StaticPokemon[]=[0x16E7F3, 0x16E7F7, 0x16E802, 0x16E702] +StaticPokemon[]=[0x16E838, 0x16E83C, 0x16E847, 0x16E740] +StaticPokemon[]=[0x161B6D, 0x161BAF, 0x161BE5] +StaticPokemon[]=[0x16F85E, 0x16F920] +StaticPokemon[]=[0x16CCB0, 0x16CD2C] +StaticPokemon[]=[0x16CCC0, 0x16CD37] +StaticPokemon[]=[0x16CCE0, 0x16CD4D] +StaticPokemon[]=[0x16CCD0, 0x16CD42] +StaticPokemon[]=[0x16CCF0, 0x16CD58] + +[Leaf Green (S)] +Game=BPGS +Version=0 +Type=FRLG +CopyFrom=Fire Red (S) +PokemonStats=0x24FF44 +PokemonMovesets=0x258F60 +PokemonTMHMCompat=0x24E374 +PokemonEvolutions=0x254F24 +StarterPokemon=0x169C28 +TrainerData=0x23A210 +TrainerClassNames=0x239CA0 +MoveDescriptions=0x47E670 +TmMoves=0x4543AC +TmMovesDuplicate=0x454614 +MoveTutorData=0x453968 +ItemImages=0x3CF2C8 +TmPals=[0xE91E18, 0xE91D78, 0xE91FD0, 0xE91E40, 0xE91F58, 0xE91FA8, 0xE91E90, 0xE91F80, 0xE91F08, 0xE91F80, 0xE91EB8, 0xE91DC8, 0xE91E90, 0xE91F30, 0xE91DF0, 0xE91E68, 0xE91DA0, 0xE91EE0] +IntroCryOffset=0x12FC8C +IntroSpriteOffset=0x1310F4 +IntroOtherOffset=0x1310A0 +TradeTableOffset=0x268734 +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C4E6, 0x16C4E9, 0x16C529, 0x16C55D] // Eevee in Celadon Mansion +StaticPokemon[]=[0x16EC80, 0x16EC87] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x16ECC6, 0x16ECCD] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x1638AE, 0x1638B5] // Electrode in The Power Plant +StaticPokemon[]=[0x16390C, 0x163913] // Electrode in The Power Plant +StaticPokemon[]=[0x16383A, 0x163841, 0x163895] // Zapdos in the Power Plant +StaticPokemon[]=[0x16322E, 0x163235, 0x163289] // Articuno in the Seafoams +StaticPokemon[]=[0x163BB5, 0x163BBC, 0x163C10] // Moltres in Mt.Ember +StaticPokemon[]=[0x162578, 0x16258C, 0x1625D2] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x1680B9, 0x1680BF] // Sleeping Snorlax (12) +StaticPokemon[]=[0x1681C5, 0x1681CB] // Sleeping Snorlax (16) +StaticPokemon[]=[0x163D2A, 0x163D30] // Hypno in Berry Forest +StaticPokemon[]=[0x165356, 0x165366, 0x1653B0, 0x1653BB] // Deoxys on Birth Island +StaticPokemon[]=[0x1650AC, 0x1650DB, 0x16512A, 0x165135] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x1651FA, 0x165205, 0x16524F, 0x16525A] // Lugia on Navel Rock +StaticPokemon[]=[0x16E859, 0x16E85D, 0x16E868, 0x16E75A] // Old Amber +StaticPokemon[]=[0x16E7CF, 0x16E7D3, 0x16E7DE, 0x16E6DE] // Helix Fossil +StaticPokemon[]=[0x16E814, 0x16E818, 0x16E823, 0x16E71C] // Dome Fossil +StaticPokemon[]=[0x161B49, 0x161B8B, 0x161BC1] // Lapras in Silph. Co +StaticPokemon[]=[0x16F83A, 0x16F8FC] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x16CC8C, 0x16CD08] // Abra +StaticPokemon[]=[0x16CC9C, 0x16CD13] // Clefairy +StaticPokemon[]=[0x16CCAC, 0x16CD3F] // Pinsir +StaticPokemon[]=[0x16CCBC, 0x16CD1E] // Dratini +StaticPokemon[]=[0x16CCCC, 0x16CD34] // Porygon + +[Fire Red (I)] +Game=BPRI +Version=0 +Type=FRLG +CopyFrom=Fire Red (U) 1.0 +PokemonStats=0x24D880 +PokemonMovesets=0x256898 +PokemonTMHMCompat=0x24BCB0 +PokemonEvolutions=0x25285C +StarterPokemon=0x169B60 +TrainerData=0x237B6C +TrainerClassNames=0x2375FC +MoveDescriptions=0x47C174 +TmMoves=0x451580 +TmMovesDuplicate=0x4517E8 +MoveTutorData=0x450B3C +ItemImages=0x3CCDA4 +TmPals=[0xE91DD4, 0xE91D34, 0xE91F8C, 0xE91DFC, 0xE91F14, 0xE91F64, 0xE91E4C, 0xE91F3C, 0xE91EC4, 0xE91F3C, 0xE91E74, 0xE91D84, 0xE91E4C, 0xE91EEC, 0xE91DAC, 0xE91E24, 0xE91D5C, 0xE91E9C] +IntroCryOffset=0x12FBC8 +IntroSpriteOffset=0x131030 +IntroOtherOffset=0x130FDC +TradeTableOffset=0x26606C +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C41E, 0x16C421, 0x16C461, 0x16C495] +StaticPokemon[]=[0x16EBB8, 0x16EBBF] +StaticPokemon[]=[0x16EBFE, 0x16EC05] +StaticPokemon[]=[0x1637E6, 0x1637ED] +StaticPokemon[]=[0x163844, 0x16384B] +StaticPokemon[]=[0x163772, 0x163779, 0x1637CD] +StaticPokemon[]=[0x163166, 0x16316D, 0x1631C1] +StaticPokemon[]=[0x163AED, 0x163AF4, 0x163B48] +StaticPokemon[]=[0x1624B0, 0x1624C4, 0x16250A] +StaticPokemon[]=[0x167FF1, 0x167FF7] +StaticPokemon[]=[0x1680FD, 0x168103] +StaticPokemon[]=[0x163C62, 0x163C68] +StaticPokemon[]=[0x16528E, 0x16529E, 0x1652E8, 0x1652F3] +StaticPokemon[]=[0x164FE4, 0x165013, 0x165062, 0x16506D] +StaticPokemon[]=[0x165132, 0x16513D, 0x165187, 0x165192] +StaticPokemon[]=[0x16E791, 0x16E795, 0x16E7A0, 0x16E692] +StaticPokemon[]=[0x16E707, 0x16E70B, 0x16E716, 0x16E616] +StaticPokemon[]=[0x16E74C, 0x16E750, 0x16E75B, 0x16E654] +StaticPokemon[]=[0x161A81, 0x161AC3, 0x161AF9] +StaticPokemon[]=[0x16F772, 0x16F834] +StaticPokemon[]=[0x16CBC4, 0x16CC40] +StaticPokemon[]=[0x16CBD4, 0x16CC4B] +StaticPokemon[]=[0x16CBF4, 0x16CC61] +StaticPokemon[]=[0x16CBE4, 0x16CC56] +StaticPokemon[]=[0x16CC04, 0x16CC6C] + +[Leaf Green (I)] +Game=BPGI +Version=0 +Type=FRLG +CopyFrom=Fire Red (I) +PokemonStats=0x24D85C +PokemonMovesets=0x256878 +PokemonTMHMCompat=0x24BC8C +PokemonEvolutions=0x25283C +StarterPokemon=0x169B3C +TrainerData=0x237B48 +TrainerClassNames=0x2375D8 +MoveDescriptions=0x47B90C +TmMoves=0x450DB0 +TmMovesDuplicate=0x451018 +MoveTutorData=0x45036C +ItemImages=0x3CCBE0 +TmPals=[0xE91E54, 0xE91DB4, 0xE9200C, 0xE91E7C, 0xE91F94, 0xE91FE4, 0xE91ECC, 0xE91FBC, 0xE91F44, 0xE91FBC, 0xE91EF4, 0xE91E04, 0xE91ECC, 0xE91F6C, 0xE91E2C, 0xE91EA4, 0xE91DDC, 0xE91F1C] +IntroCryOffset=0x12FBA0 +IntroSpriteOffset=0x131008 +IntroOtherOffset=0x130FB4 +TradeTableOffset=0x26604C +StaticPokemonSupport=1 +StaticPokemon[]=[0x16C3FA, 0x16C3FD, 0x16C43D, 0x16C471] // Eevee in Celadon Mansion +StaticPokemon[]=[0x16EB94, 0x16EB9B] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x16EBDA, 0x16EBE1] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x1637C2, 0x1637C9] // Electrode in The Power Plant +StaticPokemon[]=[0x163820, 0x163827] // Electrode in The Power Plant +StaticPokemon[]=[0x16374E, 0x163755, 0x1637A9] // Zapdos in the Power Plant +StaticPokemon[]=[0x163142, 0x163149, 0x16319D] // Articuno in the Seafoams +StaticPokemon[]=[0x163AC9, 0x163AD0, 0x163B24] // Moltres in Mt.Ember +StaticPokemon[]=[0x16248C, 0x1624A0, 0x1624E6] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x167FCD, 0x167FD3] // Sleeping Snorlax (12) +StaticPokemon[]=[0x1680D9, 0x1680DF] // Sleeping Snorlax (16) +StaticPokemon[]=[0x163C3E, 0x163C44] // Hypno in Berry Forest +StaticPokemon[]=[0x16526A, 0x16527A, 0x1652C4, 0x1652CF] // Deoxys on Birth Island +StaticPokemon[]=[0x164FC0, 0x164FEF, 0x16503E, 0x165049] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x16510E, 0x165119, 0x165163, 0x16516E] // Lugia on Navel Rock +StaticPokemon[]=[0x16E76D, 0x16E771, 0x16E77C, 0x16E66E] // Old Amber +StaticPokemon[]=[0x16E6E3, 0x16E6E7, 0x16E6F2, 0x16E5F2] // Helix Fossil +StaticPokemon[]=[0x16E728, 0x16E72C, 0x16E737, 0x16E630] // Dome Fossil +StaticPokemon[]=[0x161A5D, 0x161A9F, 0x161AD5] // Lapras in Silph. Co +StaticPokemon[]=[0x16F74E, 0x16F810] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x16CBA0, 0x16CC1C] // Abra +StaticPokemon[]=[0x16CBB0, 0x16CC27] // Clefairy +StaticPokemon[]=[0x16CBC0, 0x16CC53] // Pinsir +StaticPokemon[]=[0x16CBD0, 0x16CC32] // Dratini +StaticPokemon[]=[0x16CBE0, 0x16CC48] // Porygon + +[Ruby (J)] +Game=AXVJ +Version=0 +Type=Ruby +TableFile=gba_jap +FreeSpace=0x661000 +PokemonNameLength=6 +PokemonStats=0x1D09E8 +PokemonMovesets=0x1D9980 +PokemonTMHMCompat=0x1CEEAC +PokemonEvolutions=0x1D5944 +StarterPokemon=0x3D25C8 +StarterItems=0x7F072 +TrainerData=0x1C4C94 +TrainerEntrySize=32 +TrainerCount=0x2B6 +TrainerClassNames=0x1C4A14 +TrainerClassCount=58 +TrainerClassNameLength=11 +TrainerNameLength=6 +ItemData=0x39A648 +ItemEntrySize=40 +ItemCount=348 +MoveData=0x1CCEEC +MoveNameLength=8 +MoveNames=0x1CACFC +AbilityNameLength=8 +AbilityNames=0x1CBC44 +TmMoves=0x35017C +IntroCryOffset=0x7AAA +IntroSpriteOffset=0x8838 +IntroPaletteOffset=0x8844 +IntroOtherOffset=0x8806 +PokemonFrontSprites=0x1BCB60 +PokemonNormalPalettes=0x1BEDC0 +MapBankCount=0x22 +MapBankSizes=[54,5,5,6,7,7,8,7,7,13,8,17,10,24,13,13,14,2,2,2,3,1,1,1,86,44,12,2,1,13,1,1,3,1] +ItemBallPic=59 +TradeTableOffset=0x1E9C48 +TradeTableSize=3 +TradesUnused=[] +StaticPokemonSupport=1 +StaticPokemon[]=[0x162B23, 0x162B50] // Lileep +StaticPokemon[]=[0x162B75, 0x162BA2] // Anorith +StaticPokemon[]=[0x174A01, 0x1800AD, 0x1749FA] // Groudon +StaticPokemon[]=[0x171D3A, 0x171D43] // Regirock +StaticPokemon[]=[0x1767CC, 0x1767D5] // Regice +StaticPokemon[]=[0x17687F, 0x176888] // Registeel +StaticPokemon[]=[0x179434, 0x17945A] // Latias (Southern Island) +StaticPokemon[]=[0x176B44, 0x176B4B] // Rayquaza +StaticPokemon[]=[0x1801EC, 0x1801F5] // Kecleons on OW (7) +StaticPokemon[]=[0x153337, 0x153340] // Kecleon w/ Steven +StaticPokemon[]=[0x175ADB, 0x175AE2] // Voltorb 1 +StaticPokemon[]=[0x175AF9, 0x175B00] // Voltorb 2 +StaticPokemon[]=[0x175B17, 0x175B1E] // Voltorb 3 +StaticPokemon[]=[0x18010A, 0x180111] // Electrode 1 +StaticPokemon[]=[0x180128, 0x18012F] // Electrode 2 +StaticPokemon[]=[0x14CF1E] // Wynaut Egg +StaticPokemon[]=[0x16C023, 0x16C033] // Beldum +StaticPokemon[]=[0x17E914] // Castform + +[Sapphire (J)] +Game=AXPJ +Version=0 +Type=Sapp +CopyFrom=Ruby (J) +PokemonStats=0x1D0978 +PokemonMovesets=0x1D9910 +PokemonTMHMCompat=0x1CEE3C +PokemonEvolutions=0x1D58D4 +StarterPokemon=0x3D25AC +TrainerData=0x1C4C24 +TrainerClassNames=0x1C49A4 +ItemData=0x39A62C +MoveData=0x1CCE7C +MoveNames=0x1CAC8C +AbilityNames=0x1CBBD4 +TmMoves=0x35010C +PokemonFrontSprites=0x1BCAF0 +PokemonNormalPalettes=0x1BED50 +TradeTableOffset=0x1E9BD8 +StaticPokemonSupport=1 +StaticPokemon[]=[0x162AB3, 0x162AE0] // Lileep +StaticPokemon[]=[0x162B05, 0x162B32] // Anorith +StaticPokemon[]=[0x174991, 0x18003D, 0x17498A] // Groudon +StaticPokemon[]=[0x171CCA, 0x171CD3] // Regirock +StaticPokemon[]=[0x17675C, 0x176765] // Regice +StaticPokemon[]=[0x17680F, 0x176818] // Registeel +StaticPokemon[]=[0x1793C4, 0x1793EA] // Latias (Southern Island) +StaticPokemon[]=[0x176AD4, 0x176ADB] // Rayquaza +StaticPokemon[]=[0x18017C, 0x180185] // Kecleons on OW (7) +StaticPokemon[]=[0x1532CB, 0x1532D4] // Kecleon w/ Steven +StaticPokemon[]=[0x175A6B, 0x175A72] // Voltorb 1 +StaticPokemon[]=[0x175A89, 0x175A90] // Voltorb 2 +StaticPokemon[]=[0x175AA7, 0x175AAE] // Voltorb 3 +StaticPokemon[]=[0x18009A, 0x1800A1] // Electrode 1 +StaticPokemon[]=[0x1800B8, 0x1800BF] // Electrode 2 +StaticPokemon[]=[0x14CEB2] // Wynaut Egg +StaticPokemon[]=[0x16BFB3, 0x16BFC3] // Beldum +StaticPokemon[]=[0x17E8A4] // Castform + +[Emerald (J)] +Game=BPEJ +Version=0 +Type=Em +TableFile=gba_jap +FreeSpace=0xE40000 +PokemonNameLength=6 +PokemonStats=0x2F0D70 +PokemonMovesets=0x2F9D08 +PokemonTMHMCompat=0x2EF228 +PokemonEvolutions=0x2F5CCC +StarterPokemon=0x590C08 +StarterItems=0xB0A66 +TrainerData=0x2E383C +TrainerEntrySize=32 +TrainerCount=0x357 +TrainerClassNames=0x2E3564 +TrainerClassCount=66 +TrainerClassNameLength=11 +TrainerNameLength=6 +ItemEntrySize=40 +ItemCount=376 +MoveNameLength=8 +AbilityNameLength=8 +TmMoves=0x5E144C +TmMovesDuplicate=0x5E18F8 +MoveTutorData=0x5E08C4 +MoveTutorMoves=30 +ItemImages=0x5DFCC8 +TmPals=[0xDB613C, 0xDB609C, 0xDB62F4, 0xDB6164, 0xDB627C, 0xDB62CC, 0xDB61B4, 0xDB62A4, 0xDB622C, 0xDB62A4, 0xDB61DC, 0xDB60EC, 0xDB61B4, 0xDB6254, 0xDB6114, 0xDB618C, 0xDB60C4, 0xDB6204] +IntroCryOffset=0x3084C +IntroSpriteOffset=0x31664 +MapBankCount=0x22 +MapBankSizes=[57,5,5,6,7,8,9,7,7,14,8,17,10,23,13,15,15,2,2,2,3,1,1,1,108,61,89,2,1,13,1,1,2,1] +ItemBallPic=59 +TradeTableOffset=0x30D114 +TradeTableSize=4 +TradesUnused=[] +StaticPokemonSupport=1 +StaticPokemon[]=[0x2015EE, 0x201613, 0x201616, 0x201698, 0x2016A6] // Lileep +StaticPokemon[]=[0x201600, 0x2016B6, 0x2016B9, 0x20173B, 0x201749] // Anorith +StaticPokemon[]=[0x21D057, 0x21D065, 0x21D0BA] // Kyogre +StaticPokemon[]=[0x21D128, 0x21D136, 0x21D18B] // Groudon +StaticPokemon[]=[0x213E8A, 0x213E93, 0x213ED9] // Regirock +StaticPokemon[]=[0x21B8A1, 0x21B8AA, 0x21B8F0] // Regice +StaticPokemon[]=[0x21B9A3, 0x21B9AC, 0x21B9F2] // Registeel +StaticPokemon[]=[0x21BF95, 0x21BF9E, 0x21BFE4] // Rayquaza +StaticPokemon[]=[0x243407, 0x243410] // Kecleons on OW (7) +StaticPokemon[]=[0x1EDB4E, 0x1EDB57] // Kecleon w/ Steven +StaticPokemon[]=[0x21A80D, 0x21A814] // Voltorb 1 +StaticPokemon[]=[0x21A85A, 0x21A861] // Voltorb 2 +StaticPokemon[]=[0x21A8A7, 0x21A8AE] // Voltorb 3 +StaticPokemon[]=[0x217CF7, 0x217CFE] // Electrode 1 +StaticPokemon[]=[0x217D44, 0x217D4B] // Electrode 2 +StaticPokemon[]=[0x222AB7, 0x222AC5] // Sudowoodo in Battle Frontier +StaticPokemon[]=[0x222944] +StaticPokemon[]=[0x222957] // Latias/Latios on Southern Island +StaticPokemon[]=[0x23B6A0, 0x23B6B0, 0x23B6FA, 0x23B705] // Deoxys on Birth Island +StaticPokemon[]=[0x23B4DB, 0x23B515, 0x23B56A, 0x23B575] // Mew on Faraway Island +StaticPokemon[]=[0x23C1AE, 0x23C1DD, 0x23C22C, 0x23C237] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x23C2F6, 0x23C301, 0x23C34B, 0x23C356] // Lugia on Navel Rock +StaticPokemon[]=[0x1E5912] // Wynaut Egg +StaticPokemon[]=[0x20C8FA, 0x20C8FD, 0x20C97F, 0x20C990] // Beldum +StaticPokemon[]=[0x24161F, 0x241622] // Castform + +[Emerald (T-Eng)] +Game=BPET +Version=0 +Type=Em +CopyStaticPokemon=1 +CopyFrom=Emerald (J) + +[Fire Red (J)] +Game=BPRJ +Version=0 +Type=FRLG +TableFile=gba_jap +FreeSpace=0x800000 +PokemonNameLength=6 +PokemonStats=0x2111A8 +PokemonMovesets=0x21A1C0 +PokemonTMHMCompat=0x20F5D8 +PokemonEvolutions=0x216184 +BattleTrappersBanned=[55,56,57,58,59] +StarterPokemon=0x17D1D0 +TrainerData=0x1FDFD8 +TrainerEntrySize=32 +TrainerCount=0x2E7 +TrainerClassNames=0x1FDB3C +TrainerClassCount=107 +TrainerClassNameLength=11 +TrainerNameLength=6 +ItemEntrySize=40 +ItemCount=374 +MoveNameLength=8 +AbilityNameLength=8 +TmMoves=0x419D34 +TmMovesDuplicate=0x419F9C +MoveTutorData=0x4192F0 +MoveTutorMoves=15 +ItemImages=0x39C79C +TmPals=[0xD91E8C, 0xD91DEC, 0xD92044, 0xD91EB4, 0xD91FCC, 0xD9201C, 0xD91F04, 0xD91FF4, 0xD91F7C, 0xD91FF4, 0xD91F2C, 0xD91E3C, 0xD91F04, 0xD91FA4, 0xD91E64, 0xD91EDC, 0xD91E14, 0xD91F54] +IntroCryOffset=0x13034C +IntroSpriteOffset=0x1317B4 +IntroOtherOffset=0x131760 +MapBankCount=0x2B +MapBankSizes=[5,123,60,66,4,6,8,10,6,8,20,10,8,2,10,4,2,2,2,1,1,2,2,3,2,3,2,1,1,1,1,7,5,5,8,8,5,5,1,1,1,2,1] +ItemBallPic=92 +TradeTableOffset=0x22D2F8 +TradeTableSize=9 +TradesUnused=[] +StaticPokemonSupport=1 +StaticPokemon[]=[0x184BB8, 0x184BBB, 0x184BFB, 0x184C2F] // Eevee in Celadon Mansion +StaticPokemon[]=[0x18A2E0, 0x18A2E7] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x18A326, 0x18A32D] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x16C18B, 0x16C192] // Electrode in The Power Plant +StaticPokemon[]=[0x16C1E9, 0x16C1F0] // Electrode in The Power Plant +StaticPokemon[]=[0x16C117, 0x16C11E, 0x16C172] // Zapdos in the Power Plant +StaticPokemon[]=[0x16B2B8, 0x16B2BF, 0x16B313] // Articuno in the Seafoams +StaticPokemon[]=[0x16C846, 0x16C84D, 0x16C8A1] // Moltres in Mt.Ember +StaticPokemon[]=[0x1693E1, 0x1693F5, 0x16943B] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x1769F0, 0x1769F7] // Sleeping Snorlax (12) +StaticPokemon[]=[0x177A5E, 0x177A65] // Sleeping Snorlax (16) +StaticPokemon[]=[0x16C9CA, 0x16C9D0] // Hypno in Berry Forest +StaticPokemon[]=[0x16F243, 0x16F253, 0x16F29D, 0x16F2A8] // Deoxys on Birth Island +StaticPokemon[]=[0x16EF99, 0x16EFC8, 0x16F017, 0x16F022] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x16F0E7, 0x16F0F2, 0x16F13C, 0x16F147] // Lugia on Navel Rock +StaticPokemon[]=[0x18959C, 0x1895A0, 0x1895AB, 0x18949D] // Old Amber +StaticPokemon[]=[0x189512, 0x189516, 0x189521, 0x189421] // Helix Fossil +StaticPokemon[]=[0x189557, 0x18955B, 0x189566, 0x18945F] // Dome Fossil +StaticPokemon[]=[0x1675D8, 0x167617, 0x16764A] // Lapras in Silph. Co +StaticPokemon[]=[0x18C668, 0x18C727] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x185A8A, 0x185B06] // Abra +StaticPokemon[]=[0x185A9A, 0x185B11] // Clefairy +StaticPokemon[]=[0x185ABA, 0x185B27] // Scyther +StaticPokemon[]=[0x185AAA, 0x185B1C] // Dratini +StaticPokemon[]=[0x185ACA, 0x185B32] // Porygon + +[Fire Red (J) 1.1] +Game=BPRJ +Version=1 +Type=FRLG +CopyFrom=Fire Red (J) +PokemonStats=0x20C9C0 +PokemonMovesets=0x2159D8 +PokemonTMHMCompat=0x20ADF0 +PokemonEvolutions=0x21199C +StarterPokemon=0x17CD8E +TrainerData=0x1F97F0 +TrainerClassNames=0x1F9354 +TmMoves=0x415634 +TmMovesDuplicate=0x41589C +MoveTutorData=0x414BF0 +ItemImages=0x397F5C +IntroCryOffset=0x13029C +IntroSpriteOffset=0x131704 +IntroOtherOffset=0x1316B0 +TradeTableOffset=0x228B10 +StaticPokemonSupport=1 +StaticPokemon[]=[0x184774, 0x184777, 0x1847B7, 0x1847EB] // Eevee in Celadon Mansion +StaticPokemon[]=[0x189E9C, 0x189EA3] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x189EE2, 0x189EE9] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x16BD20, 0x16BD27] // Electrode in The Power Plant +StaticPokemon[]=[0x16BD7E, 0x16BD85] // Electrode in The Power Plant +StaticPokemon[]=[0x16BCAC, 0x16BCB3, 0x16BD07] // Zapdos in the Power Plant +StaticPokemon[]=[0x16AE48, 0x16AE4F, 0x16AEA3] // Articuno in the Seafoams +StaticPokemon[]=[0x16C3DB, 0x16C3E2, 0x16C436] // Moltres in Mt.Ember +StaticPokemon[]=[0x168F71, 0x168F85, 0x168FCB] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x1765A4, 0x1765AB] // Sleeping Snorlax (12) +StaticPokemon[]=[0x177612, 0x177619] // Sleeping Snorlax (16) +StaticPokemon[]=[0x16C55F, 0x16C565] // Hypno in Berry Forest +StaticPokemon[]=[0x16EDDB, 0x16EDEB, 0x16EE35, 0x16EE40] // Deoxys on Birth Island +StaticPokemon[]=[0x16EB31, 0x16EB60, 0x16EBAF, 0x16EBBA] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x16EC7F, 0x16EC8A, 0x16ECD4, 0x16ECDF] // Lugia on Navel Rock +StaticPokemon[]=[0x189158, 0x18915C, 0x189167, 0x189059] // Old Amber +StaticPokemon[]=[0x1890CE, 0x1890D2, 0x1890DD, 0x188FDD] // Helix Fossil +StaticPokemon[]=[0x189113, 0x189117, 0x189122, 0x18901B] // Dome Fossil +StaticPokemon[]=[0x167163, 0x1671A2, 0x1671D5] // Lapras in Silph. Co +StaticPokemon[]=[0x18C224, 0x18C2E3] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x185646, 0x1856C2] // Abra +StaticPokemon[]=[0x185656, 0x1856CD] // Clefairy +StaticPokemon[]=[0x185676, 0x1856E3] // Scyther +StaticPokemon[]=[0x185666, 0x1856D8] // Dratini +StaticPokemon[]=[0x185686, 0x1856EE] // Porygon + +[Leaf Green (J)] +Game=BPGJ +Version=0 +Type=FRLG +CopyFrom=Fire Red (J) +PokemonStats=0x211184 +PokemonMovesets=0x21A1A0 +PokemonTMHMCompat=0x20F5B4 +PokemonEvolutions=0x216164 +StarterPokemon=0x17D1AC +TrainerData=0x1FDFB4 +TrainerClassNames=0x1FDB18 +TmMoves=0x419CBC +TmMovesDuplicate=0x419F24 +MoveTutorData=0x419278 +ItemImages=0x39C60C +TmPals=[0xD91F0C, 0xD91E6C, 0xD920C4, 0xD91F34, 0xD9204C, 0xD9209C, 0xD91F84, 0xD92074, 0xD91FFC, 0xD92074, 0xD91FAC, 0xD91EBC, 0xD91F84, 0xD92024, 0xD91EE4, 0xD91F5C, 0xD91E94, 0xD91FD4] +IntroCryOffset=0x130324 +IntroSpriteOffset=0x13178C +IntroOtherOffset=0x131738 +TradeTableOffset=0x22D2D8 +StaticPokemonSupport=1 +StaticPokemon[]=[0x184B94, 0x184B97, 0x184BD7, 0x184C0B] // Eevee in Celadon Mansion +StaticPokemon[]=[0x18A2BC, 0x18A2C3] // Hitmonlee in Fighting Dojo +StaticPokemon[]=[0x18A302, 0x18A309] // Hitmonchan in Fighting Dojo +StaticPokemon[]=[0x16C167, 0x16C16E] // Electrode in The Power Plant +StaticPokemon[]=[0x16C1C5, 0x16C1CC] // Electrode in The Power Plant +StaticPokemon[]=[0x16C0F3, 0x16C0FA, 0x16C14E] // Zapdos in the Power Plant +StaticPokemon[]=[0x16B294, 0x16B29B, 0x16B2EF] // Articuno in the Seafoams +StaticPokemon[]=[0x16C822, 0x16C829, 0x16C87D] // Moltres in Mt.Ember +StaticPokemon[]=[0x1693BD, 0x1693D1, 0x169417] // Mewtwo in Unk. Dungeon +StaticPokemon[]=[0x1769CC, 0x1769D3] // Sleeping Snorlax (12) +StaticPokemon[]=[0x177A3A, 0x177A41] // Sleeping Snorlax (16) +StaticPokemon[]=[0x16C9A6, 0x16C9AC] // Hypno in Berry Forest +StaticPokemon[]=[0x16F21F, 0x16F22F, 0x16F279, 0x16F284] // Deoxys on Birth Island +StaticPokemon[]=[0x16EF75, 0x16EFA4, 0x16EFF3, 0x16EFFE] // Ho-Oh on Navel Rock +StaticPokemon[]=[0x16F0C3, 0x16F0CE, 0x16F118, 0x16F123] // Lugia on Navel Rock +StaticPokemon[]=[0x189578, 0x18957C, 0x189587, 0x189479] // Old Amber +StaticPokemon[]=[0x1894EE, 0x1894F2, 0x1894FD, 0x1893FD] // Helix Fossil +StaticPokemon[]=[0x189533, 0x189537, 0x189542, 0x18943B] // Dome Fossil +StaticPokemon[]=[0x1675B4, 0x1675F3, 0x167626] // Lapras in Silph. Co +StaticPokemon[]=[0x18C644, 0x18C703] // Magikarp in Mt.Moon Center +StaticPokemon[]=[0x185A66, 0x185AE2] +StaticPokemon[]=[0x185A76, 0x185AED] +StaticPokemon[]=[0x185A86, 0x185B19] +StaticPokemon[]=[0x185A96, 0x185AF8] +StaticPokemon[]=[0x185AA6, 0x185B0E] \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gen4_offsets.ini b/src/com/dabomstew/pkrandom/config/gen4_offsets.ini new file mode 100755 index 000000000..cc8ef5948 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gen4_offsets.ini @@ -0,0 +1,539 @@ +[Diamond (U)] +Game=ADAE +Type=DP +PokemonStats=poketool/personal/personal.narc +PokemonMovesets=poketool/personal/wotbl.narc +PokemonEvolutions=poketool/personal/evo.narc +BabyPokemon=poketool/personal/pms.narc +TrainerData=poketool/trainer/trdata.narc +TrainerPokemon=poketool/trainer/trpoke.narc +MoveData=poketool/waza/waza_tbl.narc +WildPokemon=fielddata/encountdata/d_enc_data.narc +InGameTrades=fielddata/pokemon_trade/fld_trade.narc +StarterPokemonOvlNumber=64 +StarterPokemonOffset=0x1B88 +Scripts=fielddata/script/scr_seq_release.narc +Text=msgdata/msg.narc +HasExtraPokemonNames=Yes +PokemonNamesTextOffset=362 +TrainerNamesTextOffset=559 +TrainerClassesTextOffset=560 +MoveDescriptionsTextOffset=587 +MoveNamesTextOffset=588 +AbilityNamesTextOffset=552 +ItemNamesTextOffset=344 +ItemDescriptionsTextOffset=343 +StarterScreenTextOffset=320 +PokedexSpeciesTextOffset=621 +StarterLocationTextOffset=270 +IngameTradesTextOffset=326 +HiddenItemTableOffset=0xF2DB4 +HiddenItemCount=229 +Events=fielddata/eventdata/zone_event_release.narc +ItemBallsScriptOffset=370 +ItemBallsSkip=[40, 196] +StaticPokemonSupport=1 +StaticPokemon[]=[230:0x4AE, 230:0xE9A, 230:0xECE, 230:0x1201, 230:0x1235] // Dialga +StaticPokemon[]=[230:0x4B4, 230:0xEA0, 230:0xED4, 230:0x1207, 230:0x123B] // Palkia +StaticPokemon[]=[352:0x39, 352:0x48] // Uxie +StaticPokemon[]=[348:0x81, 348:0x90] // Azelf +StaticPokemon[]=[278:0x16F, 278:0x17E] // Heatran +StaticPokemon[]=[309:0x88, 309:0x94] // Regigigas +StaticPokemon[]=[283:0x50, 283:0x5F] // Giratina +StaticPokemon[]=[354:0x40] // Darkrai +StaticPokemon[]=[302:0x39, 302:0x48] // Shaymin +StaticPokemon[]=[232:0x45, 232:0x53, 232:0x62] // Arceus +StaticPokemon[]=[112:0xB5] // Eevee +StaticPokemon[]=[90:0x568] // Happiny +StaticPokemon[]=[321:0x332] // Riolu +StaticPokemon[]=[210:0x1C5, 210:0x1D6] // Drifloon +StaticPokemon[]=[329:0x74, 329:0x80] // Rotom +StaticPokemon[]=[406:0x153, 406:0x160] // Spiritomb + + +[Pearl (U)] +Game=APAE +Type=DP +CopyStaticPokemon=1 +CopyFrom=ADAE +PokemonStats=poketool/personal_pearl/personal.narc +WildPokemon=fielddata/encountdata/p_enc_data.narc + +[Platinum (U)] +Game=CPUE +Type=Plat +CopyFrom=ADAE +PokemonStats=poketool/personal/pl_personal.narc +MoveData=poketool/waza/pl_waza_tbl.narc +WildPokemon=fielddata/encountdata/pl_enc_data.narc +Scripts=fielddata/script/scr_seq.narc +StarterPokemonOvlNumber=78 +StarterPokemonOffset=0x1BC0 +MoveTutorMovesOvlNumber=5 +MoveTutorMovesOffset=0x2FF64 +MoveTutorCount=38 +MoveTutorBytesCount=12 +MoveTutorCompatOvlNumber=5 +MoveTutorCompatOffset=0x3012C +MoveTutorCompatBytes=5 +Text=msgdata/pl_msg.narc +PokemonNamesTextOffset=412 +TrainerNamesTextOffset=618 +TrainerClassesTextOffset=619 +MoveDescriptionsTextOffset=646 +MoveNamesTextOffset=647 +AbilityNamesTextOffset=610 +ItemDescriptionsTextOffset=391 +ItemNamesTextOffset=392 +StarterScreenTextOffset=360 +PokedexSpeciesTextOffset=711 +StarterLocationTextOffset=466 +IngameTradesTextOffset=370 +HiddenItemTableOffset=0xEA378 +HiddenItemCount=257 +Events=fielddata/eventdata/zone_event.narc +ItemBallsScriptOffset=404 +ItemBallsSkip=[25, 238, 321, 325, 326] +StaticPokemonSupport=1 +StaticPokemon[]=[361:0x39, 361:0x48] // Uxie +StaticPokemon[]=[357:0x81, 357:0x90] // Azelf +StaticPokemon[]=[239:0xAB, 239:0xB8] // Dialga +StaticPokemon[]=[240:0xAB, 240:0xB8] // Palkia +StaticPokemon[]=[286:0x103, 286:0x112] // Heatran +StaticPokemon[]=[317:0x88, 317:0x94] // Regigigas +StaticPokemon[]=[392:0xB0, 392:0xBD] // Registeel +StaticPokemon[]=[394:0xB0, 394:0xBD] // Regice +StaticPokemon[]=[396:0xB0, 396:0xBD] // Regirock +StaticPokemon[]=[363:0x8E] // Darkrai +StaticPokemon[]=[310:0x87, 310:0x96] // Shaymin +StaticPokemon[]=[238:0x6C, 238:0x7A, 238:0x89] // Arceus +StaticPokemon[]=[71:0xE5B] // Togepi +StaticPokemon[]=[117:0x79] // Eevee +StaticPokemon[]=[153:0x7D] // Porygon +StaticPokemon[]=[329:0x338] // Riolu +StaticPokemon[]=[216:0x1C9, 216:0x1DA] // Drifloon +StaticPokemon[]=[337:0x51, 337:0x5D] // Rotom +StaticPokemon[]=[441:0x153, 441:0x160] // Spiritomb + +[HeartGold (U)] +Game=IPKE +Type=HGSS +PokemonStats=a/0/0/2 +PokemonMovesets=a/0/3/3 +PokemonEvolutions=a/0/3/4 +BabyPokemon=poketool/personal/pms.narc +TrainerData=a/0/5/5 +TrainerPokemon=a/0/5/6 +MoveData=a/0/1/1 +WildPokemon=a/0/3/7 +InGameTrades=a/1/1/2 +Scripts=a/0/1/2 +MoveTutorMovesOvlNumber=1 +MoveTutorMovesOffset=0x23AE0 +MoveTutorCount=51 +MoveTutorBytesCount=4 +MoveTutorCompat=fielddata/wazaoshie/waza_oshie.bin +MoveTutorCompatOffset=0 +MoveTutorCompatBytesCount=8 +Text=a/0/2/7 +HasExtraPokemonNames=Yes +PokemonNamesTextOffset=237 +TrainerNamesTextOffset=729 +TrainerClassesTextOffset=730 +MoveDescriptionsTextOffset=749 +MoveNamesTextOffset=750 +AbilityNamesTextOffset=720 +ItemDescriptionsTextOffset=221 +ItemNamesTextOffset=222 +StarterScreenTextOffset=190 +IngameTradesTextOffset=200 +HiddenItemTableOffset=0xFA558 +HiddenItemCount=231 +Events=a/0/3/2 +ItemBallsScriptOffset=141 +ItemBallsSkip=[58] +StaticPokemonSupport=1 +StaticPokemon[]=[104:0x108] // Lugia +StaticPokemon[]=[21:0xD1] // Ho-oh +StaticPokemon[]=[216:0x58F, 216:0x6E8, 216:0x708, 24:0x67, 24:0xB4, 24:0x314, 24:0x320, 24:0xD4] // Suicune +StaticPokemon[]=[14:0x2F, 14:0x3B] // Articuno +StaticPokemon[]=[191:0x26B, 191:0x277] // Zapdos +StaticPokemon[]=[106:0x2F, 106:0x3B] // Moltres +StaticPokemon[]=[11:0x2F, 11:0x3B] // Mewtwo +StaticPokemon[]=[134:0xA3, 134:0xB4] // Kyogre +StaticPokemon[]=[133:0xA3, 133:0xB4] // Groudon +StaticPokemon[]=[135:0xDA, 135:0xEB, 135:0x62, 135:0x98] // Rayquaza +StaticPokemon[]=[131:0x43A, 131:0x67C, 131:0x872, 131:0x8E4] // Dialga +StaticPokemon[]=[131:0x4A2, 131:0x695, 131:0x88D, 131:0x8FA] // Palkia +StaticPokemon[]=[131:0x50A] // Giratina +StaticPokemon[]=[750:0x4CC] // Latias +StaticPokemon[]=[750:0x4B7] // Latios +StaticPokemon[]=[243:0x2FD, 243:0x14B] // Sudowoodo +StaticPokemon[]=[58:0x61, 58:0x6D] // Lapras +StaticPokemon[]=[938:0x3CD, 938:0x3DE] // Red Gyarados +StaticPokemon[]=[197:0x6C, 197:0x7D] // Snorlax +StaticPokemon[]=[89:0xF3D, 89:0x1078, 89:0x10A5, 89:0x112C, 89:0x11B1] // Koffing @ Rocket Base +StaticPokemon[]=[89:0xF6A, 89:0xFC4, 89:0x101E, 89:0x104B, 89:0x1159, 89:0x1186] // Voltorb @ Rocket Base +StaticPokemon[]=[89:0xF97, 89:0xFF1, 89:0x10D2, 89:0x10FF, 89:0x11E0] // Geodude @ Rocket Base +StaticPokemon[]=[90:0x784] // Electrode @ Rocket Base (1) +StaticPokemon[]=[90:0x7E8] // Electrode @ Rocket Base (2) +StaticPokemon[]=[90:0x84C] // Electrode @ Rocket Base (3) +StaticPokemon[]=[892:0x61] // Eevee +StaticPokemon[]=[98:0x71] // Tyrogue +StaticPokemon[]=[112:0x4D1] // Dratini +StaticPokemon[]=[740:0x66F, 740:0x675, 740:0x695, 740:0x818, 740:0x8BC] // Bulbasaur +StaticPokemon[]=[740:0x71D, 740:0x723, 740:0x743, 740:0x833, 740:0x8D7] // Squirtle +StaticPokemon[]=[740:0x7CB, 740:0x7D1, 740:0x7F1] // Charmander +StaticPokemon[]=[837:0x28F] // Treecko +StaticPokemon[]=[837:0x2A8] // Torchic +StaticPokemon[]=[837:0x2B4] // Mudkipz +StaticPokemon[]=[860:0x146, 860:0x14D] // Primo's Mareep Egg +StaticPokemon[]=[860:0x180, 860:0x187] // Primo's Wooper Egg +StaticPokemon[]=[860:0x1BA, 860:0x1C1] // Primo's Slugma Egg +StaticPokemonTrades=[6,7] // Shuckie & Kenya +MysteryEggOffset=0x1C80E // Togepi Mystery Egg + +[SoulSilver (U)] +Game=IPGE +Type=HGSS +CopyStaticPokemon=1 +CopyFrom=IPKE +WildPokemon=a/1/3/6 + +[Pearl (J)] +Game=APAJ +Type=DP +CopyFrom=APAE +StarterPokemonOffset=0x30 +Scripts=fielddata/script/scr_seq.narc +HiddenItemTableOffset=0xF4C14 +Events=fielddata/eventdata/zone_event.narc +HasExtraPokemonNames=No +PokemonNamesTextOffset=356 +TrainerNamesTextOffset=550 +TrainerClassesTextOffset=551 +MoveDescriptionsTextOffset=574 +MoveNamesTextOffset=575 +AbilityNamesTextOffset=544 +ItemDescriptionsTextOffset=340 +ItemNamesTextOffset=341 +StarterScreenTextOffset=318 +PokedexSpeciesTextOffset=607 +StarterLocationTextOffset=269 +IngameTradesTextOffset=324 + +[Diamond (J)] +Game=ADAJ +Type=DP +CopyFrom=ADAE +StarterPokemonOffset=0x30 +Scripts=fielddata/script/scr_seq.narc +HiddenItemTableOffset=0xF4C10 +Events=fielddata/eventdata/zone_event.narc +HasExtraPokemonNames=No +PokemonNamesTextOffset=356 +TrainerNamesTextOffset=550 +TrainerClassesTextOffset=551 +MoveDescriptionsTextOffset=574 +MoveNamesTextOffset=575 +AbilityNamesTextOffset=544 +ItemDescriptionsTextOffset=340 +ItemNamesTextOffset=341 +StarterScreenTextOffset=318 +PokedexSpeciesTextOffset=607 +StarterLocationTextOffset=269 +IngameTradesTextOffset=324 + +[Pearl (G)] +Game=APAD +Type=DP +CopyFrom=APAE +HiddenItemTableOffset=0xF2DC4 +InGameTrades=resource/ger/pokemon_trade/fld_trade.narc + +[Diamond (G)] +Game=ADAD +Type=DP +CopyFrom=ADAE +HiddenItemTableOffset=0xF2DC4 +InGameTrades=resource/ger/pokemon_trade/fld_trade.narc + +[Pearl (S)] +Game=APAS +Type=DP +CopyFrom=APAE +HiddenItemTableOffset=0xF2E00 +InGameTrades=resource/spa/pokemon_trade/fld_trade.narc + +[Diamond (S)] +Game=ADAS +Type=DP +CopyFrom=ADAE +HiddenItemTableOffset=0xF2E00 +InGameTrades=resource/spa/pokemon_trade/fld_trade.narc + +[Pearl (I)] +Game=APAI +Type=DP +CopyFrom=APAE +HiddenItemTableOffset=0xF2D68 +InGameTrades=resource/ita/pokemon_trade/fld_trade.narc + +[Diamond (I)] +Game=ADAI +Type=DP +CopyFrom=ADAE +HiddenItemTableOffset=0xF2D68 +InGameTrades=resource/ita/pokemon_trade/fld_trade.narc + +[Pearl (F)] +Game=APAF +Type=DP +CopyFrom=APAE +HiddenItemTableOffset=0xF2DF4 +InGameTrades=resource/fra/pokemon_trade/fld_trade.narc + +[Diamond (F)] +Game=ADAF +Type=DP +CopyFrom=ADAE +HiddenItemTableOffset=0xF2DF4 +InGameTrades=resource/fra/pokemon_trade/fld_trade.narc + +[Pearl (K)] +Game=APAK +Type=DP +CopyFrom=APAE +HiddenItemTableOffset=0xEE400 +HasExtraPokemonNames=No +PokemonNamesTextOffset=357 +TrainerNamesTextOffset=552 +TrainerClassesTextOffset=553 +MoveDescriptionsTextOffset=576 +MoveNamesTextOffset=577 +AbilityNamesTextOffset=546 +ItemDescriptionsTextOffset=341 +ItemNamesTextOffset=342 +StarterScreenTextOffset=319 +PokedexSpeciesTextOffset=609 +StarterLocationTextOffset=269 +IngameTradesTextOffset=325 +InGameTrades=resource/kor/pokemon_trade/fld_trade.narc + +[Diamond (K)] +Game=ADAK +Type=DP +CopyFrom=ADAE +HiddenItemTableOffset=0xEE400 +HasExtraPokemonNames=No +PokemonNamesTextOffset=357 +TrainerNamesTextOffset=552 +TrainerClassesTextOffset=553 +MoveDescriptionsTextOffset=576 +MoveNamesTextOffset=577 +AbilityNamesTextOffset=546 +ItemDescriptionsTextOffset=341 +ItemNamesTextOffset=342 +StarterScreenTextOffset=319 +PokedexSpeciesTextOffset=609 +StarterLocationTextOffset=269 +IngameTradesTextOffset=325 +InGameTrades=resource/kor/pokemon_trade/fld_trade.narc + +[Platinum (J)] +Game=CPUJ +Type=Plat +CopyFrom=CPUE +HiddenItemTableOffset=0xE9A4C +MoveTutorMovesOffset=0x2FD54 +MoveTutorCompatOffset=0x2FF1C +StarterPokemonOffset=0x1BAC +HasExtraPokemonNames=No +PokemonNamesTextOffset=408 +TrainerNamesTextOffset=611 +TrainerClassesTextOffset=612 +MoveDescriptionsTextOffset=635 +MoveNamesTextOffset=636 +AbilityNamesTextOffset=604 +ItemDescriptionsTextOffset=389 +ItemNamesTextOffset=390 +StarterScreenTextOffset=359 +PokedexSpeciesTextOffset=698 +StarterLocationTextOffset=460 +IngameTradesTextOffset=369 + +[Platinum (G)] +Game=CPUD +Type=Plat +CopyFrom=CPUE +HiddenItemTableOffset=0xEA3D0 +MoveTutorMovesOffset=0x2FF80 +MoveTutorCompatOffset=0x30148 +InGameTrades=resource/ger/pokemon_trade/fld_trade.narc + +[Platinum (F)] +Game=CPUF +Type=Plat +CopyFrom=CPUE +HiddenItemTableOffset=0xEA400 +MoveTutorMovesOffset=0x2FF6C +MoveTutorCompatOffset=0x30134 +InGameTrades=resource/fra/pokemon_trade/fld_trade.narc + +[Platinum (S)] +Game=CPUS +Type=Plat +CopyFrom=CPUE +HiddenItemTableOffset=0xEA40C +MoveTutorMovesOffset=0x2FF6C +MoveTutorCompatOffset=0x30134 +InGameTrades=resource/spa/pokemon_trade/fld_trade.narc + +[Platinum (I)] +Game=CPUI +Type=Plat +CopyFrom=CPUE +HiddenItemTableOffset=0xEA394 +MoveTutorMovesOffset=0x2FF74 +MoveTutorCompatOffset=0x3013C +InGameTrades=resource/ita/pokemon_trade/fld_trade.narc + +[Platinum (K)] +Game=CPUK +Type=Plat +CopyFrom=CPUE +HiddenItemTableOffset=0xEAE00 +MoveTutorMovesOffset=0x2FF5C +MoveTutorCompatOffset=0x30124 +HasExtraPokemonNames=No +PokemonNamesTextOffset=408 +TrainerNamesTextOffset=612 +TrainerClassesTextOffset=613 +MoveDescriptionsTextOffset=636 +MoveNamesTextOffset=637 +AbilityNamesTextOffset=605 +ItemDescriptionsTextOffset=389 +ItemNamesTextOffset=390 +StarterScreenTextOffset=359 +PokedexSpeciesTextOffset=701 +StarterLocationTextOffset=461 +IngameTradesTextOffset=369 +InGameTrades=resource/kor/pokemon_trade/fld_trade.narc + +[HeartGold (J)] +Game=IPKJ +Type=HGSS +CopyFrom=IPKE +HiddenItemTableOffset=0xF9D08 +MoveTutorMovesOffset=0x23954 +HasExtraPokemonNames=No +PokemonNamesTextOffset=232 +TrainerNamesTextOffset=719 +TrainerClassesTextOffset=720 +MoveDescriptionsTextOffset=738 +MoveNamesTextOffset=739 +AbilityNamesTextOffset=711 +ItemDescriptionsTextOffset=218 +ItemNamesTextOffset=219 +StarterScreenTextOffset=188 +IngameTradesTextOffset=198 + +[SoulSilver (J)] +Game=IPGJ +Type=HGSS +CopyFrom=IPGE +HiddenItemTableOffset=0xF9D08 +MoveTutorMovesOffset=0x23954 +HasExtraPokemonNames=No +PokemonNamesTextOffset=232 +TrainerNamesTextOffset=719 +TrainerClassesTextOffset=720 +MoveDescriptionsTextOffset=738 +MoveNamesTextOffset=739 +AbilityNamesTextOffset=711 +ItemDescriptionsTextOffset=218 +ItemNamesTextOffset=219 +StarterScreenTextOffset=188 +IngameTradesTextOffset=198 + +[HeartGold (K)] +Game=IPKK +Type=HGSS +CopyFrom=IPKE +HasExtraPokemonNames=No +HiddenItemTableOffset=0xFAC04 +PokemonNamesTextOffset=233 +TrainerNamesTextOffset=723 +TrainerClassesTextOffset=724 +MoveDescriptionsTextOffset=742 +MoveNamesTextOffset=743 +AbilityNamesTextOffset=715 +ItemDescriptionsTextOffset=219 +ItemNamesTextOffset=220 +StarterScreenTextOffset=189 +IngameTradesTextOffset=199 + +[SoulSilver (K)] +Game=IPGK +Type=HGSS +CopyFrom=IPGE +HasExtraPokemonNames=No +HiddenItemTableOffset=0xFABFC +PokemonNamesTextOffset=233 +TrainerNamesTextOffset=723 +TrainerClassesTextOffset=724 +MoveDescriptionsTextOffset=742 +MoveNamesTextOffset=743 +AbilityNamesTextOffset=715 +ItemDescriptionsTextOffset=219 +ItemNamesTextOffset=220 +StarterScreenTextOffset=189 +IngameTradesTextOffset=199 + +[HeartGold (F)] +Game=IPKF +Type=HGSS +CopyFrom=IPKE +HiddenItemTableOffset=0xFA53C + +[SoulSilver (F)] +Game=IPGF +Type=HGSS +CopyFrom=IPGE +HiddenItemTableOffset=0xFA53C + +[HeartGold (G)] +Game=IPKD +Type=HGSS +CopyFrom=IPKE +HiddenItemTableOffset=0xFA50C + +[SoulSilver (G)] +Game=IPGD +Type=HGSS +CopyFrom=IPGE +HiddenItemTableOffset=0xFA50C + +[HeartGold (S)] +Game=IPKS +Type=HGSS +CopyFrom=IPKE +HiddenItemTableOffset=0xFA540 + +[SoulSilver (S)] +Game=IPGS +Type=HGSS +CopyFrom=IPGE +HiddenItemTableOffset=0xFA548 + +[HeartGold (I)] +Game=IPKI +Type=HGSS +CopyFrom=IPKE +HiddenItemTableOffset=0xFA4D0 + +[SoulSilver (I)] +Game=IPGI +Type=HGSS +CopyFrom=IPGE +HiddenItemTableOffset=0xFA4D0 \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gen5_offsets.ini b/src/com/dabomstew/pkrandom/config/gen5_offsets.ini new file mode 100755 index 000000000..d74f20c39 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gen5_offsets.ini @@ -0,0 +1,334 @@ +[Black (U)] +Game=IRBO +Type=BW1 +PokemonStats=a/0/1/6 +PokemonMovesets=a/0/1/8 +PokemonEvolutions=a/0/1/9 +BabyPokemon=a/0/2/0 +TrainerData=a/0/9/2 +TrainerPokemon=a/0/9/3 +MoveData=a/0/2/1 +WildPokemon=a/1/2/6 +Scripts=a/0/5/7 +InGameTrades=a/1/6/5 +TradesUnused=[1,3,7,8,9,10,11,12] +StarterOffsets1=[782:639, 782:644, 782:0x361, 782:0x5FD, 304:0xF9, 304:0x19C] +StarterOffsets2=[782:687, 782:692, 782:0x356, 782:0x5F2, 304:0x11C, 304:0x1C4] +StarterOffsets3=[782:716, 782:721, 782:0x338, 782:0x5D4, 304:0x12C, 304:0x1D9] +PokedexGivenFileOffset=792 +StarterGraphics=a/2/0/5 +PokemonGraphics=a/0/0/4 +TextStrings=a/0/0/2 +TextStory=a/0/0/3 +PokemonNamesTextOffset=70 +TrainerNamesTextOffset=190 +TrainerClassesTextOffset=191 +TrainerMugshotsTextOffset=176 +MoveDescriptionsTextOffset=202 +MoveNamesTextOffset=203 +AbilityNamesTextOffset=182 +ItemDescriptionsTextOffset=53 +ItemNamesTextOffset=54 +StarterLocationTextOffset=430 +IngameTradesTextOffset=35 +ItemBallsScriptOffset=864 +HiddenItemsScriptOffset=865 +ItemBallsSkip=[] +HiddenItemsSkip=[] +StaticPokemonSupport=1 +StaticPokemon[]=[304:0x121, 304:0x1C9, 304:0x299] // Grass Monkey +StaticPokemon[]=[304:0x131, 304:0x1DE, 304:0x2B7] // Fire Monkey +StaticPokemon[]=[304:0xFE, 304:0x1A1, 304:0x268] // Water Monkey +StaticPokemon[]=[526:0x758] // Magikarp +StaticPokemon[]=[94:0x810, 94:0x64, 94:0xB4, 94:0x44B, 94:0x7AB, 94:0x7D0, 94:0x7DC] // Zorua +StaticPokemon[]=[776:0x85, 776:0xB2] // Larvesta +StaticPokemon[]=[316:0x369] // Darmanitan 1 +StaticPokemon[]=[316:0x437] // Darmanitan 2 +StaticPokemon[]=[316:0x505] // Darmanitan 3 +StaticPokemon[]=[316:0x5D3] // Darmanitan 4 +StaticPokemon[]=[316:0x6A1] // Darmanitan 5 +StaticPokemon[]=[306:0x65, 306:0x8F] // Musharna +StaticPokemon[]=[770:0x2F8, 770:0x353] // Zoroark +StaticPokemon[]=[364:0xE, 364:0x1F] // Volcarona +StaticPokemon[]=[474:0x1CE, 474:0x20A] // Victini +StaticPokemon[]=[426:0x133, 426:0x15B, 556:0x1841, 556:0xCFC, 556:0x1878, 556:0x18EA] // Reshiram +StaticPokemon[]=[426:0x127, 426:0x174, 556:0x184D, 556:0x186C, 556:0xD15, 556:0x18DE] // Zekrom +StaticPokemon[]=[670:0x415, 670:0x426, 692:0x1E2] // Cobalion +StaticPokemon[]=[458:0x10, 458:0x21, 692:0x203] // Terrakion +StaticPokemon[]=[312:0x10, 312:0x21, 692:0x224] // Virizion +StaticPokemon[]=[752:0x66D, 752:0x6CC, 752:0x6DD] // Landorus +StaticPokemon[]=[464:0x10, 468:0x4F, 468:0x60] // Kyurem + +[White (U)] +Game=IRAO +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRBO +TradesUnused=[0,2,7,8,9,10,11,12] + +[Black 2 (U)] +Game=IREO +Type=BW2 +CopyFrom=IRBO +TrainerData=a/0/9/1 +TrainerPokemon=a/0/9/2 +DriftveilPokemon=a/2/5/0 +WildPokemon=a/1/2/7 +HiddenHollows=a/2/7/3 +HabitatList=a/2/9/6 +PokemonAreaData=a/1/7/6 +Scripts=a/0/5/6 +InGameTrades=a/1/6/3 +TradesUnused=[25] +StarterOffsets1=[854:0x58B, 854:0x590, 854:0x595] +StarterOffsets2=[854:0x5C0, 854:0x5C5, 854:0x5CA] +StarterOffsets3=[854:0x5E2, 854:0x5E7, 854:0x5EC] +PokedexGivenFileOffset=854 +StarterGraphics=a/2/0/2 +PokemonGraphics=a/0/0/4 +MoveTutorOvlNumber=36 +MoveTutorDataOffset=0x51538 +PokemonNamesTextOffset=90 +TrainerNamesTextOffset=382 +TrainerClassesTextOffset=383 +TrainerMugshotsTextOffset=368 +MoveDescriptionsTextOffset=402 +MoveNamesTextOffset=403 +AbilityNamesTextOffset=374 +ItemDescriptionsTextOffset=63 +ItemNamesTextOffset=64 +StarterLocationTextOffset=169 +IngameTradesTextOffset=37 +ItemBallsScriptOffset=1240 +HiddenItemsScriptOffset=1241 +ItemBallsSkip=[] +HiddenItemsSkip=[] +StaticPokemonSupport=1 +StaticPokemon[]=[662:0x1DE, 662:0x240, 740:0xCD, 740:0xFC, 740:0x12C, 740:0x14C] // Cobalion +StaticPokemon[]=[730:0x13A, 730:0x15F, 730:0x19B, 730:0x1BB] // Virizion +StaticPokemon[]=[948:0x45D, 948:0x48D, 948:0x4AD] // Terrakion +StaticPokemon[]=[426:0x38A, 426:0x39B, 556:0x367, 556:0x568, 556:0x5E6, 556:0x6E1, 1208:0x3A4, 1208:0xA6A, 1208:0x717] // Reshiram +StaticPokemon[]=[426:0x36B, 426:0x37C, 556:0x350, 556:0x551, 556:0x5C7, 556:0x6C3, 1208:0x38D, 1208:0xA53, 1208:0x706] // Zekrom +StaticPokemon[]=[1112:0x133, 1122:0x2BA, 1122:0x311, 1128:0x37A, 1128:0x3D1, 1208:0x1B7, 1208:0x1F8, 1208:0xD8B, 1208:0xD97, 1208:0xDB6, 1208:0xDC2, 1208:0x723, 1208:0xF3D, 1208:0xF4E] // Kyurem +StaticPokemon[]=[304:0xCC, 304:0x14B, 304:0x1BC, 304:0x237, 304:0x327, 304:0x3E6, 304:0x4A1, 304:0x54A, 304:0x5BD, 304:0x5CE] // Latias +StaticPokemon[]=[304:0xB5, 304:0x134, 304:0x1A5, 304:0x220, 304:0x310, 304:0x3CF, 304:0x48A, 304:0x533, 304:0x59E, 304:0x5AF] // Latios +StaticPokemon[]=[32:0x247, 32:0x2B0, 32:0x2C1, 1034:0x12A] // Uxie +StaticPokemon[]=[684:0x136, 684:0x1C2, 684:0x1D3, 1034:0x169] // Mesprit +StaticPokemon[]=[950:0xA1, 950:0x10A, 950:0x11B, 1034:0x1BE] // Azelf +StaticPokemon[]=[1222:0x134, 1222:0x145, 1018:0x32] // Regirock +StaticPokemon[]=[1224:0x134, 1224:0x145, 1018:0x2C] // Regice +StaticPokemon[]=[1226:0x134, 1226:0x145, 1018:0x38] // Registeel +StaticPokemon[]=[1018:0x97, 1018:0xA8] // Regigigas +StaticPokemon[]=[526:0x48D, 526:0x512, 526:0x523] // Cresselia +StaticPokemon[]=[1068:0x193, 1068:0x1D6, 1068:0x1E7, 1080:0x193, 1080:0x1D6, 1080:0x1E7] // Heatran +StaticPokemon[]=[652:0x5C6, 652:0x5E9] // Mandibuzz +StaticPokemon[]=[1102:0x592, 1102:0x5B5] // Braviary +StaticPokemon[]=[364:0xE, 364:0x32, 364:0x40] // Volcarona +StaticPokemon[]=[1030:0x290, 1030:0x2A1] // Crustle +StaticPokemon[]=[480:0xE1, 480:0x10A, 480:0x131, 480:0x15A] // Jellicent +StaticPokemon[]=[1168:0x2C, 1168:0x4F] // Shiny Haxorus +StaticPokemon[]=[988:0x382] // Eevee +StaticPokemon[]=[664:0x3B5, 664:0x3E2, 664:0x40F, 664:0x43C] // Deerling +StaticPokemon[]=[880:0xAB4, 880:0xAC7] // Shiny Gible +StaticPokemon[]=[880:0xAD3, 880:0xAE6] // Shiny Dratini +StaticPokemon[]=[54:0xDD] // Happiny Egg +StaticPokemon[]=[526:0x27E] // Magikarp +StaticPokemonFormValues=[1208:0xD8D, 1208:0xD9B, 1208:0xDB8, 1208:0xDC6, 664:0x3B7, 664:0x3E4, 664:0x411, 664:0x43E] // Kyurem, Deerling + +[White 2 (U)] +Game=IRDO +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IREO +MoveTutorDataOffset=0x5152C +TradesUnused=[24] + +[Black (F)] +Game=IRBF +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRBO + +[White (F)] +Game=IRAF +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRAO + +[Black (G)] +Game=IRBD +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRBO + +[White (G)] +Game=IRAD +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRAO + +[Black (S)] +Game=IRBS +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRBO + +[White (S)] +Game=IRAS +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRAO + +[Black (I)] +Game=IRBI +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRBO + +[White (I)] +Game=IRAI +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRAO + +[Black (J)] +Game=IRBJ +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRBO +MoveDescriptionsTextOffset=203 +MoveNamesTextOffset=204 + +[White (J)] +Game=IRAJ +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRAO +MoveDescriptionsTextOffset=203 +MoveNamesTextOffset=204 + +[Black (K)] +Game=IRBK +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRBO + +[White (K)] +Game=IRAK +Type=BW1 +CopyStaticPokemon=1 +CopyFrom=IRAO + +[Black 2 (F)] +Game=IREF +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IREO +MoveTutorDataOffset=0x5152C + +[White 2 (F)] +Game=IRDF +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IRDO +MoveTutorDataOffset=0x51520 + +[Black 2 (G)] +Game=IRED +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IREO +MoveTutorDataOffset=0x5155C + +[White 2 (G)] +Game=IRDD +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IRDO +MoveTutorDataOffset=0x51550 + +[Black 2 (I)] +Game=IREI +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IREO +MoveTutorDataOffset=0x51554 + +[White 2 (I)] +Game=IRDI +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IRDO +MoveTutorDataOffset=0x51548 + +[Black 2 (S)] +Game=IRES +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IREO +MoveTutorDataOffset=0x5153C + +[White 2 (S)] +Game=IRDS +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IRDO +MoveTutorDataOffset=0x51530 + +[Black 2 (J)] +Game=IREJ +Type=BW2 +CopyFrom=IREO +MoveTutorDataOffset=0x512DC +StaticPokemonSupport=1 +StaticPokemon[]=[662:0x1DE, 662:0x240, 740:0xCD, 740:0xFC, 740:0x12C, 740:0x14C] // Cobalion +StaticPokemon[]=[730:0x13A, 730:0x15F, 730:0x19B, 730:0x1BB] // Virizion +StaticPokemon[]=[948:0x45D, 948:0x48D, 948:0x4AD] // Terrakion +StaticPokemon[]=[426:0x38A, 426:0x39B, 556:0x367, 556:0x568, 556:0x5E6, 556:0x6E1, 1208:0x3A4, 1208:0xA6A, 1208:0x717] // Reshiram +StaticPokemon[]=[426:0x36B, 426:0x37C, 556:0x350, 556:0x551, 556:0x5C7, 556:0x6C3, 1208:0x38D, 1208:0xA53, 1208:0x706] // Zekrom +StaticPokemon[]=[1112:0x133, 1122:0x2BA, 1122:0x311, 1128:0x37A, 1128:0x3D1, 1208:0x1B7, 1208:0x1F8, 1208:0xD8B, 1208:0xD97, 1208:0xDB6, 1208:0xDC2, 1208:0x723, 1208:0xF3D, 1208:0xF4E] // Kyurem +StaticPokemon[]=[304:0xCC, 304:0x14B, 304:0x1B8, 304:0x22F, 304:0x31B, 304:0x3D6, 304:0x491, 304:0x536, 304:0x5A9, 304:0x5BA] // Latias +StaticPokemon[]=[304:0xB5, 304:0x134, 304:0x1A1, 304:0x218, 304:0x304, 304:0x3BF, 304:0x47A, 304:0x51F, 304:0x58A, 304:0x59B] // Latios +StaticPokemon[]=[32:0x247, 32:0x2B0, 32:0x2C1, 1034:0x12A] // Uxie +StaticPokemon[]=[684:0x136, 684:0x1C2, 684:0x1D3, 1034:0x169] // Mesprit +StaticPokemon[]=[950:0xA1, 950:0x10A, 950:0x11B, 1034:0x1BE] // Azelf +StaticPokemon[]=[1222:0x134, 1222:0x145, 1018:0x32] // Regirock +StaticPokemon[]=[1224:0x134, 1224:0x145, 1018:0x2C] // Regice +StaticPokemon[]=[1226:0x134, 1226:0x145, 1018:0x38] // Registeel +StaticPokemon[]=[1018:0x97, 1018:0xA8] // Regigigas +StaticPokemon[]=[526:0x48D, 526:0x512, 526:0x523] // Cresselia +StaticPokemon[]=[1068:0x171, 1068:0x1B4, 1068:0x1C5, 1080:0x171, 1080:0x1B4, 1080:0x1C5] // Heatran +StaticPokemon[]=[652:0x5C6, 652:0x5E9] // Mandibuzz +StaticPokemon[]=[1102:0x592, 1102:0x5B5] // Braviary +StaticPokemon[]=[364:0xE, 364:0x32, 364:0x40] // Volcarona +StaticPokemon[]=[1030:0x290, 1030:0x2A1] // Crustle +StaticPokemon[]=[480:0xE1, 480:0x10A, 480:0x131, 480:0x15A] // Jellicent +StaticPokemon[]=[1168:0x2C, 1168:0x4F] // Shiny Haxorus +StaticPokemon[]=[988:0x382] // Eevee +StaticPokemon[]=[664:0x3B5, 664:0x3E2, 664:0x40F, 664:0x43C] // Deerling +StaticPokemon[]=[880:0xAB4, 880:0xAC7] // Shiny Gible +StaticPokemon[]=[880:0xAD3, 880:0xAE6] // Shiny Dratini +StaticPokemon[]=[54:0xDD] // Happiny Egg +StaticPokemon[]=[526:0x27E] // Magikarp +StaticPokemonFormValues=[1208:0xD8D, 1208:0xD9B, 1208:0xDB8, 1208:0xDC6, 664:0x3B7, 664:0x3E4, 664:0x411, 664:0x43E] // Kyurem, Deerling + +[White 2 (J)] +Game=IRDJ +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IREJ +MoveTutorDataOffset=0x512D0 + +[Black 2 (K)] +Game=IREK +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IREO +MoveTutorDataOffset=0x5160C + +[White 2 (K)] +Game=IRDK +Type=BW2 +CopyStaticPokemon=1 +CopyFrom=IRDO +MoveTutorDataOffset=0x51600 \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/green_translation.tbl b/src/com/dabomstew/pkrandom/config/green_translation.tbl new file mode 100755 index 000000000..e01baf04d --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/green_translation.tbl @@ -0,0 +1,66 @@ +7F= +87=' +8D=- +90=0 +91=1 +92=2 +93=3 +94=4 +95=5 +96=6 +97=7 +98=8 +99=9 +9F=? +A1=A +A2=B +A3=C +A4=D +A5=E +A6=F +A7=G +A8=H +A9=I +AA=J +AB=K +AC=L +AD=M +AE=N +AF=O +B0=P +B1=Q +B2=R +B3=S +B4=T +B5=U +B6=V +B7=W +B8=X +B9=Y +BA=Z +C1=a +C2=b +C3=c +C4=d +C5=e +C6=f +C7=g +C8=h +C9=i +CA=j +CB=k +CC=l +CD=m +CE=n +CF=o +D0=p +D1=q +D2=r +D3=s +D4=t +D5=u +D6=v +D7=w +D8=x +D9=y +DA=z \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gsc_english.tbl b/src/com/dabomstew/pkrandom/config/gsc_english.tbl new file mode 100755 index 000000000..ac7cd6e9a --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gsc_english.tbl @@ -0,0 +1,96 @@ +4A=[pk] +54=[POKé] +58=$ +74=â„– +75=… +7F= +79=┌ +7A=─ +7B=â” +7C=│ +7D=â”” +7E=┘ +80=A +81=B +82=C +83=D +84=E +85=F +86=G +87=H +88=I +89=J +8A=K +8B=L +8C=M +8D=N +8E=O +8F=P +90=Q +91=R +92=S +93=T +94=U +95=V +96=W +97=X +98=Y +99=Z +9A=( +9B=) +9C=: +9D=; +9E=[ +9F=] +A0=a +A1=b +A2=c +A3=d +A4=e +A5=f +A6=g +A7=h +A8=i +A9=j +AA=k +AB=l +AC=m +AD=n +AE=o +AF=p +B0=q +B1=r +B2=s +B3=t +B4=u +B5=v +B6=w +B7=x +B8=y +B9=z +C0=Ä +C1=Ö +C2=Ãœ +C3=ä +C4=ö +C5=ü +D0='d +D1='l +D2='m +D3='r +D4='s +D5='t +D6='v +E0=' +E1=[PK] +E2=[MN] +E3=- +E6=? +E7=! +E8=. +E9=& +EA=é +EB=→ +F0=$ +F2=[.] +F4=, \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gsc_espita.tbl b/src/com/dabomstew/pkrandom/config/gsc_espita.tbl new file mode 100755 index 000000000..adb453731 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gsc_espita.tbl @@ -0,0 +1,122 @@ +4A=[pk] +54=[POKé] +58=$ +74=â„– +75=… +7F= +79=┌ +7A=─ +7B=â” +7C=│ +7D=â”” +7E=┘ +80=A +81=B +82=C +83=D +84=E +85=F +86=G +87=H +88=I +89=J +8A=K +8B=L +8C=M +8D=N +8E=O +8F=P +90=Q +91=R +92=S +93=T +94=U +95=V +96=W +97=X +98=Y +99=Z +9A=( +9B=) +9C=: +9D=; +9E=[ +9F=] +A0=a +A1=b +A2=c +A3=d +A4=e +A5=f +A6=g +A7=h +A8=i +A9=j +AA=k +AB=l +AC=m +AD=n +AE=o +AF=p +B0=q +B1=r +B2=s +B3=t +B4=u +B5=v +B6=w +B7=x +B8=y +B9=z +BA=à +BB=è +BC=é +BD=ù +BE=À +BF=à +C0=Ä +C1=Ö +C2=Ãœ +C3=ä +C4=ö +C5=ü +C6=È +C7=É +C8=ÃŒ +C9=à +CA=Ñ +CB=Ã’ +CC=Ó +CD=Ù +CE=Ú +CF=á +D0=ì +D1=í +D2=ñ +D3=ò +D4=ó +D5=ú +D6=° +D7=& +D8='d +D9='l +DA='m +DB='r +DC='s +DD='t +DE='v +E0=' +E1=[PK] +E2=[MN] +E3=- +E4=¿ +E5=¡ +E6=? +E7=! +E8=. +E9=& +EA=é +EB=→ +F0=$ +F2=[.] +F4=, \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/gsc_freger.tbl b/src/com/dabomstew/pkrandom/config/gsc_freger.tbl new file mode 100755 index 000000000..7e481b3fa --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/gsc_freger.tbl @@ -0,0 +1,115 @@ +4A=[pk] +54=[POKé] +58=$ +74=â„– +75=… +7F= +79=┌ +7A=─ +7B=â” +7C=│ +7D=â”” +7E=┘ +80=A +81=B +82=C +83=D +84=E +85=F +86=G +87=H +88=I +89=J +8A=K +8B=L +8C=M +8D=N +8E=O +8F=P +90=Q +91=R +92=S +93=T +94=U +95=V +96=W +97=X +98=Y +99=Z +9A=( +9B=) +9C=: +9D=; +9E=[ +9F=] +A0=a +A1=b +A2=c +A3=d +A4=e +A5=f +A6=g +A7=h +A8=i +A9=j +AA=k +AB=l +AC=m +AD=n +AE=o +AF=p +B0=q +B1=r +B2=s +B3=t +B4=u +B5=v +B6=w +B7=x +B8=y +B9=z +BA=à +BB=è +BC=é +BD=ù +BE=ß +BF=ç +C0=Ä +C1=Ö +C2=Ãœ +C3=ä +C4=ö +C5=ü +C6=ë +C7=ï +C8=â +C9=ô +CA=û +CB=ê +CC=î +D4=c' +D5=d' +D6=j' +D7=l' +D8=m' +D9=n' +DA=p' +DB=s' +DC='s +DD=t' +DE=u' +DF=y' +E0=' +E1=[PK] +E2=[MN] +E3=- +E4=+ +E6=? +E7=! +E8=. +E9=& +EA=é +EB=→ +F0=$ +F2=[.] +F4=, \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/nicknames.txt b/src/com/dabomstew/pkrandom/config/nicknames.txt new file mode 100755 index 000000000..e9cd587d1 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/nicknames.txt @@ -0,0 +1,20 @@ +Red +Blue +Yellow +Cuddles +Angry +Scout +Buster +Rusty +Shadow +Gizmo +Chester +Sylvester +Merlin +Smokey +Angel +Midnight +Willow +Baxter +Cookie +Callie \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/rby_english.tbl b/src/com/dabomstew/pkrandom/config/rby_english.tbl new file mode 100755 index 000000000..9ec4cbe62 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/rby_english.tbl @@ -0,0 +1,88 @@ +4A=[pk] +54=[POKé] +58=$ +74=â„– +75=… +7F= +79=┌ +7A=─ +7B=â” +7C=│ +7D=â”” +7E=┘ +80=A +81=B +82=C +83=D +84=E +85=F +86=G +87=H +88=I +89=J +8A=K +8B=L +8C=M +8D=N +8E=O +8F=P +90=Q +91=R +92=S +93=T +94=U +95=V +96=W +97=X +98=Y +99=Z +9A=( +9B=) +9C=: +9D=; +9E=[ +9F=] +A0=a +A1=b +A2=c +A3=d +A4=e +A5=f +A6=g +A7=h +A8=i +A9=j +AA=k +AB=l +AC=m +AD=n +AE=o +AF=p +B0=q +B1=r +B2=s +B3=t +B4=u +B5=v +B6=w +B7=x +B8=y +B9=z +BA=é +BB='d +BC='l +BD='s +BE='t +BF='v +E0=' +E1=[PK] +E2=[MN] +E3=- +E4='r +E5='m +E6=? +E7=! +E8=. +F0=$ +F2=[.] +F4=, \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/rby_espita.tbl b/src/com/dabomstew/pkrandom/config/rby_espita.tbl new file mode 100755 index 000000000..405b0badd --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/rby_espita.tbl @@ -0,0 +1,119 @@ +4A=[pk] +54=[POKé] +58=$ +74=â„– +75=… +7F= +79=┌ +7A=─ +7B=â” +7C=│ +7D=â”” +7E=┘ +80=A +81=B +82=C +83=D +84=E +85=F +86=G +87=H +88=I +89=J +8A=K +8B=L +8C=M +8D=N +8E=O +8F=P +90=Q +91=R +92=S +93=T +94=U +95=V +96=W +97=X +98=Y +99=Z +9A=( +9B=) +9C=: +9D=; +9E=[ +9F=] +A0=a +A1=b +A2=c +A3=d +A4=e +A5=f +A6=g +A7=h +A8=i +A9=j +AA=k +AB=l +AC=m +AD=n +AE=o +AF=p +B0=q +B1=r +B2=s +B3=t +B4=u +B5=v +B6=w +B7=x +B8=y +B9=z +BA=à +BB=è +BC=é +BD=ù +BE=À +BF=à +C0=Ä +C1=Ö +C2=Ãœ +C3=ä +C4=ö +C5=ü +C6=È +C7=É +C8=ÃŒ +C9=à +CA=Ñ +CB=Ã’ +CC=Ó +CD=Ù +CE=Ú +CF=á +D0=ì +D1=í +D2=ñ +D3=ò +D4=ó +D5=ú +D6=° +D7=& +D8='d +D9='l +DA='m +DB='r +DC='s +DD='t +DE='v +E0=' +E1=[PK] +E2=[MN] +E3=- +E4=¿ +E5=¡ +E6=? +E7=! +E8=. +F0=$ +F2=[.] +F4=, \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/rby_freger.tbl b/src/com/dabomstew/pkrandom/config/rby_freger.tbl new file mode 100755 index 000000000..382993127 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/rby_freger.tbl @@ -0,0 +1,112 @@ +4A=[pk] +54=[POKé] +58=$ +74=â„– +75=… +7F= +79=┌ +7A=─ +7B=â” +7C=│ +7D=â”” +7E=┘ +80=A +81=B +82=C +83=D +84=E +85=F +86=G +87=H +88=I +89=J +8A=K +8B=L +8C=M +8D=N +8E=O +8F=P +90=Q +91=R +92=S +93=T +94=U +95=V +96=W +97=X +98=Y +99=Z +9A=( +9B=) +9C=: +9D=; +9E=[ +9F=] +A0=a +A1=b +A2=c +A3=d +A4=e +A5=f +A6=g +A7=h +A8=i +A9=j +AA=k +AB=l +AC=m +AD=n +AE=o +AF=p +B0=q +B1=r +B2=s +B3=t +B4=u +B5=v +B6=w +B7=x +B8=y +B9=z +BA=à +BB=è +BC=é +BD=ù +BE=ß +BF=ç +C0=Ä +C1=Ö +C2=Ãœ +C3=ä +C4=ö +C5=ü +C6=ë +C7=ï +C8=â +C9=ô +CA=û +CB=ê +CC=î +D4=c' +D5=d' +D6=j' +D7=l' +D8=m' +D9=n' +DA=p' +DB=s' +DC='s +DD=t' +DE=u' +DF=y' +E0=' +E1=[PK] +E2=[MN] +E3=- +E4=+ +E6=? +E7=! +E8=. +F0=$ +F2=[.] +F4=, \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/trainerclasses.txt b/src/com/dabomstew/pkrandom/config/trainerclasses.txt new file mode 100755 index 000000000..fcebb2794 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/trainerclasses.txt @@ -0,0 +1,83 @@ +Cop +Man +God +Fan +Chef +Poet +Lazy +Lady +Gent +Boss +Biker +Chief +Clerk +Nurse +Tamer +Doctor +Leader +Artist +Rocker +Dancer +Writer +Author +Scribe +Fisher +Sailor +Ranger +Servant +Trainer +Cyclist +Gymnast +Artisan +Analyst +Juggler +Breeder +Swimmer +Manager +Operator +Designer +Director +Botanist +Engineer +Champion +Rich Kid +Rich Boy +Rich Girl +Performer +Developer +Gentleman +Schoolboy +Swimmer ♂ +Swimmer ♀ +Technician +Schoolgirl +Elite Four +Strong Man +Ghostwriter +Preschooler +Ace Trainer +Bug Catcher +Entrepreneur +Weightlifter +Cooltrainer♂ +Cooltrainer♀ +Elitetrainer +Twins +Ma&Pa +Sr&Jr +Couple +Ma & Pa +Sr & Jr +Bro&Sis +Ma & Pop +Mom & Pa +Mom & Pop +Sr. & Jr. +Ma And Pa +Champions +Old Couple +Bro And Sis +Cute Couple +Cool Couple +Young Couple +Cheerleaders \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/config/trainernames.txt b/src/com/dabomstew/pkrandom/config/trainernames.txt new file mode 100755 index 000000000..a74d12724 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/trainernames.txt @@ -0,0 +1,84 @@ +Pi +Jo +Bob +Max +Joe +Leo +Ann +Tia +Mia +Kim +Mike +Mark +Alex +Joey +Anne +Zoey +Jill +Kate +Robin +Wayne +Scott +Jimmy +Chloe +Donna +Sonya +Pansy +Dudley +Daniel +Landon +Thomas +Rachel +Noelle +Stacey +Cammie +Forrest +Esteban +Malcolm +Webster +Marilyn +Jasmine +Jessica +Crystal +Jonathan +Marshall +Lawrence +Tristian +Mallorie +Kristine +Veronica +Scarlett +Cristiano +Marcellus +Lillianna +Kimberley +Wellington +Florentino +Jacqueline +Antoinette +Christopher +Maximillian +Max&Joe +Joe&Max +Bob&Ann +Ann&Bob +Zoe&Kim +Kim&Zoe +Ally&Cam +Cam&Ally +Emma&May +May&Emma +Nick&Stu +Stu&Nick +Max & Joe +Joe & Max +Bob & Ann +Ann & Bob +Zoe & Kim +Kim & Zoe +Ally & Cam +Cam & Ally +Emma & May +May & Emma +Nick & Stu +Stu & Nick diff --git a/src/com/dabomstew/pkrandom/config/vietcrystal.tbl b/src/com/dabomstew/pkrandom/config/vietcrystal.tbl new file mode 100755 index 000000000..71a06ce37 --- /dev/null +++ b/src/com/dabomstew/pkrandom/config/vietcrystal.tbl @@ -0,0 +1,54 @@ +7F= +80=! +81=" +82=% +83=& +84=' +85=( +86=) +87=+ +88=, +89=- +8A=. +8B=/ +8C=0 +8D=1 +8E=2 +8F=3 +90=4 +91=5 +92=6 +93=7 +94=8 +95=9 +96=: +97=; +98== +99=> +9A=? +9B=A +9C=B +9D=C +9E=D +9F=E +A0=F +A1=G +A2=H +A3=I +A4=J +A5=K +A6=L +A7=M +A8=N +A9=O +AA=P +AB=Q +AC=R +AD=S +AE=T +AF=U +B0=V +B1=W +B2=X +B3=Y +B4=Z diff --git a/src/com/dabomstew/pkrandom/gui/AboutDialog.form b/src/com/dabomstew/pkrandom/gui/AboutDialog.form new file mode 100755 index 000000000..4fbd4cbf8 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/AboutDialog.form @@ -0,0 +1,117 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/dabomstew/pkrandom/gui/AboutDialog.java b/src/com/dabomstew/pkrandom/gui/AboutDialog.java new file mode 100755 index 000000000..ed26d13e1 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/AboutDialog.java @@ -0,0 +1,154 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- AboutDialog.java - describes this program! --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.awt.Desktop; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; + +/** + * + * @author Stewart + */ +public class AboutDialog extends javax.swing.JDialog { + + /** + * + */ + private static final long serialVersionUID = -4693681699711757794L; + /** + * Creates new form AboutDialog + */ + public AboutDialog(java.awt.Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + setLocationRelativeTo(parent); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + + // //GEN-BEGIN:initComponents + private void initComponents() { + + titleLabel = new javax.swing.JLabel(); + authorLabel = new javax.swing.JLabel(); + ackLabel = new javax.swing.JLabel(); + linkLabel = new javax.swing.JLabel(); + okButton = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("com/dabomstew/pkrandom/gui/Bundle"); // NOI18N + setTitle(bundle.getString("AboutDialog.title")); // NOI18N + + titleLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + titleLabel.setText(bundle.getString("AboutDialog.titleLabel.text")); // NOI18N + + authorLabel.setText(bundle.getString("AboutDialog.authorLabel.text")); // NOI18N + + ackLabel.setText(bundle.getString("AboutDialog.ackLabel.text")); // NOI18N + + linkLabel.setText(bundle.getString("AboutDialog.linkLabel.text")); // NOI18N + linkLabel.setCursor(new java.awt.Cursor(java.awt.Cursor.HAND_CURSOR)); + linkLabel.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseClicked(java.awt.event.MouseEvent evt) { + siteLinkClicked(evt); + } + }); + + okButton.setText(bundle.getString("AboutDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(titleLabel) + .addGroup(layout.createSequentialGroup() + .addGap(10, 10, 10) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(ackLabel) + .addComponent(authorLabel) + .addComponent(linkLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(okButton) + .addGap(135, 135, 135)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(titleLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(authorLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ackLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(linkLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE) + .addComponent(okButton) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void siteLinkClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_siteLinkClicked + Desktop desktop = java.awt.Desktop.getDesktop(); + try { + desktop.browse(new URI("http://pokehacks.dabomstew.com/randomizer")); + } catch (IOException e) { + } catch (URISyntaxException e) { + } + }//GEN-LAST:event_siteLinkClicked + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed + this.setVisible(false); + }//GEN-LAST:event_okButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel ackLabel; + private javax.swing.JLabel authorLabel; + private javax.swing.JLabel linkLabel; + private javax.swing.JButton okButton; + private javax.swing.JLabel titleLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/src/com/dabomstew/pkrandom/gui/Bundle.properties b/src/com/dabomstew/pkrandom/gui/Bundle.properties new file mode 100755 index 000000000..aeadedc62 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/Bundle.properties @@ -0,0 +1,280 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. + +RandomizerGUI.tpPowerLevelsCB.text=Try to use Pokemon with similar strength? +RandomizerGUI.starterPokemonPanel.border.title=Starter Pokemon +RandomizerGUI.spCustomRB.toolTipText=Lets you pick the 3 starter pokemon you want to use. +RandomizerGUI.spCustomRB.text=Custom +RandomizerGUI.spUnchangedRB.toolTipText=Don't change the starter Pokemon. +RandomizerGUI.spUnchangedRB.text=Unchanged +RandomizerGUI.riRomCodeLabel.text=ROM Code +RandomizerGUI.wpARCatchEmAllRB.toolTipText=If this is turned on, every random Pokemon chosen to replace an encounter will be one that hasn't been chosen before.
This should make sure that every Pokemon is catchable.
Once every Pokemon has been chosen the Pokemon choice list will start again from full. +RandomizerGUI.wpARCatchEmAllRB.text=Catch Em All Mode +RandomizerGUI.wpARTypeThemedRB.toolTipText=If this is chosen, every encounter area will have only Pokemon of a random type.
This may lead to a more realistic experience, or an odd one (e.g. if Fire pokemon are chosen to be in Surfing encounters). +RandomizerGUI.wpARTypeThemedRB.text=Type Theme Areas +RandomizerGUI.wildPokemonARulePanel.border.title=Additional Rule +RandomizerGUI.wpARNoneRB.toolTipText=Don't apply any other rules. +RandomizerGUI.wpARNoneRB.text=None +RandomizerGUI.spRandom2EvosRB.toolTipText=Picks 3 random starter Pokemon to be used.
These Pokemon must have 2 evolution stages, e.g. be like the starters in the real games. +RandomizerGUI.spRandom2EvosRB.text=Random (basic Pokemon with 2 evolutions) +RandomizerGUI.wpUseTimeCB.toolTipText=This affects games that have either seasons or morning/day/night encounter sets.
If this is checked, each of these sets will be treated as a separate "area".
So you will have to visit each place in morning/day/night, or in each season, to collect the Pokemon.
If this isn't checked, all of morning/day/night and all seasons will use the same encounter data. +RandomizerGUI.wpUseTimeCB.text=Use time-based encounters? +RandomizerGUI.wpNoLegendariesCB.text=Don't use legendaries? +RandomizerGUI.tmhmsPanel.border.title=TMs & HMs +RandomizerGUI.stpRandomTotalRB.toolTipText=Selecting this will replace every static Pokemon encounter, gift or purchase with another random one.
In this particular mode, any Pokemon can replace any other Pokemon, so you could get a Mew in the Game Corner.
Or fight Magikarp instead of Mewtwo... +RandomizerGUI.stpRandomTotalRB.text=Random (Completely) +RandomizerGUI.tmmUnchangedRB.toolTipText=Leave the moves in TMs as they are.
If Metronome Only Mode is selected, all TMs are changed to Metronome and this setting has no effect. +RandomizerGUI.tmmUnchangedRB.text=Unchanged +RandomizerGUI.tmMovesPanel.border.title=TM Moves +RandomizerGUI.tmHmCompatPanel.border.title=TM/HM Compatibility +RandomizerGUI.tmmRandomRB.toolTipText=Give each TM a new move.
HM moves are not affected, nor can they be selected to be put in TMs.
Each TM will still be unique.
If Metronome Only Mode is selected, all TMs are changed to Metronome and this setting has no effect. +RandomizerGUI.tmmRandomRB.text=Random +RandomizerGUI.pbsChangesShuffleRB.toolTipText=Shuffle each Pokemon's stats.
For example, its base Attack may be swapped with its base Special Attack, etc.
This does not make any Pokemon stronger or weaker. +RandomizerGUI.pbsChangesShuffleRB.text=Shuffle +RandomizerGUI.pbsChangesRandomEvosRB.toolTipText=Randomises each Pokemon's stats, as long as they fall within the original base stat total.
Evolutions of a Pokemon will follow that Pokemon's stat distribution.
This could make Pokemon stronger or weaker if they get unlucky or lucky rolls on stats they need. +RandomizerGUI.pbsChangesRandomEvosRB.text=Random (follow evolutions) +RandomizerGUI.riRomSupportLabel.text=Support: XXX +RandomizerGUI.baseStatsPanel.border.title=Pokemon Base Statistics +RandomizerGUI.pbsChangesUnchangedRB.toolTipText=Don't change Pokemon stats from the base at all. +RandomizerGUI.pbsChangesUnchangedRB.text=Unchanged +RandomizerGUI.spRandomRB.toolTipText=Picks 3 random starter Pokemon to be used. +RandomizerGUI.spRandomRB.text=Random (completely) +RandomizerGUI.staticPokemonPanel.border.title=Static Pokemon +RandomizerGUI.stpUnchangedRB.toolTipText=Static Pokemon remain the same. +RandomizerGUI.stpUnchangedRB.text=Unchanged +RandomizerGUI.stpRandomL4LRB.toolTipText=Selecting this will replace every static Pokemon encounter, gift or purchase with another random one.
In this particular mode, legendary pokemon will always be swapped for other legendaries.
Also, normal non-legendary Pokemon will only be swapped for other non-legendaries. +RandomizerGUI.stpRandomL4LRB.text=Random (Legendary <-> Legendary & Normal <-> Normal) +RandomizerGUI.romInfoPanel.border.title=ROM Information +RandomizerGUI.riRomNameLabel.text=ROM Name +RandomizerGUI.usePresetsButton.text=Premade Seed +RandomizerGUI.saveROMButton.text=Randomize (Save) +RandomizerGUI.trainersPokemonPanel.border.title=Trainers Pokemon +RandomizerGUI.goUpdateMovesCheckBox.toolTipText=If this is checked, moves will be updated to their Gen 6 stats (power, accuracy, etc) where possible.
If you don't like the changes made in Gen6, try the "Legacy" checkbox...
This does NOT add the Fairy type! Moves that were changed to Fairy type will keep their Gen 5 types. +RandomizerGUI.goUpdateMovesCheckBox.text=Update Moves +RandomizerGUI.tpUnchangedRB.toolTipText=Don't change Trainers' Pokemon at all. +RandomizerGUI.tpUnchangedRB.text=Unchanged +RandomizerGUI.goRemoveTradeEvosCheckBox.toolTipText=If this is checked, every evolution that isn't possible to do without trading in the current game will be changed.
Some of the types that will be changed are:
  • "Normal" trade evolutions
  • Trade evolutions with another condition, such as held item or Pokemon traded for
  • Day/night evolutions if there isn't day/night in the game
  • Contest-stat evolutions if there aren't contests in the game
  • Location-based evolutions, if those locations don't exist in the game
  • Move-based evolutions, only if you randomize movesets
+RandomizerGUI.goRemoveTradeEvosCheckBox.text=Change Impossible Evos +RandomizerGUI.goLowerCaseNamesCheckBox.toolTipText=If this is selected, all Pokemon names will be made into Camel Case.
e.g. VENUSAUR becomes Venusaur.
This looks better in Gen3 games, and OK in Gen1/Gen2 games. +RandomizerGUI.goLowerCaseNamesCheckBox.text=Lower Case Pokemon Names +RandomizerGUI.pmsRandomTotalRB.toolTipText=Randomise Pokemon movesets, completely ignoring the type of the move and the Pokemon.
Each Pokemon will get at least one reasonably accurate damaging move to begin with. +RandomizerGUI.pmsRandomTotalRB.text=Random (completely) +RandomizerGUI.goNationalDexCheckBox.toolTipText=If this is checked then the National Dex will be given to the player at the start of the game with the regular Pokedex.
This is only available and necessary for Gen 3 games, where in FRLG National Dex Pokemon can't evolve if you don't have it. +RandomizerGUI.goNationalDexCheckBox.text=Give National Dex at Start +RandomizerGUI.title=Universal Pokemon Randomizer +RandomizerGUI.generalOptionsPanel.border.title=General Options +RandomizerGUI.goUpdateTypesCheckBox.toolTipText=If this is checked, the type weakness/strengths/immunities will be updated to the current set.
For RBY, this means Ghost will be Super Effective against Psychic, among other changes. +RandomizerGUI.goUpdateTypesCheckBox.text=Update Type Effectiveness +RandomizerGUI.pmsRandomTypeRB.toolTipText=Randomise Pokemon movesets, preferring moves that are of the type (or one of the types) of the Pokemon.
Each Pokemon will get at least one reasonably accurate damaging move to begin with. +RandomizerGUI.pmsRandomTypeRB.text=Random (preferring same type) +RandomizerGUI.pmsUnchangedRB.toolTipText=Don't change Pokemon movesets at all. +RandomizerGUI.pmsUnchangedRB.text=Unchanged +RandomizerGUI.pokemonMovesetsPanel.border.title=Pokemon Movesets +RandomizerGUI.ptRandomTotalRB.toolTipText=Randomise Pokemon types completely.
Evolutions of a Pokemon are completely separate from that Pokemon, so the type of a Pokemon will likely completely change every evolution. +RandomizerGUI.ptRandomTotalRB.text=Random (completely) +RandomizerGUI.ptRandomFollowEvosRB.toolTipText=Randomise the types of each Pokemon, but make most evolutions copy the base Pokemon (except perhaps adding an extra secondary type).
Evolutions that don't copy types are the Eeveelutions, among others. +RandomizerGUI.ptRandomFollowEvosRB.text=Random (follow evolutions) +RandomizerGUI.ptUnchangedRB.toolTipText=Doesn't change Pokemon types at all. +RandomizerGUI.ptUnchangedRB.text=Unchanged +RandomizerGUI.pokemonTypesPanel.border.title=Pokemon Types +RandomizerGUI.pbsChangesRandomTotalRB.toolTipText=Randomises each Pokemon's stats, as long as they fall within the original base stat total.
Evolutions of a Pokemon will be completely separate from that Pokemon, so evolving a Pokemon will make its stats drastically change.
This could make Pokemon stronger or weaker if they get unlucky or lucky rolls on stats they need. +RandomizerGUI.pbsChangesRandomTotalRB.text=Random (completely) +RandomizerGUI.thcRandomTotalRB.toolTipText=Randomise the TMs and HMs that each Pokemon can learn.
Each TM or HM will have a 50% chance of being learnable regardless of type. +RandomizerGUI.thcRandomTotalRB.text=Random (completely) +RandomizerGUI.openROMButton.text=Open ROM +RandomizerGUI.thcUnchangedRB.toolTipText=Every Pokemon will be able to learn the same TMs that it could before.
Note that this applies even if you change the TM moves, which could lead to some odd combinations. +RandomizerGUI.thcUnchangedRB.text=Unchanged +RandomizerGUI.thcRandomTypeRB.toolTipText=Randomise the TMs and HMs that each Pokemon can learn.
Each TM or HM will have:
A 90% chance of being learnable if the Pokemon has it as (one of) its type(s).
A 50% chance of being learnable if the move is Normal and the Pokemon isn't.
A 25% chance otherwise. +RandomizerGUI.thcRandomTypeRB.text=Random (prefer same type) +RandomizerGUI.wpUnchangedRB.toolTipText=Don't change Wild Pokemon at all. +RandomizerGUI.wpUnchangedRB.text=Unchanged +RandomizerGUI.wpRandomRB.toolTipText=Completely randomise Wild Pokemon in every area.
This should mean that there are many different Pokemon in each area. +RandomizerGUI.wpRandomRB.text=Random +RandomizerGUI.wpArea11RB.toolTipText=Each Pokemon in a given encounter area will be replaced by another Pokemon in every slot it appears in.
This will make each area have a handful of random Pokemon. +RandomizerGUI.wpArea11RB.text=Area 1-to-1 Mapping +RandomizerGUI.wpGlobalRB.toolTipText=Every place a certain Pokemon appears in, it will be replaced by another set Pokemon.
This mode doesn't support any other rules except similar strength because it is too restrictive on its own. +RandomizerGUI.wpGlobalRB.text=Global 1-to-1 Mapping +RandomizerGUI.tpNoLegendariesCB.text=Don't use legendaries? +RandomizerGUI.tpRivalCarriesStarterCB.toolTipText=If this is selected, the rival will have their starter in every team they battle you with, evolved if it is far enough through.
The rest of their team will be chosen in the same way as every other trainer. +RandomizerGUI.tpRivalCarriesStarterCB.text=Rival carries starter through game? +RandomizerGUI.wildPokemonPanel.border.title=Wild Pokemon +RandomizerGUI.tpTypeThemedRB.toolTipText=Pick a type for each trainer and give them random Pokemon in that type.
Certain groups of trainers, such as the trainers in each gym, will all be given the same (random) type. +RandomizerGUI.tpTypeThemedRB.text=Type Themed +RandomizerGUI.tpRandomRB.toolTipText=Randomise Trainers' Pokemon completely. +RandomizerGUI.tpRandomRB.text=Random +RandomizerGUI.tpTypeWeightingCB.toolTipText=If this is checked, the number of trainers with each type will roughly match up to the number of Pokemon with that type.
This should reduce repetition of Pokemon, but may lead to a lot of trainers with the same type in a row. +RandomizerGUI.tpTypeWeightingCB.text=Weight types by # of Pokemon with them? +RandomizerGUI.tpPowerLevelsCB.toolTipText=If this is checked, the random Pokemon that replaces each Pokemon will be of similar power to the original.
However, preserving other rules such as type theming has precedence over this, so weaker or stronger Pokemon will be chosen if there are no other Pokemon available. +RandomizerGUI.tnRandomizeCB.toolTipText=Check this to randomize trainers' names when you fight them.
For RBY, this only includes the Gym Leaders and Elite 4, as no-one else has names.
For every other game this will replace the names of each individual trainer. +RandomizerGUI.tnRandomizeCB.text=Randomize Trainer Names +RandomizerGUI.tcnRandomizeCB.text=Randomize Trainer Class Names +RandomizerGUI.tcnRandomizeCB.toolTipText=Check this to randomize the class names to new names (e.g. "Youngster" could become "Misfit"). +RandomizerGUI.aboutButton.text=About +RandomizerGUI.cantWriteConfigFile=WARNING: The randomizer is unable to write its config file to the directory it is in.\nThis means that it will probably be unable to save the randomized ROMs it creates.\nPlease run the randomizer from a directory where you can write files.\nYou can try to use the randomizer as-is, but it will probably not work and the auto-updater will be disabled. +RandomizerGUI.copyNameFilesDialog.text=You appear to have customized name files in the config directory left over from an old version of the randomizer.\nWould you like these files to be copied to the main program directory so they are used in this version? +RandomizerGUI.copyNameFilesDialog.title=Copy custom names? +RandomizerGUI.copyNameFilesFailed=At least one file was not able to be copied. +RandomizerGUI.configFileMissing=The file %s is missing from the configuration and so this program cannot start.\nPlease make sure you extract the program from the ZIP file before running it. +RandomizerGUI.noRomLoaded=NO ROM LOADED +RandomizerGUI.loadingText=Loading... +RandomizerGUI.savingText=Saving... +RandomizerGUI.loadFailed=There was an unhandled exception trying to load your ROM.\nA log file containing some details has been saved to %s.\nPlease include this file in any bug reports you do. +RandomizerGUI.loadFailedNoLog=There was an unhandled exception trying to load your ROM. +RandomizerGUI.unreadableRom=Could not read %s from disk.\nPlease ensure you have read access to the ROM you're trying to open. +RandomizerGUI.tooShortToBeARom=%s appears to be a blank or nearly blank file.\nCheck to make sure you're opening the right file. +RandomizerGUI.openedZIPfile=%s is a ZIP archive, not a ROM.\nYou should extract it and try to randomize the actual ROM file inside. +RandomizerGUI.openedRARfile=%s is a RAR archive, not a ROM.\nYou should extract it and try to randomize the actual ROM file inside. +RandomizerGUI.openedIPSfile=%s is an IPS patch, not a ROM.\nYou should apply it to a ROM first before trying to randomize the result. +RandomizerGUI.unsupportedRom=Could not load %s - it's not a supported ROM. +RandomizerGUI.romSupportPrefix=Support: +RandomizerGUI.processFailed=There was an unhandled exception trying to process your ROM.\nA log file containing some details has been saved to %s.\nPlease include this file in any bug reports you do. +RandomizerGUI.processFailedNoLog=There was an unhandled exception trying to process your ROM. +RandomizerGUI.raceModeRequirements=You can't use Race Mode without randomizing either the wild Pokemon or the trainer Pokemon.\nReview this and try again. +RandomizerGUI.pokeLimitNotChosen=You enabled the option to limit the Pokemon that appear, but didn't choose any to allow.\nSelect some by clicking on the "Limit Pokemon" button and try again. +RandomizerGUI.presetFailTrainerClasses=Can't use this preset because you have a different set of random trainer class names to the creator.\nHave them make you a rndp file instead. +RandomizerGUI.presetFailTrainerNames=Can't use this preset because you have a different set of random trainer names to the creator.\nHave them make you a rndp file instead. +RandomizerGUI.presetFailNicknames=Can't use this preset because you have a different set of random nicknames to the creator.\nHave them make you a rndp file instead. +RandomizerGUI.starterUnavailable=Could not set one of the custom starters from the settings file because it does not exist in this generation. +RandomizerGUI.saveFailedIO=There was an unhandled exception trying to save your ROM to disk.\nA log file containing some details has been saved to %s.\nPlease include this file in any bug reports you do. +RandomizerGUI.saveFailedIONoLog=There was an unhandled exception trying to save your ROM to disk. +RandomizerGUI.raceModeCheckValuePopup=Your check value for the race is:\n%08X\nDistribute this along with the preset file, if you're the race maker!\nIf you received this in a race, compare it to the value the race maker gave. +RandomizerGUI.saveLogDialog.text=Do you want to save a log file of the randomization performed?\nThis may allow you to gain an unfair advantage, do not do so if you are doing something like a race. +RandomizerGUI.saveLogDialog.title=Save Log? +RandomizerGUI.logSaveFailed=Could not save log file! +RandomizerGUI.logSaved=Log file saved to\n%s.log +RandomizerGUI.randomizationDone=Randomization Complete. You can now play! +RandomizerGUI.saveFailed=There was an unhandled exception trying to save your ROM.\nA log file containing some details has been saved to %s.\nPlease include this file in any bug reports you do. +RandomizerGUI.saveFailedNoLog=There was an unhandled exception trying to save your ROM. +RandomizerGUI.cantOverwriteDS=You cannot overwrite the original ROM when you save a DS randomization.\nPlease choose a different filename. +RandomizerGUI.noUpdates=No new updates found. +RandomizerGUI.settingsFileOlder=This settings file is for an old randomizer version.\nYou should make a new file. +RandomizerGUI.settingsFileNewer=This settings file is for a newer randomizer version.\nYou should upgrade your randomizer. +RandomizerGUI.invalidSettingsFile=Settings file is not valid. +RandomizerGUI.settingsLoaded=Settings loaded from %s. +RandomizerGUI.settingsLoadFailed=Settings file load failed. Please try again. +RandomizerGUI.settingsSaveFailed=Settings file save failed. Please try again. +RandomizerGUI.abilitiesPanel.border.title=Pokemon Abilities +RandomizerGUI.paUnchangedRB.text=Unchanged +RandomizerGUI.paUnchangedRB.toolTipText=Don't change Pokemon abilities from the base at all. +RandomizerGUI.wpCatchRateCB.text=Set minimum catch rate? +RandomizerGUI.wpCatchRateCB.toolTipText=If this is selected, every Pokemon in the game with a catch rate below a certain level will have its catch rate increased.
This means that if you throw a Poke Ball at a Pokemon weakened to red, it should have at least a 25-30% chance of catching it.
Legendaries only get half this chance (about 15% at low health), except for legendaries that were already easier to catch like Mew. +RandomizerGUI.paWonderGuardCB.text=Allow Wonder Guard? +RandomizerGUI.paWonderGuardCB.toolTipText=If this is checked, Wonder Guard will be able to be chosen as any Pokemon's ability.
This can lead to some very overpowered/broken Pokemon.
USE WITH CAUTION! +RandomizerGUI.paRandomizeRB.toolTipText=Give each Pokemon new abilities.
Each Pokemon will have a base ability, and a 50% chance of having a 2nd ability different from the first.
(50% of the species will have this ability if it is made).
In Generation 5 games, each Pokemon will also receive a new random "Dream World" ability.
Pokemon such as Shedinja which have Wonder Guard as their ability will keep it to maintain balance. +RandomizerGUI.paRandomizeRB.text=Randomize +RandomizerGUI.mtMovesPanel.border.title=Move Tutor Moves +RandomizerGUI.mtmUnchangedRB.toolTipText=Leave the moves taught by tutors as they are.
If Metronome Only Mode is selected, all Move Tutors are changed to Metronome and this setting has no effect. +RandomizerGUI.mtmUnchangedRB.text=Unchanged +RandomizerGUI.mtmRandomRB.toolTipText=Give each move tutor slot a new move.
Each Move Tutor move will still be unique, and they will not overlap with TM/HM moves.
If Metronome Only Mode is selected, all Move Tutors are changed to Metronome and this setting has no effect. +RandomizerGUI.mtmRandomRB.text=Random +RandomizerGUI.mtCompatPanel.border.title=Move Tutor Compatibility +RandomizerGUI.mtcUnchangedRB.toolTipText=Every Pokemon will be able to learn the same move tutor moves that it could before.
Note that this applies even if you randomize the moves, which could lead to some odd combinations. +RandomizerGUI.mtcUnchangedRB.text=Unchanged +RandomizerGUI.mtcRandomTypeRB.text=Random (prefer same type) +RandomizerGUI.mtcRandomTypeRB.toolTipText=Randomise the Move Tutor moves that each Pokemon can learn.
Each move will have:
A 90% chance of being learnable if the Pokemon has it as (one of) its type(s).
A 50% chance of being learnable if the move is Normal and the Pokemon isn't.
A 25% chance otherwise. +RandomizerGUI.mtcRandomTotalRB.text=Random (completely) +RandomizerGUI.mtcRandomTotalRB.toolTipText=Randomise the Move Tutor moves that each Pokemon can learn.
Each move will have a 50% chance of being learnable regardless of type. +RandomizerGUI.mtNoExistLabel.text=This game does not have any Move Tutors, or they are not randomizable yet. +RandomizerGUI.moveTutorsPanel.border.title=Move Tutors +RandomizerGUI.raceModeCB.text=Race Mode +RandomizerGUI.raceModeCB.toolTipText=Select this to enable certain things which are useful for a speedrun race of the ROM you create.
The ability to save a log file will be disabled, and a check value will be generated.
You can send this value around with the preset file to ensure that everyone has the same ROM to race with. +RandomizerGUI.otherOptionsPanel.border.title=Other Options +RandomizerGUI.tpNoEarlyShedinjaCB.text=No Early Wonder Guard? +RandomizerGUI.tpNoEarlyShedinjaCB.toolTipText=Pokemon such as Shedinja, with the ability "Wonder Guard", are a pain to run into early on when you can't damage them.
Selecting this option will make sure that no trainers are given these Pokemon under level 20.
By the time you are fighting level 20 trainers you can reasonably be expected to have a counter for each type. +RandomizerGUI.randomizeHollowsCB.text=Randomize Hidden Hollows +RandomizerGUI.randomizeHollowsCB.toolTipText=Checking this randomizes the Pokemon you see in Hidden Hollows.
For now, at least, the items remain the same.
Hidden Hollows, and consequently this option, are only available in Black2/White2. +RandomizerGUI.brokenMovesCB.text=No Game-Breaking Moves +RandomizerGUI.brokenMovesCB.toolTipText=Checking this checkbox will stop moves that can break early/late games being available in randomized movesets, TMs or Move Tutors.
In first generation games, Dragon Rage, SonicBoom, Spore, and every OHKO move are banned.
In second generation onwards, only SonicBoom and Dragon Rage are banned (because OHKO moves and sleep are a LOT less broken). +RandomizerGUI.loadQSButton.toolTipText=Clicking this button allows you to load predefined settings from a file.
If the file was created with a game which has less randomization options
than the one you are randomizing now, those options will be set to "Unchanged"/Off.
If you load from a newer game, everything that isn't supported will be ignored. +RandomizerGUI.loadQSButton.text=Load Settings +RandomizerGUI.saveQSButton.text=Save Settings +RandomizerGUI.saveQSButton.toolTipText=Clicking this will allow you to save the current randomization settings as a file.
You can then load these settings when you are randomizing any ROM.
The way in which ROMs with more or less features than the current one
are handled is described when you hover over the "Load Settings" button. +RandomizerGUI.wpARSimilarStrengthRB.text=Similar Strength +RandomizerGUI.wpARSimilarStrengthRB.toolTipText=If this is checked, the random Pokemon that replaces each Pokemon will be of similar power to the original.
However, preserving other rules such as 1-1 maps has precedence over this, so weaker or stronger Pokemon will be chosen if there are no other Pokemon available.
This option is not available alongside Type Themes or Catch-em-All because the Pokemon pool would be too limited in some cases. +RandomizerGUI.pmsMetronomeOnlyRB.text=Metronome Only Mode +RandomizerGUI.pmsMetronomeOnlyRB.toolTipText=Where possible, every Pok\u00e9mon in the entire game will have Metronome as its only move,
with the PP boosted to 40 to make it possible to complete fights without always using Struggle.
Does not currently apply to non-standard battles such as the Battle Tower/Frontier or the PWT. +RandomizerGUI.inGameTradesPanel.border.title=In-Game Trades +RandomizerGUI.fieldItemsPanel.border.title=Field Items +RandomizerGUI.igtUnchangedRB.text=Unchanged +RandomizerGUI.igtUnchangedRB.toolTipText=In-game trades remain the same. +RandomizerGUI.igtGivenOnlyRB.text=Randomize Given Pokemon Only +RandomizerGUI.igtGivenOnlyRB.toolTipText=Selecting this will randomize the Pokemon you receive from each in-game trade,
but the Pokemon requested by the NPC in exchange will remain the same. +RandomizerGUI.igtBothRB.text=Randomize both Requested & Given Pokemon +RandomizerGUI.igtBothRB.toolTipText=Selecting this will replace both the Pokemon you receive from an in-game trade and the Pokemon required to do the trade. +RandomizerGUI.fiUnchangedRB.toolTipText=Items on the ground and hidden items remain the same. +RandomizerGUI.fiUnchangedRB.text=Unchanged +RandomizerGUI.fiShuffleRB.toolTipText=Selecting this will take the full set of items that can be picked up from item balls, as well as hidden items,
and randomize their order so each item appears in a new place.
Key items are left in their original location and glitch items are excluded.
This stops item balls containing seriously overpowered items, but more powerful items may be available early on.
TMs will remain in the same item balls, but the numbers of the TMs in them will be shuffled among the set. +RandomizerGUI.fiShuffleRB.text=Shuffle +RandomizerGUI.fiRandomRB.text=Randomize +RandomizerGUI.fiRandomRB.toolTipText=Selecting this will place a new random item in each item ball & hidden item slot.
Key items & glitch items are automatically excluded.
This means item balls can contain seriously overpowered items such as Master Balls.
TMs will remain in the same item balls, but the numbers of the TMs in them will be randomized, including TMs not usually available from item balls.
All TMs will still be available at least once in the game. +RandomizerGUI.igtRandomNicknameCB.toolTipText=Check this to randomize the nicknames of the Pokemon you receive.
The nicknames will be chosen from a predefined list.
If there are no more usable nicknames the original nicknames will be kept for the rest of the trades. +RandomizerGUI.igtRandomNicknameCB.text=Randomize Nicknames +RandomizerGUI.igtRandomItemCB.text=Randomize Items +RandomizerGUI.igtRandomItemCB.toolTipText=Check this to give each Pokemon you receive from an ingame trade a random held item.
This includes trades that do not normally have a held item on the Pokemon. +RandomizerGUI.igtRandomIVsCB.toolTipText=Check this to randomize the IVs of the Pokemon you receive from ingame trades.
In most games these Pokemon have set IVs, so clicking this randomizes those set IVs. +RandomizerGUI.igtRandomIVsCB.text=Randomize IVs +RandomizerGUI.igtRandomOTCB.text=Randomize OTs +RandomizerGUI.igtRandomOTCB.toolTipText=Check this to randomize the Original Trainer (ID & name) of the Pokemon you receive from trades.
The names will be chosen from the same list as trainer names,
with names that are too long to be OT names excluded. +RandomizerGUI.spHeldItemsCB.toolTipText=Checking this will randomize the items held by the starters where possible.
In Generation 2 games, each starter will get an individual random item.
In Generation 3 games, all the starters will get the same random item. +RandomizerGUI.spHeldItemsCB.text=Randomize Starter Held Items +RandomizerGUI.wpHeldItemsCB.toolTipText=Checking this will randomize the items held by Pokemon in the wild, including whether they can have one at all or not.
In some cases, these item definitions will also apply to static encounters with legendaries and the like. +RandomizerGUI.wpHeldItemsCB.text=Randomize held items? +RandomizerGUI.codeTweaksCB.text= +RandomizerGUI.codeTweaksCB.toolTipText=Select this to enable the use of certain code tweaks that you can select by clicking the button.
If this box isn't checked no tweaks will be applied!
These are only currently available for English versions of Generation 1/2 games. +RandomizerGUI.codeTweaksBtn.text=Code Tweaks +RandomizerGUI.codeTweaksBtn.textWithActive=Code Tweaks (%d) +RandomizerGUI.pokeLimitCB.text= +RandomizerGUI.pokeLimitBtn.text=Limit Pokemon +RandomizerGUI.pokeLimitCB.toolTipText=Select this to allow yourself to limit the Pokemon used by the randomization.
If this box isn't checked all Pokemon will be allowed! +RandomizerGUI.updateSettingsButton.text=Update Settings +RandomizerGUI.toggleAutoUpdatesMenuItem.text=Toggle AutoUpdate +RandomizerGUI.manualUpdateMenuItem.text=Check for Update Now +RandomizerGUI.disableAutoUpdate=Disable Auto Update +RandomizerGUI.enableAutoUpdate=Enable Auto Update +RandomizerGUI.autoUpdateDisabled=Auto update disabled.\nTo get new updates you will need to manually look for them or look on the website. +RandomizerGUI.autoUpdateEnabled=Auto update enabled.\nThe randomizer will check for new updates each time you open it. +RandomizerGUI.goUpdateMovesLegacyCheckBox.text=Legacy? +RandomizerGUI.goUpdateMovesLegacyCheckBox.toolTipText=Instead of updating moves to their Gen 6 stats, update them to their Gen 5 stats instead.
This is intended for people who used the "Update Moves" function in old randomizers but don't like the new changes.
This is available for every game except Gen 5 games where it would be pointless. +RandomizerGUI.pbsStandardEXPCurvesCB.text=Standardize EXP Curves +RandomizerGUI.pbsStandardEXPCurvesCB.toolTipText=When this is selected, every Pokemon's EXP curve will be changed to one of the following:
  • Medium Fast (1,000,000 EXP to Level 100) if it isn't a legendary
  • Slow (1,250,000 EXP to Level 100) if it is.
This will cause Pokemon to be better or worse based more on their stats/moves/type rather than the difficulty of leveling them.
WARNING: This will break trade compatibility with ROMs that don't have this option enabled. Use with caution. +GenerationLimitDialog.includePokemonHeader.text=Include Pokemon from: +GenerationLimitDialog.relatedPokemonHeader.text=... and related Pokemon from: +GenerationLimitDialog.gen1CB.text=Generation 1 +GenerationLimitDialog.gen2CB.text=Generation 2 +GenerationLimitDialog.gen3CB.text=Generation 3 +GenerationLimitDialog.gen4CB.text=Generation 4 +GenerationLimitDialog.gen5CB.text=Generation 5 +GenerationLimitDialog.okButton.text=OK +GenerationLimitDialog.gen2Short=Gen 2 +GenerationLimitDialog.gen4Short=Gen 4 +GenerationLimitDialog.gen1Short=Gen 1 +GenerationLimitDialog.gen3Short=Gen 3 +GenerationLimitDialog.title=Choose Pokemon to allow +GenerationLimitDialog.cancelButton.text=Cancel +GenerationLimitDialog.warningRomHackLabel.text=
WARNING: This functionality will NOT work correctly with ROM hacks
that change the available Pokemon or add new ones!
+AboutDialog.linkLabel.text=http://pokehacks.dabomstew.com/randomizer +AboutDialog.titleLabel.text=Universal Pokemon Randomizer 1.6.1-dev +AboutDialog.title=About +AboutDialog.ackLabel.text=Based on research by various sources; see Acknowledgements +AboutDialog.authorLabel.text=Created by Dabomstew +AboutDialog.okButton.text=OK +CodeTweaksDialog.headerLabel.text=Choose tweaks to enable... +CodeTweaksDialog.bwPatchCB.text=B/W Exp Patch +CodeTweaksDialog.bwPatchCB.toolTipText=Select this to patch the game you are randomizing to use the Black/White new XP gain system.
This system gives EXP in a different way, rewarding players for beating higher level Pokemon with lower levels.
This is currently available only for English R/B/Y and G/S/C. +CodeTweaksDialog.nerfXAccCB.text=Nerf X Accuracy +CodeTweaksDialog.nerfXAccCB.toolTipText=X Accuracy in Generation 1 games is a pretty broken item, given that it gives 100% accuracy to any and all moves used.
Apply this code tweak to stop X Accuracy from working on sleep moves, trapping moves & one-hit KO moves.
Doing this means that the player cannot use X Accuracy and lock the opponents into infinite sleep/Wrap, nor can they just use one-hit KO moves on everything.
Applying this patch will also remove the one-hit KO moves from the "broken moves" list if you use that option, since they aren't really broken without X Accuracy.
Credit to Mountebank for this patch. +CodeTweaksDialog.critRateFixCB.text="Fix" Crit Rate +CodeTweaksDialog.critRateFixCB.toolTipText=Selecting this option will "fix" Generation 1's critical hit rate to be the same as the other games (1/16), instead of being based on Speed.
Focus Energy and Dire Hit will also be fixed to increase crit rate instead of decreasing it.
"High crit rate" moves such as Slash will have double the normal crit chance, like in later games. +CodeTweaksDialog.title=Code Tweaks +CodeTweaksDialog.okButton.text=OK +CodeTweaksDialog.cancelButton.text=Cancel +PresetMakeDialog.doneButton.text=Done +PresetMakeDialog.jLabel5.text=Alternatively you can produce a file which contains this data which you can then send to people. +PresetMakeDialog.jLabel3.text=Random Seed: +PresetMakeDialog.jLabel2.text=Below are the settings you can give to other people to produce the same randomization as you just did. +PresetMakeDialog.jLabel4.text=Config String: +PresetMakeDialog.jLabel1.text=Your game has been successfully randomized! +PresetMakeDialog.title=Randomization Completed - Seed Details +PresetMakeDialog.produceFileButton.text=Produce File +RandomizerGUI.pms4MovesCB.toolTipText=Check this to make sure every Pokemon gets 4 moves at level 1, instead of them keeping their original move count.
This ensures that every Pokemon you catch will have a full moveset.
This is now available for all supported games. +RandomizerGUI.pms4MovesCB.text=Every Pokemon starts with 4 moves? diff --git a/src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.form b/src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.form new file mode 100755 index 000000000..213f6aa34 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.form @@ -0,0 +1,142 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.java b/src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.java new file mode 100755 index 000000000..4bfaaa62e --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/CodeTweaksDialog.java @@ -0,0 +1,180 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +import java.util.ResourceBundle; + +import com.dabomstew.pkrandom.CodeTweaks; + +/** + * + * @author Stewart + */ +public class CodeTweaksDialog extends javax.swing.JDialog { + /** + * + */ + private static final long serialVersionUID = 1771633141425200187L; + private static final ResourceBundle bundle = ResourceBundle + .getBundle("com/dabomstew/pkrandom/gui/Bundle"); + private boolean pressedOk; + + /** + * Creates new form CodeTweaksDialog + */ + public CodeTweaksDialog(RandomizerGUI parent, int current, int available) { + super(parent, true); + initComponents(); + initialState(available); + if (current != 0) { + current &= available; + restoreFrom(current); + } + pressedOk = false; + setLocationRelativeTo(parent); + setVisible(true); + + } + + private void initialState(int available) { + bwPatchCB.setVisible((available & CodeTweaks.BW_EXP_PATCH) > 0); + nerfXAccCB.setVisible((available & CodeTweaks.NERF_X_ACCURACY) > 0); + critRateFixCB.setVisible((available & CodeTweaks.FIX_CRIT_RATE) > 0); + } + + private void restoreFrom(int current) { + bwPatchCB.setSelected((current & CodeTweaks.BW_EXP_PATCH) > 0); + nerfXAccCB.setSelected((current & CodeTweaks.NERF_X_ACCURACY) > 0); + critRateFixCB.setSelected((current & CodeTweaks.FIX_CRIT_RATE) > 0); + } + + public boolean pressedOK() { + return pressedOk; + } + + public int getChoice() { + int choice = 0; + if (bwPatchCB.isSelected()) { + choice |= CodeTweaks.BW_EXP_PATCH; + } + if (nerfXAccCB.isSelected()) { + choice |= CodeTweaks.NERF_X_ACCURACY; + } + if (critRateFixCB.isSelected()) { + choice |= CodeTweaks.FIX_CRIT_RATE; + } + return choice; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + headerLabel = new javax.swing.JLabel(); + bwPatchCB = new javax.swing.JCheckBox(); + nerfXAccCB = new javax.swing.JCheckBox(); + critRateFixCB = new javax.swing.JCheckBox(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("com/dabomstew/pkrandom/gui/Bundle"); // NOI18N + setTitle(bundle.getString("CodeTweaksDialog.title")); // NOI18N + + headerLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + headerLabel.setText(bundle.getString("CodeTweaksDialog.headerLabel.text")); // NOI18N + + bwPatchCB.setText(bundle.getString("CodeTweaksDialog.bwPatchCB.text")); // NOI18N + bwPatchCB.setToolTipText(bundle.getString("CodeTweaksDialog.bwPatchCB.toolTipText")); // NOI18N + + nerfXAccCB.setText(bundle.getString("CodeTweaksDialog.nerfXAccCB.text")); // NOI18N + nerfXAccCB.setToolTipText(bundle.getString("CodeTweaksDialog.nerfXAccCB.toolTipText")); // NOI18N + + critRateFixCB.setText(bundle.getString("CodeTweaksDialog.critRateFixCB.text")); // NOI18N + critRateFixCB.setToolTipText(bundle.getString("CodeTweaksDialog.critRateFixCB.toolTipText")); // NOI18N + + okButton.setText(bundle.getString("CodeTweaksDialog.okButton.text")); // NOI18N + okButton.setMaximumSize(new java.awt.Dimension(65, 23)); + okButton.setMinimumSize(new java.awt.Dimension(65, 23)); + okButton.setPreferredSize(new java.awt.Dimension(65, 23)); + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + cancelButton.setText(bundle.getString("CodeTweaksDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(cancelButton)) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(critRateFixCB) + .addComponent(nerfXAccCB) + .addComponent(bwPatchCB) + .addComponent(headerLabel)) + .addGap(0, 0, Short.MAX_VALUE))) + .addContainerGap()) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(headerLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(bwPatchCB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(nerfXAccCB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(critRateFixCB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 31, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(okButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(cancelButton)) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_okButtonActionPerformed + pressedOk = true; + setVisible(false); + }// GEN-LAST:event_okButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_cancelButtonActionPerformed + pressedOk = false; + setVisible(false); + }// GEN-LAST:event_cancelButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JCheckBox bwPatchCB; + private javax.swing.JButton cancelButton; + private javax.swing.JCheckBox critRateFixCB; + private javax.swing.JLabel headerLabel; + private javax.swing.JCheckBox nerfXAccCB; + private javax.swing.JButton okButton; + // End of variables declaration//GEN-END:variables +} diff --git a/src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.form b/src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.form new file mode 100755 index 000000000..e116fbc99 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.form @@ -0,0 +1,314 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.java b/src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.java new file mode 100755 index 000000000..82bc1779a --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/GenerationLimitDialog.java @@ -0,0 +1,448 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +import com.dabomstew.pkrandom.pokemon.GenRestrictions; + +/** + * + * @author Stewart + */ +public class GenerationLimitDialog extends javax.swing.JDialog { + + /** + * + */ + private static final long serialVersionUID = 106783506965080925L; + private boolean pressedOk; + + /** + * Creates new form GenerationLimitDialog + */ + public GenerationLimitDialog(RandomizerGUI parent, GenRestrictions current, + int generation) { + super(parent, true); + initComponents(); + initialState(generation); + if (current != null) { + current.limitToGen(generation); + restoreFrom(current); + } + enableAndDisableBoxes(); + pressedOk = false; + setLocationRelativeTo(parent); + setVisible(true); + + } + + public boolean pressedOK() { + return pressedOk; + } + + public GenRestrictions getChoice() { + GenRestrictions gr = new GenRestrictions(); + gr.allow_gen1 = this.gen1CB.isSelected(); + gr.allow_gen2 = this.gen2CB.isSelected(); + gr.allow_gen3 = this.gen3CB.isSelected(); + gr.allow_gen4 = this.gen4CB.isSelected(); + gr.allow_gen5 = this.gen5CB.isSelected(); + + gr.assoc_g1_g2 = this.g1Rg2CB.isSelected(); + gr.assoc_g1_g4 = this.g1Rg4CB.isSelected(); + + gr.assoc_g2_g1 = this.g2Rg1CB.isSelected(); + gr.assoc_g2_g3 = this.g2Rg3CB.isSelected(); + gr.assoc_g2_g4 = this.g2Rg4CB.isSelected(); + + gr.assoc_g3_g2 = this.g3Rg2CB.isSelected(); + gr.assoc_g3_g4 = this.g3Rg4CB.isSelected(); + + gr.assoc_g4_g1 = this.g4Rg1CB.isSelected(); + gr.assoc_g4_g2 = this.g4Rg2CB.isSelected(); + gr.assoc_g4_g3 = this.g4Rg3CB.isSelected(); + + return gr; + } + + private void initialState(int generation) { + if (generation < 2) { + gen2CB.setVisible(false); + g1Rg2CB.setVisible(false); + g2Rg1CB.setVisible(false); + g2Rg3CB.setVisible(false); + g2Rg4CB.setVisible(false); + } + if (generation < 3) { + gen3CB.setVisible(false); + g2Rg3CB.setVisible(false); + g3Rg2CB.setVisible(false); + g3Rg4CB.setVisible(false); + } + if (generation < 4) { + gen4CB.setVisible(false); + g1Rg4CB.setVisible(false); + g2Rg4CB.setVisible(false); + g3Rg4CB.setVisible(false); + g4Rg1CB.setVisible(false); + g4Rg2CB.setVisible(false); + g4Rg3CB.setVisible(false); + } + if (generation < 5) { + gen5CB.setVisible(false); + } + } + + private void restoreFrom(GenRestrictions restrict) { + gen1CB.setSelected(restrict.allow_gen1); + gen2CB.setSelected(restrict.allow_gen2); + gen3CB.setSelected(restrict.allow_gen3); + gen4CB.setSelected(restrict.allow_gen4); + gen5CB.setSelected(restrict.allow_gen5); + + g1Rg2CB.setSelected(restrict.assoc_g1_g2); + g1Rg4CB.setSelected(restrict.assoc_g1_g4); + + g2Rg1CB.setSelected(restrict.assoc_g2_g1); + g2Rg3CB.setSelected(restrict.assoc_g2_g3); + g2Rg4CB.setSelected(restrict.assoc_g2_g4); + + g3Rg2CB.setSelected(restrict.assoc_g3_g2); + g3Rg4CB.setSelected(restrict.assoc_g3_g4); + + g4Rg1CB.setSelected(restrict.assoc_g4_g1); + g4Rg2CB.setSelected(restrict.assoc_g4_g2); + g4Rg3CB.setSelected(restrict.assoc_g4_g3); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + includePokemonHeader = new javax.swing.JLabel(); + gen1CB = new javax.swing.JCheckBox(); + gen2CB = new javax.swing.JCheckBox(); + gen3CB = new javax.swing.JCheckBox(); + gen4CB = new javax.swing.JCheckBox(); + gen5CB = new javax.swing.JCheckBox(); + relatedPokemonHeader = new javax.swing.JLabel(); + g1Rg2CB = new javax.swing.JCheckBox(); + g1Rg4CB = new javax.swing.JCheckBox(); + g2Rg1CB = new javax.swing.JCheckBox(); + g2Rg3CB = new javax.swing.JCheckBox(); + g2Rg4CB = new javax.swing.JCheckBox(); + g3Rg2CB = new javax.swing.JCheckBox(); + g3Rg4CB = new javax.swing.JCheckBox(); + g4Rg1CB = new javax.swing.JCheckBox(); + g4Rg2CB = new javax.swing.JCheckBox(); + g4Rg3CB = new javax.swing.JCheckBox(); + okButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + warningRomHackLabel = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("com/dabomstew/pkrandom/gui/Bundle"); // NOI18N + setTitle(bundle.getString("GenerationLimitDialog.title")); // NOI18N + + includePokemonHeader.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + includePokemonHeader.setText(bundle.getString("GenerationLimitDialog.includePokemonHeader.text")); // NOI18N + + gen1CB.setText(bundle.getString("GenerationLimitDialog.gen1CB.text")); // NOI18N + gen1CB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gen1CBActionPerformed(evt); + } + }); + + gen2CB.setText(bundle.getString("GenerationLimitDialog.gen2CB.text")); // NOI18N + gen2CB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gen2CBActionPerformed(evt); + } + }); + + gen3CB.setText(bundle.getString("GenerationLimitDialog.gen3CB.text")); // NOI18N + gen3CB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gen3CBActionPerformed(evt); + } + }); + + gen4CB.setText(bundle.getString("GenerationLimitDialog.gen4CB.text")); // NOI18N + gen4CB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gen4CBActionPerformed(evt); + } + }); + + gen5CB.setText(bundle.getString("GenerationLimitDialog.gen5CB.text")); // NOI18N + gen5CB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + gen5CBActionPerformed(evt); + } + }); + + relatedPokemonHeader.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + relatedPokemonHeader.setText(bundle.getString("GenerationLimitDialog.relatedPokemonHeader.text")); // NOI18N + + g1Rg2CB.setText(bundle.getString("GenerationLimitDialog.gen2Short")); // NOI18N + + g1Rg4CB.setText(bundle.getString("GenerationLimitDialog.gen4Short")); // NOI18N + + g2Rg1CB.setText(bundle.getString("GenerationLimitDialog.gen1Short")); // NOI18N + + g2Rg3CB.setText(bundle.getString("GenerationLimitDialog.gen3Short")); // NOI18N + + g2Rg4CB.setText(bundle.getString("GenerationLimitDialog.gen4Short")); // NOI18N + + g3Rg2CB.setText(bundle.getString("GenerationLimitDialog.gen2Short")); // NOI18N + + g3Rg4CB.setText(bundle.getString("GenerationLimitDialog.gen4Short")); // NOI18N + + g4Rg1CB.setText(bundle.getString("GenerationLimitDialog.gen1Short")); // NOI18N + + g4Rg2CB.setText(bundle.getString("GenerationLimitDialog.gen2Short")); // NOI18N + + g4Rg3CB.setText(bundle.getString("GenerationLimitDialog.gen3Short")); // NOI18N + + okButton.setText(bundle.getString("GenerationLimitDialog.okButton.text")); // NOI18N + okButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + okButtonActionPerformed(evt); + } + }); + + cancelButton.setText(bundle.getString("GenerationLimitDialog.cancelButton.text")); // NOI18N + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + warningRomHackLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + warningRomHackLabel.setForeground(new java.awt.Color(255, 0, 0)); + warningRomHackLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + warningRomHackLabel.setText(bundle.getString("GenerationLimitDialog.warningRomHackLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(gen5CB) + .addGroup(layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(includePokemonHeader) + .addComponent(gen1CB) + .addComponent(gen2CB) + .addComponent(gen3CB) + .addComponent(gen4CB)) + .addGap(106, 106, 106) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(g4Rg1CB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(g4Rg2CB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(g4Rg3CB)) + .addGroup(layout.createSequentialGroup() + .addComponent(g3Rg2CB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(g3Rg4CB)) + .addGroup(layout.createSequentialGroup() + .addComponent(g2Rg1CB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(g2Rg3CB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(g2Rg4CB)) + .addGroup(layout.createSequentialGroup() + .addComponent(g1Rg2CB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(g1Rg4CB)) + .addComponent(relatedPokemonHeader)))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addGap(0, 0, Short.MAX_VALUE) + .addComponent(okButton) + .addGap(32, 32, 32) + .addComponent(cancelButton) + .addGap(134, 134, 134)))) + .addGroup(layout.createSequentialGroup() + .addGap(20, 20, 20) + .addComponent(warningRomHackLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(includePokemonHeader) + .addComponent(relatedPokemonHeader)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(gen1CB) + .addComponent(g1Rg2CB) + .addComponent(g1Rg4CB)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(gen2CB) + .addComponent(g2Rg1CB) + .addComponent(g2Rg3CB) + .addComponent(g2Rg4CB)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(gen3CB) + .addComponent(g3Rg2CB) + .addComponent(g3Rg4CB)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(gen4CB) + .addComponent(g4Rg1CB) + .addComponent(g4Rg2CB) + .addComponent(g4Rg3CB)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(gen5CB) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(okButton) + .addComponent(cancelButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(warningRomHackLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap()) + ); + + pack(); + }// //GEN-END:initComponents + + private void enableAndDisableBoxes() { + + // enable sub-boxes of checked main boxes + g1Rg2CB.setEnabled(gen1CB.isSelected()); + g1Rg4CB.setEnabled(gen1CB.isSelected()); + g2Rg1CB.setEnabled(gen2CB.isSelected()); + g2Rg3CB.setEnabled(gen2CB.isSelected()); + g2Rg4CB.setEnabled(gen2CB.isSelected()); + g3Rg2CB.setEnabled(gen3CB.isSelected()); + g3Rg4CB.setEnabled(gen3CB.isSelected()); + g4Rg1CB.setEnabled(gen4CB.isSelected()); + g4Rg2CB.setEnabled(gen4CB.isSelected()); + g4Rg3CB.setEnabled(gen4CB.isSelected()); + + // uncheck disabled subboxes + if (!gen1CB.isSelected()) { + g1Rg2CB.setSelected(false); + g1Rg4CB.setSelected(false); + } + if (!gen2CB.isSelected()) { + g2Rg1CB.setSelected(false); + g2Rg3CB.setSelected(false); + g2Rg4CB.setSelected(false); + } + if (!gen3CB.isSelected()) { + g3Rg2CB.setSelected(false); + g3Rg4CB.setSelected(false); + } + if (!gen4CB.isSelected()) { + g4Rg1CB.setSelected(false); + g4Rg2CB.setSelected(false); + g4Rg3CB.setSelected(false); + } + + // check and disable implied boxes + if (gen1CB.isSelected()) { + g2Rg1CB.setEnabled(false); + g2Rg1CB.setSelected(true); + g4Rg1CB.setEnabled(false); + g4Rg1CB.setSelected(true); + } + + if (gen2CB.isSelected()) { + g1Rg2CB.setEnabled(false); + g1Rg2CB.setSelected(true); + g3Rg2CB.setEnabled(false); + g3Rg2CB.setSelected(true); + g4Rg2CB.setEnabled(false); + g4Rg2CB.setSelected(true); + } + + if (gen3CB.isSelected()) { + g2Rg3CB.setEnabled(false); + g2Rg3CB.setSelected(true); + g4Rg3CB.setEnabled(false); + g4Rg3CB.setSelected(true); + } + + if (gen4CB.isSelected()) { + g1Rg4CB.setEnabled(false); + g1Rg4CB.setSelected(true); + g2Rg4CB.setEnabled(false); + g2Rg4CB.setSelected(true); + g3Rg4CB.setEnabled(false); + g3Rg4CB.setSelected(true); + } + } + + private void gen1CBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_gen1CBActionPerformed + enableAndDisableBoxes(); + }// GEN-LAST:event_gen1CBActionPerformed + + private void gen2CBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_gen2CBActionPerformed + enableAndDisableBoxes(); + }// GEN-LAST:event_gen2CBActionPerformed + + private void gen3CBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_gen3CBActionPerformed + enableAndDisableBoxes(); + }// GEN-LAST:event_gen3CBActionPerformed + + private void gen4CBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_gen4CBActionPerformed + enableAndDisableBoxes(); + }// GEN-LAST:event_gen4CBActionPerformed + + private void gen5CBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_gen5CBActionPerformed + enableAndDisableBoxes(); + }// GEN-LAST:event_gen5CBActionPerformed + + private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_okButtonActionPerformed + pressedOk=true; + setVisible(false); + }// GEN-LAST:event_okButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_cancelButtonActionPerformed + pressedOk=false; + setVisible(false); + }// GEN-LAST:event_cancelButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton cancelButton; + private javax.swing.JCheckBox g1Rg2CB; + private javax.swing.JCheckBox g1Rg4CB; + private javax.swing.JCheckBox g2Rg1CB; + private javax.swing.JCheckBox g2Rg3CB; + private javax.swing.JCheckBox g2Rg4CB; + private javax.swing.JCheckBox g3Rg2CB; + private javax.swing.JCheckBox g3Rg4CB; + private javax.swing.JCheckBox g4Rg1CB; + private javax.swing.JCheckBox g4Rg2CB; + private javax.swing.JCheckBox g4Rg3CB; + private javax.swing.JCheckBox gen1CB; + private javax.swing.JCheckBox gen2CB; + private javax.swing.JCheckBox gen3CB; + private javax.swing.JCheckBox gen4CB; + private javax.swing.JCheckBox gen5CB; + private javax.swing.JLabel includePokemonHeader; + private javax.swing.JButton okButton; + private javax.swing.JLabel relatedPokemonHeader; + private javax.swing.JLabel warningRomHackLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/src/com/dabomstew/pkrandom/gui/InvalidSupplementFilesException.java b/src/com/dabomstew/pkrandom/gui/InvalidSupplementFilesException.java new file mode 100755 index 000000000..4e792678a --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/InvalidSupplementFilesException.java @@ -0,0 +1,36 @@ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- InvalidSupplementFilesException.java - thrown when the trainer class --*/ +/*-- or trainer name files found are--*/ +/*-- different from those of the --*/ +/*-- preset creator. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class InvalidSupplementFilesException extends Exception { + + /** + * + */ + private static final long serialVersionUID = -3778498838677886358L; + +} diff --git a/src/com/dabomstew/pkrandom/gui/OperationDialog.form b/src/com/dabomstew/pkrandom/gui/OperationDialog.form new file mode 100755 index 000000000..e4166a967 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/OperationDialog.form @@ -0,0 +1,79 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/dabomstew/pkrandom/gui/OperationDialog.java b/src/com/dabomstew/pkrandom/gui/OperationDialog.java new file mode 100755 index 000000000..cae3427dc --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/OperationDialog.java @@ -0,0 +1,125 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +import java.awt.Dialog; +import java.awt.Frame; +import java.awt.Image; +import java.awt.Toolkit; +import java.io.IOException; +import java.io.InputStream; + +import javax.swing.ImageIcon; + +/** + * + * @author Stewart + */ +public class OperationDialog extends javax.swing.JDialog { + + /** + * + */ + private static final long serialVersionUID = 5965463550336235236L; + + /** + * Creates new form OperationDialog + */ + public OperationDialog(String text, Frame parent, boolean modal) { + super(parent, modal); + initComponents(); + this.loadingLabel.setText(text); + setLocationRelativeTo(parent); + } + + public OperationDialog(String text, Dialog parent, boolean modal) { + super(parent, modal); + initComponents(); + this.loadingLabel.setText(text); + setLocationRelativeTo(parent); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + jPanel1 = new javax.swing.JPanel(); + loadingLabel = new javax.swing.JLabel(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE); + setResizable(false); + setUndecorated(true); + + jPanel1.setBorder(new javax.swing.border.LineBorder(new java.awt.Color( + 0, 0, 0), 2, true)); + + loadingLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + loadingLabel.setIcon(getLoadingIcon()); + loadingLabel.setText("Loading..."); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout( + jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup(jPanel1Layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING).addGroup( + jPanel1Layout.createSequentialGroup().addContainerGap() + .addComponent(loadingLabel) + .addContainerGap(53, Short.MAX_VALUE))); + jPanel1Layout.setVerticalGroup(jPanel1Layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING).addGroup( + jPanel1Layout + .createSequentialGroup() + .addContainerGap() + .addComponent(loadingLabel) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout( + getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup(layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING).addComponent( + jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)); + layout.setVerticalGroup(layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING).addComponent( + jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE)); + + pack(); + }// //GEN-END:initComponents + + private ImageIcon getLoadingIcon() { + try { + InputStream in = OperationDialog.class + .getResourceAsStream("/com/dabomstew/pkrandom/gui/loading.gif"); + byte[] buf = new byte[in.available()]; + in.read(buf); + in.close(); + Image image = Toolkit.getDefaultToolkit().createImage(buf); + return new ImageIcon(image); + } catch (IOException ex) { + return null; + } + } + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel jPanel1; + private javax.swing.JLabel loadingLabel; + + // End of variables declaration//GEN-END:variables + + public static void main(String[] args) { + // testing + new OperationDialog("Testing...", (Frame)null, true); + } +} diff --git a/src/com/dabomstew/pkrandom/gui/PresetFileFilter.java b/src/com/dabomstew/pkrandom/gui/PresetFileFilter.java new file mode 100755 index 000000000..fc6dae5d5 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/PresetFileFilter.java @@ -0,0 +1,57 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- PresetFileFilter.java - a file filter for the "randomization presets" --*/ +/*-- which allow the same random ROM to be produced--*/ +/*-- on demand. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +public class PresetFileFilter extends FileFilter { + + @Override + public boolean accept(File arg0) { + if (arg0.isDirectory()) { + return true; // needed to allow directory navigation + } + String filename = arg0.getName(); + if (filename.contains(".") == false) { + return false; + } + String extension = arg0.getName().substring( + arg0.getName().lastIndexOf('.') + 1); + return extension.toLowerCase().equals("rndp"); + } + + @Override + public String getDescription() { + return "Pokemon Randomizer Preset (*.rndp)"; + } + +} diff --git a/src/com/dabomstew/pkrandom/gui/PresetLoadDialog.form b/src/com/dabomstew/pkrandom/gui/PresetLoadDialog.form new file mode 100755 index 000000000..008d3c586 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/PresetLoadDialog.form @@ -0,0 +1,197 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/dabomstew/pkrandom/gui/PresetLoadDialog.java b/src/com/dabomstew/pkrandom/gui/PresetLoadDialog.java new file mode 100755 index 000000000..8ca2b1ba3 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/PresetLoadDialog.java @@ -0,0 +1,564 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- PresetLoadDialog.java - a dialog to allow use of preset files or --*/ +/*-- random seed/config string pairs to produce --*/ +/*-- premade ROMs. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; + +import javax.swing.JDialog; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import com.dabomstew.pkrandom.romhandlers.RomHandler; + +/** + * + * @author Stewart + */ +public class PresetLoadDialog extends javax.swing.JDialog { + + /** + * + */ + private static final long serialVersionUID = -7898067118947765260L; + private RandomizerGUI parentGUI; + private RomHandler currentROM; + private boolean completed = false; + private String requiredName = null; + private volatile boolean changeFieldsWithoutCheck = false; + private byte[] trainerClasses = null, trainerNames = null, nicknames = null; + + /** + * Creates new form PresetLoadDialog + */ + public PresetLoadDialog(RandomizerGUI parent) { + super(parent, true); + initComponents(); + this.parentGUI = parent; + this.presetFileChooser.setCurrentDirectory(new File("./")); + this.romFileChooser.setCurrentDirectory(new File("./")); + initialState(); + setLocationRelativeTo(parent); + setVisible(true); + } + + private void initialState() { + this.romFileButton.setEnabled(false); + this.acceptButton.setEnabled(false); + addChangeListener(this.randomSeedField); + addChangeListener(this.configStringField); + } + + private void addChangeListener(JTextField field) { + field.getDocument().addDocumentListener(new DocumentListener() { + + @Override + public void insertUpdate(DocumentEvent e) { + if (!changeFieldsWithoutCheck) + PresetLoadDialog.this.checkValues(); + + } + + @Override + public void removeUpdate(DocumentEvent e) { + if (!changeFieldsWithoutCheck) + PresetLoadDialog.this.checkValues(); + + } + + @Override + public void changedUpdate(DocumentEvent e) { + if (!changeFieldsWithoutCheck) + PresetLoadDialog.this.checkValues(); + + } + }); + + } + + protected boolean checkValues() { + String name; + try { + Long.parseLong(this.randomSeedField.getText()); + } catch (NumberFormatException ex) { + invalidValues(); + return false; + } + try { + name = this.parentGUI.getValidRequiredROMName( + this.configStringField.getText(), trainerClasses, + trainerNames, nicknames); + } catch (InvalidSupplementFilesException ex) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + changeFieldsWithoutCheck = true; + configStringField.setText(""); + randomSeedField.setText(""); + changeFieldsWithoutCheck = false; + } + }); + invalidValues(); + return false; + } catch (Exception ex) { + // other exception + ex.printStackTrace(); + invalidValues(); + return false; + } + if (name == null) { + invalidValues(); + return false; + } + requiredName = name; + this.romRequiredLabel.setText("ROM Required: " + name); + this.romFileButton.setEnabled(true); + + if (currentROM != null && currentROM.getROMName().equals(name) == false) { + this.currentROM = null; + this.acceptButton.setEnabled(false); + this.romFileField.setText(""); + } + return true; + } + + private void invalidValues() { + this.currentROM = null; + this.romFileField.setText(""); + this.romRequiredLabel + .setText("ROM Required: Enter settings above first."); + this.romFileButton.setEnabled(false); + this.acceptButton.setEnabled(false); + this.requiredName = null; + + } + + public boolean isCompleted() { + return completed; + } + + public RomHandler getROM() { + return currentROM; + } + + public long getSeed() { + return Long.parseLong(this.randomSeedField.getText()); + } + + public String getConfigString() { + return this.configStringField.getText(); + } + + public byte[] getTrainerClasses() { + return trainerClasses; + } + + public byte[] getTrainerNames() { + return trainerNames; + } + + public byte[] getNicknames() { + return nicknames; + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + private void initComponents() { + + presetFileChooser = new javax.swing.JFileChooser(); + romFileChooser = new javax.swing.JFileChooser(); + presetFileLabel = new javax.swing.JLabel(); + presetFileField = new javax.swing.JTextField(); + presetFileButton = new javax.swing.JButton(); + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + randomSeedField = new javax.swing.JTextField(); + jLabel3 = new javax.swing.JLabel(); + configStringField = new javax.swing.JTextField(); + romRequiredLabel = new javax.swing.JLabel(); + jLabel5 = new javax.swing.JLabel(); + romFileField = new javax.swing.JTextField(); + romFileButton = new javax.swing.JButton(); + acceptButton = new javax.swing.JButton(); + cancelButton = new javax.swing.JButton(); + + presetFileChooser.setFileFilter(new PresetFileFilter()); + + romFileChooser.setFileFilter(new ROMFilter()); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setTitle("Use Premade Seed"); + setModal(true); + setResizable(false); + + presetFileLabel.setText("Seed File:"); + + presetFileField.setEditable(false); + + presetFileButton.setText("..."); + presetFileButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + presetFileButtonActionPerformed(evt); + } + }); + + jLabel1.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + jLabel1.setText("-OR-"); + + jLabel2.setText("Random Seed:"); + + jLabel3.setText("Config String:"); + + romRequiredLabel + .setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + romRequiredLabel.setText("ROM Required: Enter settings above first."); + + jLabel5.setText("Rom File:"); + + romFileField.setEditable(false); + + romFileButton.setText("..."); + romFileButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + romFileButtonActionPerformed(evt); + } + }); + + acceptButton.setText("Apply Randomization Settings"); + acceptButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + acceptButtonActionPerformed(evt); + } + }); + + cancelButton.setText("Cancel"); + cancelButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cancelButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout( + getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup(layout + .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.TRAILING, + false) + .addComponent( + jLabel5, + javax.swing.GroupLayout.Alignment.LEADING, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + presetFileLabel, + javax.swing.GroupLayout.Alignment.LEADING, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + jLabel2, + javax.swing.GroupLayout.Alignment.LEADING, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + jLabel3, + javax.swing.GroupLayout.Alignment.LEADING, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addGap(18, 18, 18) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createSequentialGroup() + .addComponent( + acceptButton) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + 169, + Short.MAX_VALUE) + .addComponent( + cancelButton)) + .addComponent(randomSeedField) + .addComponent(configStringField) + .addComponent(presetFileField) + .addComponent(romFileField)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING, + false) + .addComponent( + presetFileButton, + javax.swing.GroupLayout.PREFERRED_SIZE, + 26, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent( + romFileButton, + javax.swing.GroupLayout.PREFERRED_SIZE, + 1, Short.MAX_VALUE)) + .addGap(12, 12, 12)) + .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(romRequiredLabel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)); + layout.setVerticalGroup(layout + .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(presetFileLabel) + .addComponent( + presetFileField, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(presetFileButton)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel1) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel2) + .addComponent( + randomSeedField, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent( + configStringField, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(romRequiredLabel) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel5) + .addComponent( + romFileField, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(romFileButton)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + 23, Short.MAX_VALUE) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(acceptButton) + .addComponent(cancelButton)) + .addContainerGap())); + + pack(); + }// //GEN-END:initComponents + + private void presetFileButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_presetFileButtonActionPerformed + presetFileChooser.setSelectedFile(null); + int returnVal = presetFileChooser.showOpenDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File fh = presetFileChooser.getSelectedFile(); + try { + DataInputStream dis = new DataInputStream(new FileInputStream( + fh)); + int checkByte = dis.readByte() & 0xFF; + if (checkByte != RandomizerGUI.PRESET_FILE_VERSION) { + dis.close(); + JOptionPane + .showMessageDialog(this, + "This seed file is not for this version of the randomizer."); + return; + } + long seed = dis.readLong(); + String preset = dis.readUTF(); + int tclen = dis.readInt(); + trainerClasses = new byte[tclen]; + dis.read(trainerClasses); + int tnlen = dis.readInt(); + trainerNames = new byte[tnlen]; + dis.read(trainerNames); + int nnlen = dis.readInt(); + nicknames = new byte[nnlen]; + dis.read(nicknames); + changeFieldsWithoutCheck = true; + this.randomSeedField.setText(Long.toString(seed)); + this.configStringField.setText(preset); + changeFieldsWithoutCheck = false; + if (checkValues()) { + this.randomSeedField.setEnabled(false); + this.configStringField.setEnabled(false); + this.presetFileField.setText(fh.getAbsolutePath()); + } else { + this.randomSeedField.setText(""); + this.configStringField.setText(""); + this.randomSeedField.setEnabled(true); + this.configStringField.setEnabled(true); + this.presetFileField.setText(""); + trainerClasses = null; + trainerNames = null; + JOptionPane.showMessageDialog(this, + "The seed file did not contain valid settings."); + } + dis.close(); + } catch (IOException ex) { + JOptionPane.showMessageDialog(this, + "Could not load seed file."); + } + } + }// GEN-LAST:event_presetFileButtonActionPerformed + + private void romFileButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_romFileButtonActionPerformed + romFileChooser.setSelectedFile(null); + int returnVal = romFileChooser.showOpenDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + final File fh = romFileChooser.getSelectedFile(); + for (RomHandler rh : parentGUI.checkHandlers) { + if (rh.detectRom(fh.getAbsolutePath())) { + final RomHandler checkHandler = rh; + final JDialog opDialog = new OperationDialog("Loading...", + this, true); + Thread t = new Thread() { + @Override + public void run() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + opDialog.setVisible(true); + } + }); + try { + checkHandler.loadRom(fh.getAbsolutePath()); + } catch (Exception ex) { + JOptionPane.showMessageDialog( + PresetLoadDialog.this, + "ROM load failed."); + } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + opDialog.setVisible(false); + if (checkHandler.getROMName().equals( + requiredName)) { + // Got it + romFileField.setText(fh + .getAbsolutePath()); + currentROM = checkHandler; + acceptButton.setEnabled(true); + return; + } else { + JOptionPane.showMessageDialog( + PresetLoadDialog.this, + "This isn't the required ROM.\nRequired: " + + requiredName + + "\nThis ROM: " + + checkHandler + .getROMName()); + return; + } + } + }); + } + }; + t.start(); + return; + } + } + JOptionPane.showMessageDialog(this, + "The file you specified isn't a valid Pokemon ROM."); + } + }// GEN-LAST:event_romFileButtonActionPerformed + + private void acceptButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_acceptButtonActionPerformed + completed = true; + this.setVisible(false); + }// GEN-LAST:event_acceptButtonActionPerformed + + private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_cancelButtonActionPerformed + completed = false; + this.setVisible(false); + }// GEN-LAST:event_cancelButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton acceptButton; + private javax.swing.JButton cancelButton; + private javax.swing.JTextField configStringField; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel5; + private javax.swing.JButton presetFileButton; + private javax.swing.JFileChooser presetFileChooser; + private javax.swing.JTextField presetFileField; + private javax.swing.JLabel presetFileLabel; + private javax.swing.JTextField randomSeedField; + private javax.swing.JButton romFileButton; + private javax.swing.JFileChooser romFileChooser; + private javax.swing.JTextField romFileField; + private javax.swing.JLabel romRequiredLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/src/com/dabomstew/pkrandom/gui/PresetMakeDialog.form b/src/com/dabomstew/pkrandom/gui/PresetMakeDialog.form new file mode 100755 index 000000000..0277fb5f2 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/PresetMakeDialog.form @@ -0,0 +1,162 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/dabomstew/pkrandom/gui/PresetMakeDialog.java b/src/com/dabomstew/pkrandom/gui/PresetMakeDialog.java new file mode 100755 index 000000000..4a367cf33 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/PresetMakeDialog.java @@ -0,0 +1,239 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- PresetMakeDialog.java - a dialog to allow preset pairs to either be --*/ +/*-- copied down or saved to a binary file for --*/ +/*-- later use. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; + +import com.dabomstew.pkrandom.FileFunctions; + +/** + * + * @author Stewart + */ +public class PresetMakeDialog extends javax.swing.JDialog { + + /** + * + */ + private static final long serialVersionUID = 7663903108783731673L; + private long seed; + private String configString; + + /** + * Creates new form PresetMakeDialog + */ + public PresetMakeDialog(java.awt.Frame parent, long seed, + String configString) { + super(parent, true); + initComponents(); + randomSeedField.setText(Long.toString(seed)); + configStringField.setText(configString); + this.seed = seed; + this.configString = configString; + presetFileChooser.setCurrentDirectory(new File("./")); + setLocationRelativeTo(parent); + setVisible(true); + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + presetFileChooser = new javax.swing.JFileChooser(); + jLabel1 = new javax.swing.JLabel(); + jLabel2 = new javax.swing.JLabel(); + jLabel3 = new javax.swing.JLabel(); + randomSeedField = new javax.swing.JTextField(); + jLabel4 = new javax.swing.JLabel(); + configStringField = new javax.swing.JTextField(); + jLabel5 = new javax.swing.JLabel(); + produceFileButton = new javax.swing.JButton(); + doneButton = new javax.swing.JButton(); + + presetFileChooser.setFileFilter(new PresetFileFilter()); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("com/dabomstew/pkrandom/gui/Bundle"); // NOI18N + setTitle(bundle.getString("PresetMakeDialog.title")); // NOI18N + setModal(true); + setResizable(false); + + jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + jLabel1.setText(bundle.getString("PresetMakeDialog.jLabel1.text")); // NOI18N + + jLabel2.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + jLabel2.setText(bundle.getString("PresetMakeDialog.jLabel2.text")); // NOI18N + + jLabel3.setText(bundle.getString("PresetMakeDialog.jLabel3.text")); // NOI18N + + randomSeedField.setEditable(false); + + jLabel4.setText(bundle.getString("PresetMakeDialog.jLabel4.text")); // NOI18N + + configStringField.setEditable(false); + + jLabel5.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + jLabel5.setText(bundle.getString("PresetMakeDialog.jLabel5.text")); // NOI18N + + produceFileButton.setText(bundle.getString("PresetMakeDialog.produceFileButton.text")); // NOI18N + produceFileButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + produceFileButtonActionPerformed(evt); + } + }); + + doneButton.setText(bundle.getString("PresetMakeDialog.doneButton.text")); // NOI18N + doneButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + doneButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jLabel2, javax.swing.GroupLayout.DEFAULT_SIZE, 599, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jLabel3) + .addComponent(jLabel4)) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(randomSeedField) + .addComponent(configStringField)) + .addContainerGap()) + .addComponent(jLabel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(layout.createSequentialGroup() + .addGap(67, 67, 67) + .addComponent(produceFileButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(doneButton) + .addGap(66, 66, 66)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(jLabel1) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jLabel2) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel3) + .addComponent(randomSeedField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(jLabel4) + .addComponent(configStringField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(jLabel5) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(produceFileButton) + .addComponent(doneButton)) + .addGap(0, 11, Short.MAX_VALUE)) + ); + + pack(); + }// //GEN-END:initComponents + + private void produceFileButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_produceFileButtonActionPerformed + presetFileChooser.setSelectedFile(null); + int returnVal = presetFileChooser.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File fh = presetFileChooser.getSelectedFile(); + // Fix extension? + fh = FileFunctions.fixFilename(fh, "rndp"); + try { + DataOutputStream dos = new DataOutputStream( + new FileOutputStream(fh)); + dos.writeByte((byte)RandomizerGUI.PRESET_FILE_VERSION); + dos.writeLong(seed); + dos.writeUTF(configString); + byte[] trainerclasses = readFile(FileFunctions + .openConfig("trainerclasses.txt")); + dos.writeInt(trainerclasses.length); + dos.write(trainerclasses); + byte[] trainernames = readFile(FileFunctions + .openConfig("trainernames.txt")); + dos.writeInt(trainernames.length); + dos.write(trainernames); + byte[] nicknames = readFile(FileFunctions + .openConfig("nicknames.txt")); + dos.writeInt(nicknames.length); + dos.write(nicknames); + dos.close(); + JOptionPane.showMessageDialog(this, "Preset file saved to\n" + + fh.getAbsolutePath()); + } catch (IOException ex) { + JOptionPane.showMessageDialog(this, + "Could not save the preset file."); + } + } + }// GEN-LAST:event_produceFileButtonActionPerformed + + protected static byte[] readFile(InputStream is) throws IOException { + byte[] file = new byte[is.available()]; + is.read(file); + is.close(); + return file; + } + + private void doneButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_doneButtonActionPerformed + this.setVisible(false); + }// GEN-LAST:event_doneButtonActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTextField configStringField; + private javax.swing.JButton doneButton; + private javax.swing.JLabel jLabel1; + private javax.swing.JLabel jLabel2; + private javax.swing.JLabel jLabel3; + private javax.swing.JLabel jLabel4; + private javax.swing.JLabel jLabel5; + private javax.swing.JFileChooser presetFileChooser; + private javax.swing.JButton produceFileButton; + private javax.swing.JTextField randomSeedField; + // End of variables declaration//GEN-END:variables +} diff --git a/src/com/dabomstew/pkrandom/gui/QSFileFilter.java b/src/com/dabomstew/pkrandom/gui/QSFileFilter.java new file mode 100755 index 000000000..f578de71f --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/QSFileFilter.java @@ -0,0 +1,57 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- PresetFileFilter.java - a file filter for the "randomization presets" --*/ +/*-- which allow the same random ROM to be produced--*/ +/*-- on demand. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +public class QSFileFilter extends FileFilter { + + @Override + public boolean accept(File arg0) { + if (arg0.isDirectory()) { + return true; // needed to allow directory navigation + } + String filename = arg0.getName(); + if (filename.contains(".") == false) { + return false; + } + String extension = arg0.getName().substring( + arg0.getName().lastIndexOf('.') + 1); + return extension.toLowerCase().equals("rnqs"); + } + + @Override + public String getDescription() { + return "Randomization Quick Settings (*.rnqs)"; + } + +} diff --git a/src/com/dabomstew/pkrandom/gui/ROMFilter.java b/src/com/dabomstew/pkrandom/gui/ROMFilter.java new file mode 100755 index 000000000..cbfe411e6 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/ROMFilter.java @@ -0,0 +1,58 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- ROMFilter.java - a file filter for the various games which can be --*/ +/*-- randomized with this program. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +public class ROMFilter extends FileFilter { + + @Override + public boolean accept(File arg0) { + if (arg0.isDirectory()) { + return true; // needed to allow directory navigation + } + String filename = arg0.getName(); + if (filename.contains(".") == false) { + return false; + } + String extension = arg0.getName() + .substring(arg0.getName().lastIndexOf('.') + 1).toLowerCase(); + return extension.equals("gb") || extension.equals("sgb") + || extension.equals("gbc") || extension.equals("gba") + || extension.equals("nds"); + } + + @Override + public String getDescription() { + return "Nintendo GB(C/A)/DS ROM File (*.gb,*.sgb,*.gbc,*.gba,*.nds)"; + } + +} diff --git a/src/com/dabomstew/pkrandom/gui/RandomizerGUI.form b/src/com/dabomstew/pkrandom/gui/RandomizerGUI.form new file mode 100755 index 000000000..643300a4d --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/RandomizerGUI.form @@ -0,0 +1,2449 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/dabomstew/pkrandom/gui/RandomizerGUI.java b/src/com/dabomstew/pkrandom/gui/RandomizerGUI.java new file mode 100755 index 000000000..42190fdc5 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/RandomizerGUI.java @@ -0,0 +1,5307 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +/*----------------------------------------------------------------------------*/ +/*-- RandomizerGUI.java - the main GUI for the randomizer, containing the --*/ +/*-- various options available and such. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.TreeMap; +import java.util.zip.CRC32; + +import javax.swing.AbstractButton; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; +import javax.xml.bind.DatatypeConverter; + +import com.dabomstew.pkrandom.CodeTweaks; +import com.dabomstew.pkrandom.FileFunctions; +import com.dabomstew.pkrandom.RandomSource; +import com.dabomstew.pkrandom.pokemon.Encounter; +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.GenRestrictions; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.TrainerPokemon; +import com.dabomstew.pkrandom.romhandlers.AbstractDSRomHandler; +import com.dabomstew.pkrandom.romhandlers.Gen1RomHandler; +import com.dabomstew.pkrandom.romhandlers.Gen2RomHandler; +import com.dabomstew.pkrandom.romhandlers.Gen3RomHandler; +import com.dabomstew.pkrandom.romhandlers.Gen4RomHandler; +import com.dabomstew.pkrandom.romhandlers.Gen5RomHandler; +import com.dabomstew.pkrandom.romhandlers.RomHandler; + +/** + * + * @author Stewart + */ +public class RandomizerGUI extends javax.swing.JFrame { + + /** + * + */ + private static final long serialVersionUID = 637989089525556154L; + private RomHandler romHandler; + protected RomHandler[] checkHandlers; + public static final int PRESET_FILE_VERSION = 160; + public static final int UPDATE_VERSION = 1602; + + public static PrintStream verboseLog = System.out; + + private OperationDialog opDialog; + private boolean presetMode; + private GenRestrictions currentRestrictions; + private int currentCodeTweaks; + + private static String rootPath = "./"; + + // Settings + private boolean autoUpdateEnabled; + private boolean haveCheckedCustomNames; + + java.util.ResourceBundle bundle; + + /** + * @param args + * the command line arguments + */ + public static void main(String args[]) { + boolean autoupdate = true; + for (String arg : args) { + if (arg.equalsIgnoreCase("--noupdate")) { + autoupdate = false; + break; + } + } + final boolean au = autoupdate; + try { + javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager + .getSystemLookAndFeelClassName()); + } catch (ClassNotFoundException ex) { + java.util.logging.Logger.getLogger(RandomizerGUI.class.getName()) + .log(java.util.logging.Level.SEVERE, null, ex); + } catch (InstantiationException ex) { + java.util.logging.Logger.getLogger(RandomizerGUI.class.getName()) + .log(java.util.logging.Level.SEVERE, null, ex); + } catch (IllegalAccessException ex) { + java.util.logging.Logger.getLogger(RandomizerGUI.class.getName()) + .log(java.util.logging.Level.SEVERE, null, ex); + } catch (javax.swing.UnsupportedLookAndFeelException ex) { + java.util.logging.Logger.getLogger(RandomizerGUI.class.getName()) + .log(java.util.logging.Level.SEVERE, null, ex); + } + + /* Create and display the form */ + java.awt.EventQueue.invokeLater(new Runnable() { + public void run() { + new RandomizerGUI(au); + } + }); + } + + // constructor + /** + * Creates new form RandomizerGUI + * + * @param autoupdate + */ + public RandomizerGUI(boolean autoupdate) { + + try { + URL location = RandomizerGUI.class.getProtectionDomain() + .getCodeSource().getLocation(); + File fh = new File(java.net.URLDecoder.decode(location.getFile(), + "UTF-8")).getParentFile(); + rootPath = fh.getAbsolutePath() + File.separator; + } catch (Exception e) { + rootPath = "./"; + } + + bundle = java.util.ResourceBundle + .getBundle("com/dabomstew/pkrandom/gui/Bundle"); // NOI18N + testForRequiredConfigs(); + checkHandlers = new RomHandler[] { new Gen1RomHandler(), + new Gen2RomHandler(), new Gen3RomHandler(), + new Gen4RomHandler(), new Gen5RomHandler() }; + initComponents(); + initialiseState(); + autoUpdateEnabled = true; + haveCheckedCustomNames = false; + attemptReadConfig(); + if (!autoupdate) { + // override autoupdate + autoUpdateEnabled = false; + } + boolean canWrite = attemptWriteConfig(); + if (!canWrite) { + JOptionPane.showMessageDialog(null, + bundle.getString("RandomizerGUI.cantWriteConfigFile")); + autoUpdateEnabled = false; + } + setLocationRelativeTo(null); + setVisible(true); + checkCustomNames(); + if (autoUpdateEnabled) { + new UpdateCheckThread(this, false).start(); + } + } + + // config-related stuff + + private void checkCustomNames() { + String[] cnamefiles = new String[] { "trainerclasses.txt", + "trainernames.txt", "nicknames.txt" }; + int[] defaultcsums = new int[] { -1442281799, -1499590809, 1641673648 }; + + boolean foundCustom = false; + for (int file = 0; file < 3; file++) { + File oldFile = new File(rootPath + "/config/" + cnamefiles[file]); + File currentFile = new File(rootPath + cnamefiles[file]); + if (oldFile.exists() && oldFile.canRead() && !currentFile.exists()) { + try { + int crc = getFileChecksum(new FileInputStream(oldFile)); + if (crc != defaultcsums[file]) { + foundCustom = true; + break; + } + } catch (FileNotFoundException e) { + } + } + } + + if (foundCustom) { + int response = JOptionPane + .showConfirmDialog( + RandomizerGUI.this, + bundle.getString("RandomizerGUI.copyNameFilesDialog.text"), + bundle.getString("RandomizerGUI.copyNameFilesDialog.title"), + JOptionPane.YES_NO_OPTION); + boolean onefailed = false; + if (response == JOptionPane.YES_OPTION) { + for (String filename : cnamefiles) { + if (new File(rootPath + "/config/" + filename).canRead()) { + try { + FileInputStream fis = new FileInputStream(new File( + rootPath + "config/" + filename)); + FileOutputStream fos = new FileOutputStream( + new File(rootPath + filename)); + byte[] buf = new byte[1024]; + int len; + while ((len = fis.read(buf)) > 0) { + fos.write(buf, 0, len); + } + fos.close(); + fis.close(); + } catch (IOException ex) { + onefailed = true; + } + } + } + if (onefailed) { + JOptionPane.showMessageDialog(this, bundle + .getString("RandomizerGUI.copyNameFilesFailed")); + } + } + } + + haveCheckedCustomNames = true; + attemptWriteConfig(); + } + + private void attemptReadConfig() { + File fh = new File(rootPath + "config.ini"); + if (!fh.exists() || !fh.canRead()) { + return; + } + + try { + Scanner sc = new Scanner(fh, "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine().trim(); + if (q.contains("//")) { + q = q.substring(0, q.indexOf("//")).trim(); + } + if (!q.isEmpty()) { + String[] tokens = q.split("=", 2); + if (tokens.length == 2) { + String key = tokens[0].trim(); + if (key.equalsIgnoreCase("autoupdate")) { + autoUpdateEnabled = Boolean.parseBoolean(tokens[1] + .trim()); + } else if (key.equalsIgnoreCase("checkedcustomnames")) { + haveCheckedCustomNames = Boolean + .parseBoolean(tokens[1].trim()); + } + } + } + } + sc.close(); + } catch (IOException ex) { + + } + } + + private boolean attemptWriteConfig() { + File fh = new File(rootPath + "config.ini"); + if (fh.exists() && !fh.canWrite()) { + return false; + } + + try { + PrintStream ps = new PrintStream(new FileOutputStream(fh), true, + "UTF-8"); + ps.println("autoupdate=" + autoUpdateEnabled); + ps.println("checkedcustomnames=" + haveCheckedCustomNames); + ps.close(); + return true; + } catch (IOException e) { + return false; + } + + } + + private void testForRequiredConfigs() { + String[] required = new String[] { "gameboy_jap.tbl", + "rby_english.tbl", "rby_freger.tbl", "rby_espita.tbl", + "green_translation.tbl", "gsc_english.tbl", "gsc_freger.tbl", + "gsc_espita.tbl", "gba_english.tbl", "gba_jap.tbl", + "Generation4.tbl", "Generation5.tbl", "gen1_offsets.ini", + "gen2_offsets.ini", "gen3_offsets.ini", "gen4_offsets.ini", + "gen5_offsets.ini", "trainerclasses.txt", "trainernames.txt", + "nicknames.txt" }; + for (String filename : required) { + if (!FileFunctions.configExists(filename)) { + JOptionPane.showMessageDialog(null, String.format( + bundle.getString("RandomizerGUI.configFileMissing"), + filename)); + System.exit(1); + return; + } + } + } + + // form initial state + + private void initialiseState() { + this.romHandler = null; + this.currentRestrictions = null; + this.currentCodeTweaks = 0; + updateCodeTweaksButtonText(); + initialFormState(); + this.romOpenChooser.setCurrentDirectory(new File(rootPath)); + this.romSaveChooser.setCurrentDirectory(new File(rootPath)); + if (new File(rootPath + "settings/").exists()) { + this.qsOpenChooser.setCurrentDirectory(new File(rootPath + + "settings/")); + this.qsSaveChooser.setCurrentDirectory(new File(rootPath + + "settings/")); + } else { + this.qsOpenChooser.setCurrentDirectory(new File(rootPath)); + this.qsSaveChooser.setCurrentDirectory(new File(rootPath)); + } + } + + private void initialFormState() { + // Disable all rom components + this.goRemoveTradeEvosCheckBox.setEnabled(false); + this.goUpdateMovesCheckBox.setEnabled(false); + this.goUpdateMovesLegacyCheckBox.setEnabled(false); + this.goUpdateTypesCheckBox.setEnabled(false); + this.goLowerCaseNamesCheckBox.setEnabled(false); + this.goNationalDexCheckBox.setEnabled(false); + + this.goRemoveTradeEvosCheckBox.setSelected(false); + this.goUpdateMovesCheckBox.setSelected(false); + this.goUpdateMovesLegacyCheckBox.setSelected(false); + this.goUpdateTypesCheckBox.setSelected(false); + this.goLowerCaseNamesCheckBox.setSelected(false); + this.goNationalDexCheckBox.setSelected(false); + + this.goUpdateMovesLegacyCheckBox.setVisible(true); + + this.codeTweaksCB.setEnabled(false); + this.codeTweaksCB.setSelected(false); + this.codeTweaksBtn.setEnabled(false); + this.codeTweaksBtn.setVisible(true); + this.codeTweaksCB.setVisible(true); + this.pokeLimitCB.setEnabled(false); + this.pokeLimitCB.setSelected(false); + this.pokeLimitBtn.setEnabled(false); + this.pokeLimitBtn.setVisible(true); + this.pokeLimitCB.setVisible(true); + this.raceModeCB.setEnabled(false); + this.raceModeCB.setSelected(false); + this.randomizeHollowsCB.setEnabled(false); + this.randomizeHollowsCB.setSelected(false); + this.brokenMovesCB.setEnabled(false); + this.brokenMovesCB.setSelected(false); + + this.riRomNameLabel.setText(bundle + .getString("RandomizerGUI.noRomLoaded")); + this.riRomCodeLabel.setText(""); + this.riRomSupportLabel.setText(""); + + this.loadQSButton.setEnabled(false); + this.saveQSButton.setEnabled(false); + + this.pbsChangesUnchangedRB.setEnabled(false); + this.pbsChangesRandomEvosRB.setEnabled(false); + this.pbsChangesRandomTotalRB.setEnabled(false); + this.pbsChangesShuffleRB.setEnabled(false); + this.pbsChangesUnchangedRB.setSelected(true); + this.pbsStandardEXPCurvesCB.setEnabled(false); + this.pbsStandardEXPCurvesCB.setSelected(false); + + this.abilitiesPanel.setVisible(true); + this.paUnchangedRB.setEnabled(false); + this.paRandomizeRB.setEnabled(false); + this.paWonderGuardCB.setEnabled(false); + this.paUnchangedRB.setSelected(true); + this.paWonderGuardCB.setSelected(false); + + this.spCustomPoke1Chooser.setEnabled(false); + this.spCustomPoke2Chooser.setEnabled(false); + this.spCustomPoke3Chooser.setEnabled(false); + this.spCustomPoke1Chooser.setSelectedIndex(0); + this.spCustomPoke1Chooser.setModel(new DefaultComboBoxModel( + new String[] { "--" })); + this.spCustomPoke2Chooser.setSelectedIndex(0); + this.spCustomPoke2Chooser.setModel(new DefaultComboBoxModel( + new String[] { "--" })); + this.spCustomPoke3Chooser.setSelectedIndex(0); + this.spCustomPoke3Chooser.setModel(new DefaultComboBoxModel( + new String[] { "--" })); + this.spCustomRB.setEnabled(false); + this.spRandomRB.setEnabled(false); + this.spRandom2EvosRB.setEnabled(false); + this.spUnchangedRB.setEnabled(false); + this.spUnchangedRB.setSelected(true); + this.spHeldItemsCB.setEnabled(false); + this.spHeldItemsCB.setSelected(false); + this.spHeldItemsCB.setVisible(true); + + this.pmsRandomTotalRB.setEnabled(false); + this.pmsRandomTypeRB.setEnabled(false); + this.pmsUnchangedRB.setEnabled(false); + this.pmsUnchangedRB.setSelected(true); + this.pmsMetronomeOnlyRB.setEnabled(false); + this.pms4MovesCB.setEnabled(false); + this.pms4MovesCB.setSelected(false); + this.pms4MovesCB.setVisible(true); + + this.ptRandomFollowEvosRB.setEnabled(false); + this.ptRandomTotalRB.setEnabled(false); + this.ptUnchangedRB.setEnabled(false); + this.ptUnchangedRB.setSelected(true); + + this.tpPowerLevelsCB.setEnabled(false); + this.tpRandomRB.setEnabled(false); + this.tpRivalCarriesStarterCB.setEnabled(false); + this.tpTypeThemedRB.setEnabled(false); + this.tpTypeWeightingCB.setEnabled(false); + this.tpNoLegendariesCB.setEnabled(false); + this.tpNoEarlyShedinjaCB.setEnabled(false); + this.tpNoEarlyShedinjaCB.setVisible(true); + this.tpUnchangedRB.setEnabled(false); + this.tpUnchangedRB.setSelected(true); + this.tpPowerLevelsCB.setSelected(false); + this.tpRivalCarriesStarterCB.setSelected(false); + this.tpTypeWeightingCB.setSelected(false); + this.tpNoLegendariesCB.setSelected(false); + this.tpNoEarlyShedinjaCB.setSelected(false); + + this.tnRandomizeCB.setEnabled(false); + this.tcnRandomizeCB.setEnabled(false); + + this.tnRandomizeCB.setSelected(false); + this.tcnRandomizeCB.setSelected(false); + + this.wpUnchangedRB.setEnabled(false); + this.wpRandomRB.setEnabled(false); + this.wpArea11RB.setEnabled(false); + this.wpGlobalRB.setEnabled(false); + this.wpUnchangedRB.setSelected(true); + + this.wpARNoneRB.setEnabled(false); + this.wpARCatchEmAllRB.setEnabled(false); + this.wpARTypeThemedRB.setEnabled(false); + this.wpARSimilarStrengthRB.setEnabled(false); + this.wpARNoneRB.setSelected(true); + + this.wpUseTimeCB.setEnabled(false); + this.wpUseTimeCB.setVisible(true); + this.wpUseTimeCB.setSelected(false); + + this.wpNoLegendariesCB.setEnabled(false); + this.wpNoLegendariesCB.setSelected(false); + + this.wpCatchRateCB.setEnabled(false); + this.wpCatchRateCB.setSelected(false); + + this.wpHeldItemsCB.setEnabled(false); + this.wpHeldItemsCB.setSelected(false); + this.wpHeldItemsCB.setVisible(true); + + this.stpRandomL4LRB.setEnabled(false); + this.stpRandomTotalRB.setEnabled(false); + this.stpUnchangedRB.setEnabled(false); + this.stpUnchangedRB.setSelected(true); + + this.tmmRandomRB.setEnabled(false); + this.tmmUnchangedRB.setEnabled(false); + this.tmmUnchangedRB.setSelected(true); + + this.thcRandomTotalRB.setEnabled(false); + this.thcRandomTypeRB.setEnabled(false); + this.thcUnchangedRB.setEnabled(false); + this.thcUnchangedRB.setSelected(true); + + this.mtmRandomRB.setEnabled(false); + this.mtmUnchangedRB.setEnabled(false); + this.mtmUnchangedRB.setSelected(true); + + this.mtcRandomTotalRB.setEnabled(false); + this.mtcRandomTypeRB.setEnabled(false); + this.mtcUnchangedRB.setEnabled(false); + this.mtcUnchangedRB.setSelected(true); + + this.mtMovesPanel.setVisible(true); + this.mtCompatPanel.setVisible(true); + this.mtNoExistLabel.setVisible(false); + + this.igtUnchangedRB.setEnabled(false); + this.igtGivenOnlyRB.setEnabled(false); + this.igtBothRB.setEnabled(false); + this.igtUnchangedRB.setSelected(true); + + this.igtRandomItemCB.setEnabled(false); + this.igtRandomItemCB.setSelected(false); + this.igtRandomItemCB.setVisible(true); + + this.igtRandomIVsCB.setEnabled(false); + this.igtRandomIVsCB.setSelected(false); + this.igtRandomIVsCB.setVisible(true); + + this.igtRandomOTCB.setEnabled(false); + this.igtRandomOTCB.setSelected(false); + this.igtRandomOTCB.setVisible(true); + + this.igtRandomNicknameCB.setEnabled(false); + this.igtRandomNicknameCB.setSelected(false); + + this.fiUnchangedRB.setEnabled(false); + this.fiShuffleRB.setEnabled(false); + this.fiRandomRB.setEnabled(false); + this.fiUnchangedRB.setSelected(true); + + } + + // rom loading + + private void loadROM() { + romOpenChooser.setSelectedFile(null); + int returnVal = romOpenChooser.showOpenDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + final File fh = romOpenChooser.getSelectedFile(); + // first, check for common filetypes that aren't ROMs + // read first 10 bytes of the file to do this + try { + FileInputStream fis = new FileInputStream(fh); + byte[] sig = new byte[10]; + int siglen = fis.read(sig); + fis.close(); + if (siglen < 10) { + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.tooShortToBeARom"), + fh.getName())); + return; + } + if (sig[0] == 0x50 && sig[1] == 0x4b && sig[2] == 0x03 + && sig[3] == 0x04) { + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.openedZIPfile"), + fh.getName())); + return; + } + if (sig[0] == 0x52 && sig[1] == 0x61 && sig[2] == 0x72 + && sig[3] == 0x21 && sig[4] == 0x1A && sig[5] == 0x07) { + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.openedRARfile"), + fh.getName())); + return; + } + if (sig[0] == 'P' && sig[1] == 'A' && sig[2] == 'T' + && sig[3] == 'C' && sig[4] == 'H') { + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.openedIPSfile"), + fh.getName())); + return; + } + // none of these? let's see if it's a valid ROM, then + } catch (IOException ex) { + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.unreadableRom"), + fh.getName())); + return; + } + for (RomHandler rh : checkHandlers) { + if (rh.detectRom(fh.getAbsolutePath())) { + this.romHandler = rh; + opDialog = new OperationDialog( + bundle.getString("RandomizerGUI.loadingText"), + this, true); + Thread t = new Thread() { + @Override + public void run() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + opDialog.setVisible(true); + } + }); + try { + RandomizerGUI.this.romHandler.loadRom(fh + .getAbsolutePath()); + } catch (Exception ex) { + long time = System.currentTimeMillis(); + try { + String errlog = "error_" + time + ".txt"; + PrintStream ps = new PrintStream( + new FileOutputStream(errlog)); + PrintStream e1 = System.err; + System.setErr(ps); + ex.printStackTrace(); + verboseLog.close(); + System.setErr(e1); + ps.close(); + JOptionPane + .showMessageDialog( + RandomizerGUI.this, + String.format( + bundle.getString("RandomizerGUI.loadFailed"), + errlog)); + } catch (Exception logex) { + JOptionPane + .showMessageDialog( + RandomizerGUI.this, + bundle.getString("RandomizerGUI.loadFailedNoLog")); + verboseLog.close(); + } + } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + RandomizerGUI.this.opDialog + .setVisible(false); + RandomizerGUI.this.initialFormState(); + RandomizerGUI.this.romLoaded(); + } + }); + } + }; + t.start(); + + return; + } + } + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.unsupportedRom"), + fh.getName())); + } + + } + + private void romLoaded() { + try { + this.currentRestrictions = null; + this.currentCodeTweaks = 0; + updateCodeTweaksButtonText(); + this.riRomNameLabel.setText(this.romHandler.getROMName()); + this.riRomCodeLabel.setText(this.romHandler.getROMCode()); + this.riRomSupportLabel.setText(bundle + .getString("RandomizerGUI.romSupportPrefix") + + " " + + this.romHandler.getSupportLevel()); + this.goUpdateMovesCheckBox.setSelected(false); + if (romHandler instanceof Gen1RomHandler) { + this.goUpdateTypesCheckBox.setEnabled(true); + } + this.goUpdateMovesCheckBox.setSelected(false); + this.goUpdateMovesCheckBox.setEnabled(true); + this.goUpdateMovesLegacyCheckBox.setSelected(false); + this.goUpdateMovesLegacyCheckBox.setEnabled(false); + this.goUpdateMovesLegacyCheckBox + .setVisible(!(romHandler instanceof Gen5RomHandler)); + this.goRemoveTradeEvosCheckBox.setSelected(false); + this.goRemoveTradeEvosCheckBox.setEnabled(true); + if (!(romHandler instanceof Gen5RomHandler) + && !(romHandler instanceof Gen4RomHandler)) { + this.goLowerCaseNamesCheckBox.setSelected(false); + this.goLowerCaseNamesCheckBox.setEnabled(true); + } + if (romHandler instanceof Gen3RomHandler) { + this.goNationalDexCheckBox.setSelected(false); + this.goNationalDexCheckBox.setEnabled(true); + } + this.raceModeCB.setSelected(false); + this.raceModeCB.setEnabled(true); + + this.codeTweaksCB.setSelected(false); + this.codeTweaksCB.setEnabled(romHandler.codeTweaksAvailable() != 0); + this.codeTweaksBtn.setEnabled(false); + this.codeTweaksBtn + .setVisible(romHandler.codeTweaksAvailable() != 0); + this.codeTweaksCB.setVisible(romHandler.codeTweaksAvailable() != 0); + + this.pokeLimitCB.setSelected(false); + this.pokeLimitBtn.setEnabled(false); + this.pokeLimitCB + .setEnabled(!(romHandler instanceof Gen1RomHandler)); + this.pokeLimitCB + .setVisible(!(romHandler instanceof Gen1RomHandler)); + this.pokeLimitBtn + .setVisible(!(romHandler instanceof Gen1RomHandler)); + + this.randomizeHollowsCB.setSelected(false); + this.randomizeHollowsCB.setEnabled(romHandler + .hasHiddenHollowPokemon()); + + this.brokenMovesCB.setSelected(false); + this.brokenMovesCB.setEnabled(true); + + this.loadQSButton.setEnabled(true); + this.saveQSButton.setEnabled(true); + + this.pbsChangesUnchangedRB.setEnabled(true); + this.pbsChangesUnchangedRB.setSelected(true); + this.pbsChangesRandomEvosRB.setEnabled(true); + this.pbsChangesRandomTotalRB.setEnabled(true); + this.pbsChangesShuffleRB.setEnabled(true); + + this.pbsStandardEXPCurvesCB.setEnabled(true); + this.pbsStandardEXPCurvesCB.setSelected(false); + + if (romHandler.abilitiesPerPokemon() > 0) { + this.paUnchangedRB.setEnabled(true); + this.paUnchangedRB.setSelected(true); + this.paRandomizeRB.setEnabled(true); + this.paWonderGuardCB.setEnabled(false); + } else { + this.abilitiesPanel.setVisible(false); + } + + this.spUnchangedRB.setEnabled(true); + this.spUnchangedRB.setSelected(true); + + this.spCustomPoke3Chooser.setVisible(true); + if (romHandler.canChangeStarters()) { + this.spCustomRB.setEnabled(true); + this.spRandomRB.setEnabled(true); + this.spRandom2EvosRB.setEnabled(true); + if (romHandler.isYellow()) { + this.spCustomPoke3Chooser.setVisible(false); + } + populateDropdowns(); + } + + this.spHeldItemsCB.setSelected(false); + boolean hasStarterHeldItems = (romHandler instanceof Gen2RomHandler || romHandler instanceof Gen3RomHandler); + this.spHeldItemsCB.setEnabled(hasStarterHeldItems); + this.spHeldItemsCB.setVisible(hasStarterHeldItems); + + this.pmsRandomTotalRB.setEnabled(true); + this.pmsRandomTypeRB.setEnabled(true); + this.pmsUnchangedRB.setEnabled(true); + this.pmsUnchangedRB.setSelected(true); + this.pmsMetronomeOnlyRB.setEnabled(true); + + this.pms4MovesCB.setVisible(romHandler.supportsFourStartingMoves()); + + this.ptRandomFollowEvosRB.setEnabled(true); + this.ptRandomTotalRB.setEnabled(true); + this.ptUnchangedRB.setEnabled(true); + this.ptUnchangedRB.setSelected(true); + + this.tpRandomRB.setEnabled(true); + this.tpTypeThemedRB.setEnabled(true); + this.tpUnchangedRB.setEnabled(true); + this.tpUnchangedRB.setSelected(true); + this.tnRandomizeCB.setEnabled(true); + this.tcnRandomizeCB.setEnabled(true); + + if (romHandler instanceof Gen1RomHandler + || romHandler instanceof Gen2RomHandler) { + this.tpNoEarlyShedinjaCB.setVisible(false); + } else { + this.tpNoEarlyShedinjaCB.setVisible(true); + } + this.tpNoEarlyShedinjaCB.setSelected(false); + + this.wpArea11RB.setEnabled(true); + this.wpGlobalRB.setEnabled(true); + this.wpRandomRB.setEnabled(true); + this.wpUnchangedRB.setEnabled(true); + this.wpUnchangedRB.setSelected(true); + this.wpUseTimeCB.setEnabled(false); + this.wpNoLegendariesCB.setEnabled(false); + if (!romHandler.hasTimeBasedEncounters()) { + this.wpUseTimeCB.setVisible(false); + } + this.wpCatchRateCB.setEnabled(true); + + this.wpHeldItemsCB.setSelected(false); + this.wpHeldItemsCB.setEnabled(true); + if (romHandler instanceof Gen1RomHandler) { + this.wpHeldItemsCB.setVisible(false); + } + + this.stpUnchangedRB.setEnabled(true); + if (this.romHandler.canChangeStaticPokemon()) { + this.stpRandomL4LRB.setEnabled(true); + this.stpRandomTotalRB.setEnabled(true); + + } + + this.tmmRandomRB.setEnabled(true); + this.tmmUnchangedRB.setEnabled(true); + + this.thcRandomTotalRB.setEnabled(true); + this.thcRandomTypeRB.setEnabled(true); + this.thcUnchangedRB.setEnabled(true); + + if (this.romHandler.hasMoveTutors()) { + this.mtmRandomRB.setEnabled(true); + this.mtmUnchangedRB.setEnabled(true); + + this.mtcRandomTotalRB.setEnabled(true); + this.mtcRandomTypeRB.setEnabled(true); + this.mtcUnchangedRB.setEnabled(true); + } else { + this.mtCompatPanel.setVisible(false); + this.mtMovesPanel.setVisible(false); + this.mtNoExistLabel.setVisible(true); + } + + this.igtUnchangedRB.setEnabled(true); + this.igtBothRB.setEnabled(true); + this.igtGivenOnlyRB.setEnabled(true); + + if (this.romHandler instanceof Gen1RomHandler) { + this.igtRandomItemCB.setVisible(false); + this.igtRandomIVsCB.setVisible(false); + this.igtRandomOTCB.setVisible(false); + } + + this.fiUnchangedRB.setEnabled(true); + this.fiRandomRB.setEnabled(true); + this.fiShuffleRB.setEnabled(true); + + if (this.romHandler instanceof AbstractDSRomHandler) { + ((AbstractDSRomHandler) this.romHandler).closeInnerRom(); + } + } catch (Exception ex) { + long time = System.currentTimeMillis(); + try { + String errlog = "error_" + time + ".txt"; + PrintStream ps = new PrintStream(new FileOutputStream(errlog)); + PrintStream e1 = System.err; + System.setErr(ps); + ex.printStackTrace(); + System.setErr(e1); + ps.close(); + JOptionPane + .showMessageDialog( + RandomizerGUI.this, + String.format( + bundle.getString("RandomizerGUI.processFailed"), + errlog)); + } catch (Exception logex) { + JOptionPane.showMessageDialog(RandomizerGUI.this, + bundle.getString("RandomizerGUI.processFailedNoLog")); + } + } + } + + private void populateDropdowns() { + List currentStarters = romHandler.getStarters(); + List allPokes = romHandler.getPokemon(); + String[] pokeNames = new String[allPokes.size() - 1]; + for (int i = 1; i < allPokes.size(); i++) { + pokeNames[i - 1] = allPokes.get(i).name; + } + this.spCustomPoke1Chooser.setModel(new DefaultComboBoxModel(pokeNames)); + this.spCustomPoke1Chooser + .setSelectedIndex(currentStarters.get(0).number - 1); + this.spCustomPoke2Chooser.setModel(new DefaultComboBoxModel(pokeNames)); + this.spCustomPoke2Chooser + .setSelectedIndex(currentStarters.get(1).number - 1); + if (!romHandler.isYellow()) { + this.spCustomPoke3Chooser.setModel(new DefaultComboBoxModel( + pokeNames)); + this.spCustomPoke3Chooser + .setSelectedIndex(currentStarters.get(2).number - 1); + } + } + + private void enableOrDisableSubControls() { + // This isn't for a new ROM being loaded (that's romLoaded) + // This is just for when a radio button gets selected or state is loaded + // and we need to enable/disable secondary controls + // e.g. wild pokemon / trainer pokemon "modifier" + // and the 3 starter pokemon dropdowns + + if (this.goUpdateMovesCheckBox.isSelected() + && !(romHandler instanceof Gen5RomHandler)) { + this.goUpdateMovesLegacyCheckBox.setEnabled(true); + } else { + this.goUpdateMovesLegacyCheckBox.setEnabled(false); + this.goUpdateMovesLegacyCheckBox.setSelected(false); + } + + this.codeTweaksBtn.setEnabled(this.codeTweaksCB.isSelected()); + updateCodeTweaksButtonText(); + this.pokeLimitBtn.setEnabled(this.pokeLimitCB.isSelected()); + + if (this.spCustomRB.isSelected()) { + this.spCustomPoke1Chooser.setEnabled(true); + this.spCustomPoke2Chooser.setEnabled(true); + this.spCustomPoke3Chooser.setEnabled(true); + } else { + this.spCustomPoke1Chooser.setEnabled(false); + this.spCustomPoke2Chooser.setEnabled(false); + this.spCustomPoke3Chooser.setEnabled(false); + } + + if (this.paRandomizeRB.isSelected()) { + this.paWonderGuardCB.setEnabled(true); + } else { + this.paWonderGuardCB.setEnabled(false); + this.paWonderGuardCB.setSelected(false); + } + + if (this.tpUnchangedRB.isSelected()) { + this.tpPowerLevelsCB.setEnabled(false); + this.tpRivalCarriesStarterCB.setEnabled(false); + this.tpNoLegendariesCB.setEnabled(false); + this.tpNoEarlyShedinjaCB.setEnabled(false); + this.tpNoEarlyShedinjaCB.setSelected(false); + } else { + this.tpPowerLevelsCB.setEnabled(true); + this.tpRivalCarriesStarterCB.setEnabled(true); + this.tpNoLegendariesCB.setEnabled(true); + this.tpNoEarlyShedinjaCB.setEnabled(true); + } + + if (this.tpTypeThemedRB.isSelected()) { + this.tpTypeWeightingCB.setEnabled(true); + } else { + this.tpTypeWeightingCB.setEnabled(false); + } + + if (this.wpArea11RB.isSelected() || this.wpRandomRB.isSelected()) { + this.wpARNoneRB.setEnabled(true); + this.wpARSimilarStrengthRB.setEnabled(true); + this.wpARCatchEmAllRB.setEnabled(true); + this.wpARTypeThemedRB.setEnabled(true); + } else if (this.wpGlobalRB.isSelected()) { + if (this.wpARCatchEmAllRB.isSelected() + || this.wpARTypeThemedRB.isSelected()) { + this.wpARNoneRB.setSelected(true); + } + this.wpARNoneRB.setEnabled(true); + this.wpARSimilarStrengthRB.setEnabled(true); + this.wpARCatchEmAllRB.setEnabled(false); + this.wpARTypeThemedRB.setEnabled(false); + } else { + this.wpARNoneRB.setEnabled(false); + this.wpARSimilarStrengthRB.setEnabled(false); + this.wpARCatchEmAllRB.setEnabled(false); + this.wpARTypeThemedRB.setEnabled(false); + this.wpARNoneRB.setSelected(true); + } + + if (this.wpUnchangedRB.isSelected()) { + this.wpUseTimeCB.setEnabled(false); + this.wpNoLegendariesCB.setEnabled(false); + } else { + this.wpUseTimeCB.setEnabled(true); + this.wpNoLegendariesCB.setEnabled(true); + } + + if (this.igtUnchangedRB.isSelected()) { + this.igtRandomItemCB.setEnabled(false); + this.igtRandomIVsCB.setEnabled(false); + this.igtRandomNicknameCB.setEnabled(false); + this.igtRandomOTCB.setEnabled(false); + } else { + this.igtRandomItemCB.setEnabled(true); + this.igtRandomIVsCB.setEnabled(true); + this.igtRandomNicknameCB.setEnabled(true); + this.igtRandomOTCB.setEnabled(true); + } + + if (this.pmsMetronomeOnlyRB.isSelected()) { + this.tmmUnchangedRB.setEnabled(false); + this.tmmRandomRB.setEnabled(false); + this.tmmUnchangedRB.setSelected(true); + + this.mtmUnchangedRB.setEnabled(false); + this.mtmRandomRB.setEnabled(false); + this.mtmUnchangedRB.setSelected(true); + } else { + this.tmmUnchangedRB.setEnabled(true); + this.tmmRandomRB.setEnabled(true); + + this.mtmUnchangedRB.setEnabled(true); + this.mtmRandomRB.setEnabled(true); + } + + if (this.pmsMetronomeOnlyRB.isSelected() + || this.pmsUnchangedRB.isSelected()) { + this.pms4MovesCB.setEnabled(false); + this.pms4MovesCB.setSelected(false); + } else { + this.pms4MovesCB.setEnabled(true); + } + } + + private void saveROM() { + if (romHandler == null) { + return; // none loaded + } + if (raceModeCB.isSelected() && tpUnchangedRB.isSelected() + && wpUnchangedRB.isSelected()) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.raceModeRequirements")); + return; + } + if (pokeLimitCB.isSelected() + && (this.currentRestrictions == null || this.currentRestrictions + .nothingSelected())) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.pokeLimitNotChosen")); + return; + } + romSaveChooser.setSelectedFile(null); + int returnVal = romSaveChooser.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File fh = romSaveChooser.getSelectedFile(); + // Fix or add extension + List extensions = new ArrayList(Arrays.asList( + "sgb", "gbc", "gba", "nds")); + extensions.remove(this.romHandler.getDefaultExtension()); + fh = FileFunctions.fixFilename(fh, + this.romHandler.getDefaultExtension(), extensions); + boolean allowed = true; + if (this.romHandler instanceof AbstractDSRomHandler) { + String currentFN = this.romHandler.loadedFilename(); + if (currentFN.equals(fh.getAbsolutePath())) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.cantOverwriteDS")); + allowed = false; + } + } + if (allowed) { + // Get a seed + long seed = RandomSource.pickSeed(); + // Apply it + RandomSource.seed(seed); + presetMode = false; + performRandomization(fh.getAbsolutePath(), seed, null, null, + null); + } + } + } + + private String getConfigString() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + baos.write(makeByteSelected(this.goLowerCaseNamesCheckBox, + this.goNationalDexCheckBox, this.goRemoveTradeEvosCheckBox, + this.goUpdateMovesCheckBox, this.goUpdateMovesLegacyCheckBox, + this.goUpdateTypesCheckBox, this.tnRandomizeCB, + this.tcnRandomizeCB)); + + baos.write(makeByteSelected(this.pbsChangesRandomEvosRB, + this.pbsChangesRandomTotalRB, this.pbsChangesShuffleRB, + this.pbsChangesUnchangedRB, this.paUnchangedRB, + this.paRandomizeRB, this.paWonderGuardCB, + this.pbsStandardEXPCurvesCB)); + + baos.write(makeByteSelected(this.ptRandomFollowEvosRB, + this.ptRandomTotalRB, this.ptUnchangedRB, this.codeTweaksCB, + this.raceModeCB, this.randomizeHollowsCB, this.brokenMovesCB, + this.pokeLimitCB)); + + baos.write(makeByteSelected(this.spCustomRB, this.spRandomRB, + this.spUnchangedRB, this.spRandom2EvosRB, this.spHeldItemsCB)); + + // @4 + writePokemonIndex(baos, this.spCustomPoke1Chooser); + writePokemonIndex(baos, this.spCustomPoke2Chooser); + writePokemonIndex(baos, this.spCustomPoke3Chooser); + + // 10 + baos.write(makeByteSelected(this.pmsRandomTotalRB, + this.pmsRandomTypeRB, this.pmsUnchangedRB, + this.pmsMetronomeOnlyRB, this.pms4MovesCB)); + + // changed 160 + baos.write(makeByteSelected(this.tpPowerLevelsCB, this.tpRandomRB, + this.tpRivalCarriesStarterCB, this.tpTypeThemedRB, + this.tpTypeWeightingCB, this.tpUnchangedRB, + this.tpNoLegendariesCB, this.tpNoEarlyShedinjaCB)); + + baos.write(makeByteSelected(this.wpARCatchEmAllRB, this.wpArea11RB, + this.wpARNoneRB, this.wpARTypeThemedRB, this.wpGlobalRB, + this.wpRandomRB, this.wpUnchangedRB, this.wpUseTimeCB)); + + // bugfix 161 + baos.write(makeByteSelected(this.wpCatchRateCB, this.wpNoLegendariesCB, + this.wpARSimilarStrengthRB, this.wpHeldItemsCB)); + + baos.write(makeByteSelected(this.stpUnchangedRB, this.stpRandomL4LRB, + this.stpRandomTotalRB)); + + baos.write(makeByteSelected(this.thcRandomTotalRB, + this.thcRandomTypeRB, this.thcUnchangedRB, this.tmmRandomRB, + this.tmmUnchangedRB)); + + baos.write(makeByteSelected(this.mtcRandomTotalRB, + this.mtcRandomTypeRB, this.mtcUnchangedRB, this.mtmRandomRB, + this.mtmUnchangedRB)); + + // new 150 + baos.write(makeByteSelected(this.igtBothRB, this.igtGivenOnlyRB, + this.igtRandomItemCB, this.igtRandomIVsCB, + this.igtRandomNicknameCB, this.igtRandomOTCB, + this.igtUnchangedRB)); + + baos.write(makeByteSelected(this.fiRandomRB, this.fiShuffleRB, + this.fiUnchangedRB)); + + // @ 19 + try { + if (currentRestrictions != null) { + writeFullInt(baos, currentRestrictions.toInt()); + } else { + writeFullInt(baos, 0); + } + } catch (IOException e) { + } + + // @ 23 + try { + writeFullInt(baos, currentCodeTweaks); + } catch (IOException e) { + + } + + try { + byte[] romName = romHandler.getROMName().getBytes("US-ASCII"); + baos.write(romName.length); + baos.write(romName); + } catch (UnsupportedEncodingException e) { + baos.write(0); + } catch (IOException e) { + baos.write(0); + } + + byte[] current = baos.toByteArray(); + CRC32 checksum = new CRC32(); + checksum.update(current); + + try { + writeFullInt(baos, (int) checksum.getValue()); + writeFullInt(baos, getFileChecksum("trainerclasses.txt")); + writeFullInt(baos, getFileChecksum("trainernames.txt")); + writeFullInt(baos, getFileChecksum("nicknames.txt")); + } catch (IOException e) { + } + + return DatatypeConverter.printBase64Binary(baos.toByteArray()); + } + + private static int getFileChecksum(String filename) { + try { + return getFileChecksum(FileFunctions.openConfig(filename)); + } catch (IOException e) { + return 0; + } + } + + private static int getFileChecksum(InputStream stream) { + try { + Scanner sc = new Scanner(stream, "UTF-8"); + CRC32 checksum = new CRC32(); + while (sc.hasNextLine()) { + String line = sc.nextLine().trim(); + if (!line.isEmpty()) { + checksum.update(line.getBytes("UTF-8")); + } + } + sc.close(); + return (int) checksum.getValue(); + } catch (IOException e) { + return 0; + } + + } + + public String getValidRequiredROMName(String config, byte[] trainerClasses, + byte[] trainerNames, byte[] nicknames) + throws UnsupportedEncodingException, + InvalidSupplementFilesException { + byte[] data = DatatypeConverter.parseBase64Binary(config); + + if (data.length < 44) { + return null; // too short + } + + // Check the checksum + ByteBuffer buf = ByteBuffer.allocate(4).put(data, data.length - 16, 4); + buf.rewind(); + int crc = buf.getInt(); + + CRC32 checksum = new CRC32(); + checksum.update(data, 0, data.length - 16); + if ((int) checksum.getValue() != crc) { + return null; // checksum failure + } + + // Check the trainerclass & trainernames crc + if (trainerClasses == null + && !checkOtherCRC(data, 0, 6, "trainerclasses.txt", + data.length - 12)) { + JOptionPane.showMessageDialog(null, + bundle.getString("RandomizerGUI.presetFailTrainerClasses")); + throw new InvalidSupplementFilesException(); + } + if (trainerNames == null + && (!checkOtherCRC(data, 0, 5, "trainernames.txt", + data.length - 8) || !checkOtherCRC(data, 16, 5, + "trainernames.txt", data.length - 8))) { + JOptionPane.showMessageDialog(null, + bundle.getString("RandomizerGUI.presetFailTrainerNames")); + throw new InvalidSupplementFilesException(); + } + if (nicknames == null + && !checkOtherCRC(data, 16, 4, "nicknames.txt", data.length - 4)) { + JOptionPane.showMessageDialog(null, + bundle.getString("RandomizerGUI.presetFailNicknames")); + throw new InvalidSupplementFilesException(); + } + + int nameLength = data[27] & 0xFF; + if (data.length != 44 + nameLength) { + return null; // not valid length + } + String name = new String(data, 28, nameLength, "US-ASCII"); + return name; + } + + private boolean restoreFrom(String config) { + // Need to add enables + byte[] data = DatatypeConverter.parseBase64Binary(config); + + // Check the checksum + ByteBuffer buf = ByteBuffer.allocate(4).put(data, data.length - 16, 4); + buf.rewind(); + int crc = buf.getInt(); + + CRC32 checksum = new CRC32(); + checksum.update(data, 0, data.length - 16); + + if ((int) checksum.getValue() != crc) { + return false; // checksum failure + } + + // Restore the actual controls + restoreStates(data[0], this.goLowerCaseNamesCheckBox, + this.goNationalDexCheckBox, this.goRemoveTradeEvosCheckBox, + this.goUpdateMovesCheckBox, this.goUpdateMovesLegacyCheckBox, + this.goUpdateTypesCheckBox, this.tnRandomizeCB, + this.tcnRandomizeCB); + + // sanity override + if (this.goUpdateMovesLegacyCheckBox.isSelected() + && (romHandler instanceof Gen5RomHandler)) { + // they probably don't want moves updated actually + this.goUpdateMovesLegacyCheckBox.setSelected(false); + this.goUpdateMovesCheckBox.setSelected(false); + } + + restoreStates(data[1], this.pbsChangesRandomEvosRB, + this.pbsChangesRandomTotalRB, this.pbsChangesShuffleRB, + this.pbsChangesUnchangedRB, this.paUnchangedRB, + this.paRandomizeRB, this.paWonderGuardCB, + this.pbsStandardEXPCurvesCB); + + restoreStates(data[2], this.ptRandomFollowEvosRB, this.ptRandomTotalRB, + this.ptUnchangedRB, this.codeTweaksCB, this.raceModeCB, + this.randomizeHollowsCB, this.brokenMovesCB, this.pokeLimitCB); + + restoreStates(data[3], this.spCustomRB, this.spRandomRB, + this.spUnchangedRB, this.spRandom2EvosRB, this.spHeldItemsCB); + + restoreSelectedIndex(data, 4, this.spCustomPoke1Chooser); + restoreSelectedIndex(data, 6, this.spCustomPoke2Chooser); + restoreSelectedIndex(data, 8, this.spCustomPoke3Chooser); + + restoreStates(data[10], this.pmsRandomTotalRB, this.pmsRandomTypeRB, + this.pmsUnchangedRB, this.pmsMetronomeOnlyRB, this.pms4MovesCB); + + // changed 160 + restoreStates(data[11], this.tpPowerLevelsCB, this.tpRandomRB, + this.tpRivalCarriesStarterCB, this.tpTypeThemedRB, + this.tpTypeWeightingCB, this.tpUnchangedRB, + this.tpNoLegendariesCB, this.tpNoEarlyShedinjaCB); + + restoreStates(data[12], this.wpARCatchEmAllRB, this.wpArea11RB, + this.wpARNoneRB, this.wpARTypeThemedRB, this.wpGlobalRB, + this.wpRandomRB, this.wpUnchangedRB, this.wpUseTimeCB); + + restoreStates(data[13], this.wpCatchRateCB, this.wpNoLegendariesCB, + this.wpARSimilarStrengthRB, this.wpHeldItemsCB); + + restoreStates(data[14], this.stpUnchangedRB, this.stpRandomL4LRB, + this.stpRandomTotalRB); + + restoreStates(data[15], this.thcRandomTotalRB, this.thcRandomTypeRB, + this.thcUnchangedRB, this.tmmRandomRB, this.tmmUnchangedRB); + restoreStates(data[16], this.mtcRandomTotalRB, this.mtcRandomTypeRB, + this.mtcUnchangedRB, this.mtmRandomRB, this.mtmUnchangedRB); + + // new 150 + restoreStates(data[17], this.igtBothRB, this.igtGivenOnlyRB, + this.igtRandomItemCB, this.igtRandomIVsCB, + this.igtRandomNicknameCB, this.igtRandomOTCB, + this.igtUnchangedRB); + restoreStates(data[18], this.fiRandomRB, this.fiShuffleRB, + this.fiUnchangedRB); + + // gen restrictions + int genlim = readFullInt(data, 19); + if (genlim == 0) { + this.currentRestrictions = null; + } else { + this.currentRestrictions = new GenRestrictions(genlim); + this.currentRestrictions.limitToGen(this.romHandler + .generationOfPokemon()); + } + + int codetweaks = readFullInt(data, 23); + this.currentCodeTweaks = codetweaks + & this.romHandler.codeTweaksAvailable(); + updateCodeTweaksButtonText(); + // Sanity override + if (this.currentCodeTweaks == 0) { + this.codeTweaksCB.setSelected(false); + } + + this.enableOrDisableSubControls(); + + // Name data is ignored here - we should've used it earlier. + return true; + } + + private void performRandomization(final String filename, final long seed, + byte[] trainerClasses, byte[] trainerNames, byte[] nicknames) { + + final boolean raceMode = raceModeCB.isSelected(); + int checkValue = 0; + final long startTime = System.currentTimeMillis(); + // Setup verbose log + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + String nl = System.getProperty("line.separator"); + try { + verboseLog = new PrintStream(baos, false, "UTF-8"); + } catch (UnsupportedEncodingException e) { + verboseLog = new PrintStream(baos); + } + try { + // limit pokemon? + if (this.pokeLimitCB.isSelected()) { + romHandler.setPokemonPool(currentRestrictions); + romHandler.removeEvosForPokemonPool(); + } + + // Update type effectiveness in RBY? + if (romHandler instanceof Gen1RomHandler + && this.goUpdateTypesCheckBox.isSelected()) { + romHandler.fixTypeEffectiveness(); + } + + // Move updates + if (this.goUpdateMovesCheckBox.isSelected()) { + romHandler.initMoveUpdates(); + if (!(romHandler instanceof Gen5RomHandler)) { + romHandler.updateMovesToGen5(); + } + if (!this.goUpdateMovesLegacyCheckBox.isSelected()) { + romHandler.updateMovesToGen6(); + } + romHandler.printMoveUpdates(); + } + + List moves = romHandler.getMoves(); + + // Trade evolutions removal + if (this.goRemoveTradeEvosCheckBox.isSelected()) { + romHandler.removeTradeEvolutions(!this.pmsUnchangedRB + .isSelected()); + } + + // Camel case? + if (!(romHandler instanceof Gen5RomHandler) + && !(romHandler instanceof Gen4RomHandler) + && this.goLowerCaseNamesCheckBox.isSelected()) { + romHandler.applyCamelCaseNames(); + } + + // National dex gen3? + if (romHandler instanceof Gen3RomHandler + && this.goNationalDexCheckBox.isSelected()) { + romHandler.patchForNationalDex(); + } + + // Code Tweaks? + if (romHandler.codeTweaksAvailable() != 0) { + int ctavailable = romHandler.codeTweaksAvailable(); + if ((ctavailable & CodeTweaks.BW_EXP_PATCH) > 0 + && (currentCodeTweaks & CodeTweaks.BW_EXP_PATCH) > 0) { + romHandler.applyBWEXPPatch(); + } + + if ((ctavailable & CodeTweaks.FIX_CRIT_RATE) > 0 + && (currentCodeTweaks & CodeTweaks.FIX_CRIT_RATE) > 0) { + romHandler.applyCritRatePatch(); + } + + if ((ctavailable & CodeTweaks.NERF_X_ACCURACY) > 0 + && (currentCodeTweaks & CodeTweaks.NERF_X_ACCURACY) > 0) { + romHandler.applyXAccNerfPatch(); + } + } + + // Hollows? + if (romHandler.hasHiddenHollowPokemon() + && this.randomizeHollowsCB.isSelected()) { + romHandler.randomizeHiddenHollowPokemon(); + } + + List allPokes = romHandler.getPokemon(); + + // Base stats changing + if (this.pbsChangesShuffleRB.isSelected()) { + romHandler.shufflePokemonStats(); + } else if (this.pbsChangesRandomEvosRB.isSelected()) { + romHandler.randomizePokemonStats(true); + } else if (this.pbsChangesRandomTotalRB.isSelected()) { + romHandler.randomizePokemonStats(false); + } + + if (this.pbsStandardEXPCurvesCB.isSelected()) { + romHandler.standardizeEXPCurves(); + } + + // Abilities? (new 1.0.2) + if (this.romHandler.abilitiesPerPokemon() > 0 + && this.paRandomizeRB.isSelected()) { + romHandler + .randomizeAbilities(this.paWonderGuardCB.isSelected()); + } + + // Pokemon Types + if (this.ptRandomFollowEvosRB.isSelected()) { + romHandler.randomizePokemonTypes(true); + } else if (this.ptRandomTotalRB.isSelected()) { + romHandler.randomizePokemonTypes(false); + } + + // Wild Held Items? + String[] itemNames = romHandler.getItemNames(); + if (this.wpHeldItemsCB.isSelected()) { + romHandler.randomizeWildHeldItems(); + } + + // Log base stats & types if changed at all + if (this.pbsChangesUnchangedRB.isSelected() + && this.ptUnchangedRB.isSelected() + && this.paUnchangedRB.isSelected() + && this.wpHeldItemsCB.isSelected() == false) { + verboseLog.println("Pokemon base stats & type: unchanged" + nl); + } else { + verboseLog.println("--Pokemon Base Stats & Types--"); + if (romHandler instanceof Gen1RomHandler) { + verboseLog + .println("NUM|NAME |TYPE | HP| ATK| DEF| SPE|SPEC"); + for (Pokemon pkmn : allPokes) { + if (pkmn != null) { + String typeString = pkmn.primaryType == null ? "NULL" + : pkmn.primaryType.toString(); + if (pkmn.secondaryType != null) { + typeString += "/" + + (pkmn.secondaryType == null ? "NULL" + : pkmn.secondaryType.toString()); + } + verboseLog.printf( + "%3d|%-10s|%-17s|%4d|%4d|%4d|%4d|%4d" + nl, + pkmn.number, pkmn.name, typeString, + pkmn.hp, pkmn.attack, pkmn.defense, + pkmn.speed, pkmn.special); + } + + } + } else { + verboseLog + .print("NUM|NAME |TYPE | HP| ATK| DEF| SPE|SATK|SDEF"); + int abils = romHandler.abilitiesPerPokemon(); + for (int i = 0; i < abils; i++) { + verboseLog.print("|ABILITY" + (i + 1) + " "); + } + verboseLog.print("|ITEM"); + verboseLog.println(); + for (Pokemon pkmn : allPokes) { + if (pkmn != null) { + String typeString = pkmn.primaryType.toString(); + if (pkmn.secondaryType != null) { + typeString += "/" + + pkmn.secondaryType.toString(); + } + verboseLog.printf( + "%3d|%-10s|%-17s|%4d|%4d|%4d|%4d|%4d|%4d", + pkmn.number, pkmn.name, typeString, + pkmn.hp, pkmn.attack, pkmn.defense, + pkmn.speed, pkmn.spatk, pkmn.spdef); + if (abils > 0) { + verboseLog.printf("|%-12s|%-12s", + romHandler.abilityName(pkmn.ability1), + romHandler.abilityName(pkmn.ability2)); + if (abils > 2) { + verboseLog.printf("|%-12s", romHandler + .abilityName(pkmn.ability3)); + } + } + verboseLog.print("|"); + if (pkmn.guaranteedHeldItem > 0) { + verboseLog + .print(itemNames[pkmn.guaranteedHeldItem] + + " (100%)"); + } else { + int itemCount = 0; + if (pkmn.commonHeldItem > 0) { + itemCount++; + verboseLog + .print(itemNames[pkmn.commonHeldItem] + + " (common)"); + } + if (pkmn.rareHeldItem > 0) { + if (itemCount > 0) { + verboseLog.print(", "); + } + itemCount++; + verboseLog + .print(itemNames[pkmn.rareHeldItem] + + " (rare)"); + } + if (pkmn.darkGrassHeldItem > 0) { + if (itemCount > 0) { + verboseLog.print(", "); + } + itemCount++; + verboseLog + .print(itemNames[pkmn.darkGrassHeldItem] + + " (dark grass only)"); + } + } + verboseLog.println(); + } + + } + } + if (raceMode) { + for (Pokemon pkmn : allPokes) { + if (pkmn != null) { + checkValue = addToCV(checkValue, pkmn.hp, + pkmn.attack, pkmn.defense, pkmn.speed, + pkmn.spatk, pkmn.spdef, pkmn.ability1, + pkmn.ability2, pkmn.ability3); + } + } + } + verboseLog.println(); + } + + // Starter Pokemon + // Applied after type to update the strings correctly based on new + // types + if (romHandler.canChangeStarters()) { + if (this.spCustomRB.isSelected()) { + verboseLog.println("--Custom Starters--"); + Pokemon pkmn1 = allPokes.get(this.spCustomPoke1Chooser + .getSelectedIndex() + 1); + verboseLog.println("Set starter 1 to " + pkmn1.name); + Pokemon pkmn2 = allPokes.get(this.spCustomPoke2Chooser + .getSelectedIndex() + 1); + verboseLog.println("Set starter 2 to " + pkmn2.name); + if (romHandler.isYellow()) { + romHandler.setStarters(Arrays.asList(pkmn1, pkmn2)); + } else { + Pokemon pkmn3 = allPokes.get(this.spCustomPoke3Chooser + .getSelectedIndex() + 1); + verboseLog.println("Set starter 3 to " + pkmn3.name); + romHandler.setStarters(Arrays.asList(pkmn1, pkmn2, + pkmn3)); + } + verboseLog.println(); + + } else if (this.spRandomRB.isSelected()) { + // Randomise + verboseLog.println("--Random Starters--"); + int starterCount = 3; + if (romHandler.isYellow()) { + starterCount = 2; + } + List starters = new ArrayList(); + for (int i = 0; i < starterCount; i++) { + Pokemon pkmn = romHandler.randomPokemon(); + while (starters.contains(pkmn)) { + pkmn = romHandler.randomPokemon(); + } + verboseLog.println("Set starter " + (i + 1) + " to " + + pkmn.name); + starters.add(pkmn); + } + romHandler.setStarters(starters); + verboseLog.println(); + } else if (this.spRandom2EvosRB.isSelected()) { + // Randomise + verboseLog.println("--Random 2-Evolution Starters--"); + int starterCount = 3; + if (romHandler.isYellow()) { + starterCount = 2; + } + List starters = new ArrayList(); + for (int i = 0; i < starterCount; i++) { + Pokemon pkmn = romHandler.random2EvosPokemon(); + while (starters.contains(pkmn)) { + pkmn = romHandler.random2EvosPokemon(); + } + verboseLog.println("Set starter " + (i + 1) + " to " + + pkmn.name); + starters.add(pkmn); + } + romHandler.setStarters(starters); + verboseLog.println(); + } + if (this.spHeldItemsCB.isSelected() + && (romHandler instanceof Gen1RomHandler) == false) { + romHandler.randomizeStarterHeldItems(); + } + } + + // Movesets + boolean noBrokenMoves = this.brokenMovesCB.isSelected(); + boolean forceFourLv1s = romHandler.supportsFourStartingMoves() + && this.pms4MovesCB.isSelected(); + if (this.pmsRandomTypeRB.isSelected()) { + romHandler.randomizeMovesLearnt(true, noBrokenMoves, + forceFourLv1s); + } else if (this.pmsRandomTotalRB.isSelected()) { + romHandler.randomizeMovesLearnt(false, noBrokenMoves, + forceFourLv1s); + } + + // Show the new movesets if applicable + if (this.pmsUnchangedRB.isSelected()) { + verboseLog.println("Pokemon Movesets: Unchanged." + nl); + } else if (this.pmsMetronomeOnlyRB.isSelected()) { + verboseLog.println("Pokemon Movesets: Metronome Only." + nl); + } else { + verboseLog.println("--Pokemon Movesets--"); + List movesets = new ArrayList(); + Map> moveData = romHandler + .getMovesLearnt(); + for (Pokemon pkmn : moveData.keySet()) { + StringBuilder sb = new StringBuilder(); + sb.append(String.format("%03d %-10s : ", pkmn.number, + pkmn.name)); + List data = moveData.get(pkmn); + boolean first = true; + for (MoveLearnt ml : data) { + if (!first) { + sb.append(", "); + } + + sb.append(moves.get(ml.move).name + " at level " + + ml.level); + first = false; + } + movesets.add(sb.toString()); + } + Collections.sort(movesets); + for (String moveset : movesets) { + verboseLog.println(moveset); + } + verboseLog.println(); + } + + // Trainer Pokemon + if (this.tpRandomRB.isSelected()) { + romHandler.randomizeTrainerPokes( + this.tpRivalCarriesStarterCB.isSelected(), + this.tpPowerLevelsCB.isSelected(), + this.tpNoLegendariesCB.isSelected(), + this.tpNoEarlyShedinjaCB.isSelected()); + } else if (this.tpTypeThemedRB.isSelected()) { + romHandler.typeThemeTrainerPokes( + this.tpRivalCarriesStarterCB.isSelected(), + this.tpPowerLevelsCB.isSelected(), + this.tpTypeWeightingCB.isSelected(), + this.tpNoLegendariesCB.isSelected(), + this.tpNoEarlyShedinjaCB.isSelected()); + } + + // Trainer names & class names randomization + // done before trainer log to add proper names + + if (this.tcnRandomizeCB.isSelected()) { + romHandler.randomizeTrainerClassNames(trainerClasses); + } + + if (this.tnRandomizeCB.isSelected()) { + romHandler.randomizeTrainerNames(trainerNames); + } + + if (this.tpUnchangedRB.isSelected()) { + verboseLog.println("Trainers: Unchanged." + nl); + } else { + verboseLog.println("--Trainers Pokemon--"); + List trainers = romHandler.getTrainers(); + int idx = 0; + for (Trainer t : trainers) { + idx++; + verboseLog.print("#" + idx + " "); + if (t.fullDisplayName != null) { + verboseLog.print("(" + t.fullDisplayName + ")"); + } else if (t.name != null) { + verboseLog.print("(" + t.name + ")"); + } + if (t.offset != idx && t.offset != 0) { + verboseLog.printf("@%X", t.offset); + } + verboseLog.print(" - "); + boolean first = true; + for (TrainerPokemon tpk : t.pokemon) { + if (!first) { + verboseLog.print(", "); + } + verboseLog.print(tpk.pokemon.name + " Lv" + tpk.level); + first = false; + } + verboseLog.println(); + } + verboseLog.println(); + } + + // Apply metronome only mode now that trainers have been dealt with + if (pmsMetronomeOnlyRB.isSelected()) { + romHandler.metronomeOnlyMode(); + } + + if (raceMode) { + List trainers = romHandler.getTrainers(); + for (Trainer t : trainers) { + for (TrainerPokemon tpk : t.pokemon) { + checkValue = addToCV(checkValue, tpk.level, + tpk.pokemon.number); + } + } + } + + // Wild Pokemon + // actually call this code (Kappa) + if (this.wpCatchRateCB.isSelected()) { + if (romHandler instanceof Gen5RomHandler) { + romHandler.minimumCatchRate(50, 25); + } else { + romHandler.minimumCatchRate(75, 37); + } + } + if (this.wpRandomRB.isSelected()) { + romHandler.randomEncounters(this.wpUseTimeCB.isSelected(), + this.wpARCatchEmAllRB.isSelected(), + this.wpARTypeThemedRB.isSelected(), + this.wpARSimilarStrengthRB.isSelected(), + this.wpNoLegendariesCB.isSelected()); + } else if (this.wpArea11RB.isSelected()) { + romHandler.area1to1Encounters(this.wpUseTimeCB.isSelected(), + this.wpARCatchEmAllRB.isSelected(), + this.wpARTypeThemedRB.isSelected(), + this.wpARSimilarStrengthRB.isSelected(), + this.wpNoLegendariesCB.isSelected()); + } else if (this.wpGlobalRB.isSelected()) { + romHandler.game1to1Encounters(this.wpUseTimeCB.isSelected(), + this.wpARSimilarStrengthRB.isSelected(), + this.wpNoLegendariesCB.isSelected()); + } + + if (this.wpUnchangedRB.isSelected()) { + verboseLog.println("Wild Pokemon: Unchanged." + nl); + } else { + verboseLog.println("--Wild Pokemon--"); + List encounters = romHandler + .getEncounters(this.wpUseTimeCB.isSelected()); + int idx = 0; + for (EncounterSet es : encounters) { + idx++; + verboseLog.print("Set #" + idx + " "); + if (es.displayName != null) { + verboseLog.print("- " + es.displayName + " "); + } + verboseLog.print("(rate=" + es.rate + ")"); + verboseLog.print(" - "); + boolean first = true; + for (Encounter e : es.encounters) { + if (!first) { + verboseLog.print(", "); + } + verboseLog.print(e.pokemon.name + " Lv"); + if (e.maxLevel > 0 && e.maxLevel != e.level) { + verboseLog.print("s " + e.level + "-" + e.maxLevel); + } else { + verboseLog.print(e.level); + } + first = false; + } + verboseLog.println(); + } + verboseLog.println(); + } + + if (raceMode) { + List encounters = romHandler + .getEncounters(this.wpUseTimeCB.isSelected()); + for (EncounterSet es : encounters) { + for (Encounter e : es.encounters) { + checkValue = addToCV(checkValue, e.level, + e.pokemon.number); + } + } + } + + // Static Pokemon + + if (romHandler.canChangeStaticPokemon()) { + List oldStatics = romHandler.getStaticPokemon(); + if (this.stpRandomL4LRB.isSelected()) { + romHandler.randomizeStaticPokemon(true); + } else if (this.stpRandomTotalRB.isSelected()) { + romHandler.randomizeStaticPokemon(false); + } + List newStatics = romHandler.getStaticPokemon(); + if (this.stpUnchangedRB.isSelected()) { + verboseLog.println("Static Pokemon: Unchanged." + nl); + } else { + verboseLog.println("--Static Pokemon--"); + Map seenPokemon = new TreeMap(); + for (int i = 0; i < oldStatics.size(); i++) { + Pokemon oldP = oldStatics.get(i); + Pokemon newP = newStatics.get(i); + if (raceMode) { + checkValue = addToCV(checkValue, newP.number); + } + verboseLog.print(oldP.name); + if (seenPokemon.containsKey(oldP)) { + int amount = seenPokemon.get(oldP); + verboseLog.print("(" + (++amount) + ")"); + seenPokemon.put(oldP, amount); + } else { + seenPokemon.put(oldP, 1); + } + verboseLog.println(" => " + newP.name); + } + verboseLog.println(); + } + } + + // TMs + if (!pmsMetronomeOnlyRB.isSelected() + && this.tmmRandomRB.isSelected()) { + romHandler.randomizeTMMoves(noBrokenMoves); + verboseLog.println("--TM Moves--"); + List tmMoves = romHandler.getTMMoves(); + for (int i = 0; i < tmMoves.size(); i++) { + verboseLog.printf("TM%02d %s" + nl, i + 1, + moves.get(tmMoves.get(i)).name); + if (raceMode) { + checkValue = addToCV(checkValue, tmMoves.get(i)); + } + } + verboseLog.println(); + } else if (pmsMetronomeOnlyRB.isSelected()) { + verboseLog.println("TM Moves: Metronome Only." + nl); + } else { + verboseLog.println("TM Moves: Unchanged." + nl); + } + + // TM/HM compatibility + if (this.thcRandomTypeRB.isSelected()) { + romHandler.randomizeTMHMCompatibility(true); + } else if (this.thcRandomTotalRB.isSelected()) { + romHandler.randomizeTMHMCompatibility(false); + } + + // Move Tutors (new 1.0.3) + if (this.romHandler.hasMoveTutors()) { + if (!pmsMetronomeOnlyRB.isSelected() + && this.mtmRandomRB.isSelected()) { + List oldMtMoves = romHandler.getMoveTutorMoves(); + romHandler.randomizeMoveTutorMoves(noBrokenMoves); + verboseLog.println("--Move Tutor Moves--"); + List newMtMoves = romHandler.getMoveTutorMoves(); + for (int i = 0; i < newMtMoves.size(); i++) { + verboseLog.printf("%s => %s" + nl, + moves.get(oldMtMoves.get(i)).name, + moves.get(newMtMoves.get(i)).name); + if (raceMode) { + checkValue = addToCV(checkValue, newMtMoves.get(i)); + } + } + verboseLog.println(); + } else if (pmsMetronomeOnlyRB.isSelected()) { + verboseLog + .println("Move Tutor Moves: Metronome Only." + nl); + } else { + verboseLog.println("Move Tutor Moves: Unchanged." + nl); + } + + // Compatibility + if (this.mtcRandomTypeRB.isSelected()) { + romHandler.randomizeMoveTutorCompatibility(true); + } else if (this.mtcRandomTotalRB.isSelected()) { + romHandler.randomizeMoveTutorCompatibility(false); + } + } + + // In-game trades + List oldTrades = romHandler.getIngameTrades(); + if (this.igtGivenOnlyRB.isSelected()) { + romHandler.randomizeIngameTrades(false, nicknames, + this.igtRandomNicknameCB.isSelected(), trainerNames, + this.igtRandomOTCB.isSelected(), + this.igtRandomIVsCB.isSelected(), + this.igtRandomItemCB.isSelected()); + } else if (this.igtBothRB.isSelected()) { + romHandler.randomizeIngameTrades(true, nicknames, + this.igtRandomNicknameCB.isSelected(), trainerNames, + this.igtRandomOTCB.isSelected(), + this.igtRandomIVsCB.isSelected(), + this.igtRandomItemCB.isSelected()); + } + + if (!this.igtUnchangedRB.isSelected()) { + verboseLog.println("--In-Game Trades--"); + List newTrades = romHandler.getIngameTrades(); + int size = oldTrades.size(); + for (int i = 0; i < size; i++) { + IngameTrade oldT = oldTrades.get(i); + IngameTrade newT = newTrades.get(i); + verboseLog.printf( + "Trading %s for %s the %s has become trading %s for %s the %s" + + nl, oldT.requestedPokemon.name, + oldT.nickname, oldT.givenPokemon.name, + newT.requestedPokemon.name, newT.nickname, + newT.givenPokemon.name); + } + verboseLog.println(); + } + + // Field Items + if (this.fiShuffleRB.isSelected()) { + romHandler.shuffleFieldItems(); + } else if (this.fiRandomRB.isSelected()) { + romHandler.randomizeFieldItems(); + } + + // Signature... + romHandler.applySignature(); + + // Save + final int finishedCV = checkValue; + opDialog = new OperationDialog( + bundle.getString("RandomizerGUI.savingText"), this, true); + Thread t = new Thread() { + @Override + public void run() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + opDialog.setVisible(true); + } + }); + boolean succeededSave = false; + try { + RandomizerGUI.this.romHandler.saveRom(filename); + succeededSave = true; + } catch (Exception ex) { + long time = System.currentTimeMillis(); + try { + String errlog = "error_" + time + ".txt"; + PrintStream ps = new PrintStream( + new FileOutputStream(errlog)); + PrintStream e1 = System.err; + System.setErr(ps); + ex.printStackTrace(); + verboseLog.close(); + System.setErr(e1); + ps.close(); + JOptionPane + .showMessageDialog( + RandomizerGUI.this, + String.format( + bundle.getString("RandomizerGUI.saveFailedIO"), + errlog)); + } catch (Exception logex) { + JOptionPane + .showMessageDialog( + RandomizerGUI.this, + bundle.getString("RandomizerGUI.saveFailedIONoLog")); + verboseLog.close(); + } + } + if (succeededSave) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + RandomizerGUI.this.opDialog.setVisible(false); + // Log tail + verboseLog + .println("------------------------------------------------------------------"); + verboseLog.println("Randomization of " + + romHandler.getROMName() + + " completed."); + verboseLog.println("Time elapsed: " + + (System.currentTimeMillis() - startTime) + + "ms"); + verboseLog.println("RNG Calls: " + + RandomSource.callsSinceSeed()); + verboseLog + .println("------------------------------------------------------------------"); + + // Log? + verboseLog.close(); + byte[] out = baos.toByteArray(); + verboseLog = System.out; + + if (raceMode) { + JOptionPane.showMessageDialog( + RandomizerGUI.this, + String.format( + bundle.getString("RandomizerGUI.raceModeCheckValuePopup"), + finishedCV)); + } else { + int response = JOptionPane.showConfirmDialog( + RandomizerGUI.this, + bundle.getString("RandomizerGUI.saveLogDialog.text"), + bundle.getString("RandomizerGUI.saveLogDialog.title"), + JOptionPane.YES_NO_OPTION); + if (response == JOptionPane.YES_OPTION) { + try { + FileOutputStream fos = new FileOutputStream( + filename + ".log"); + fos.write(0xEF); + fos.write(0xBB); + fos.write(0xBF); + fos.write(out); + fos.close(); + } catch (IOException e) { + JOptionPane.showMessageDialog( + RandomizerGUI.this, + bundle.getString("RandomizerGUI.logSaveFailed")); + return; + } + JOptionPane.showMessageDialog( + RandomizerGUI.this, + String.format( + bundle.getString("RandomizerGUI.logSaved"), + filename)); + } + } + if (presetMode) { + JOptionPane.showMessageDialog( + RandomizerGUI.this, + bundle.getString("RandomizerGUI.randomizationDone")); + // Done + RandomizerGUI.this.romHandler = null; + initialFormState(); + } else { + // Compile a config string + String configString = getConfigString(); + // Show the preset maker + new PresetMakeDialog(RandomizerGUI.this, + seed, configString); + + // Done + RandomizerGUI.this.romHandler = null; + initialFormState(); + } + } + }); + } else { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + RandomizerGUI.this.opDialog.setVisible(false); + verboseLog = System.out; + RandomizerGUI.this.romHandler = null; + initialFormState(); + } + }); + } + } + }; + t.start(); + } catch (Exception ex) { + long time = System.currentTimeMillis(); + try { + String errlog = "error_" + time + ".txt"; + PrintStream ps = new PrintStream(new FileOutputStream(errlog)); + PrintStream e1 = System.err; + System.setErr(ps); + ex.printStackTrace(); + verboseLog.close(); + byte[] out = baos.toByteArray(); + System.err.print(new String(out, "UTF-8")); + System.setErr(e1); + ps.close(); + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.saveFailed"), errlog)); + } catch (Exception logex) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.saveFailedNoLog")); + verboseLog.close(); + } + } + + } + + private void presetLoader() { + PresetLoadDialog pld = new PresetLoadDialog(this); + if (pld.isCompleted()) { + // Apply it + long seed = pld.getSeed(); + String config = pld.getConfigString(); + this.romHandler = pld.getROM(); + this.romLoaded(); + this.restoreFrom(config); + romSaveChooser.setSelectedFile(null); + int returnVal = romSaveChooser.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File fh = romSaveChooser.getSelectedFile(); + // Fix or add extension + List extensions = new ArrayList(Arrays.asList( + "sgb", "gbc", "gba", "nds")); + extensions.remove(this.romHandler.getDefaultExtension()); + fh = FileFunctions.fixFilename(fh, + this.romHandler.getDefaultExtension(), extensions); + boolean allowed = true; + if (this.romHandler instanceof AbstractDSRomHandler) { + String currentFN = this.romHandler.loadedFilename(); + if (currentFN.equals(fh.getAbsolutePath())) { + JOptionPane.showMessageDialog(this, bundle + .getString("RandomizerGUI.cantOverwriteDS")); + allowed = false; + } + } + if (allowed) { + // Apply the seed we were given + RandomSource.seed(seed); + presetMode = true; + performRandomization(fh.getAbsolutePath(), seed, + pld.getTrainerClasses(), pld.getTrainerNames(), + pld.getNicknames()); + } else { + this.romHandler = null; + initialFormState(); + } + + } else { + this.romHandler = null; + initialFormState(); + } + } + + } + + // helper methods + + private boolean checkOtherCRC(byte[] data, int byteIndex, int switchIndex, + String filename, int offsetInData) { + // If the switch at data[byteIndex].switchIndex is on, + // then check that the CRC at + // data[offsetInData] ... data[offsetInData+3] + // matches the CRC of filename. + // If not, return false. + // If any other case, return true. + int switches = data[byteIndex] & 0xFF; + if (((switches >> switchIndex) & 0x01) == 0x01) { + // have to check the CRC + int crc = readFullInt(data, offsetInData); + + if (getFileChecksum(filename) != crc) { + return false; + } + } + return true; + } + + private void restoreSelectedIndex(byte[] data, int offset, + JComboBox comboBox) { + int selIndex = (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8); + if (comboBox.getModel().getSize() > selIndex) { + comboBox.setSelectedIndex(selIndex); + } else if (this.spCustomRB.isSelected()) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.starterUnavailable")); + } + } + + private void restoreStates(byte b, AbstractButton... switches) { + int value = b & 0xFF; + for (int i = 0; i < switches.length; i++) { + int realValue = (value >> i) & 0x01; + switches[i].setSelected(realValue == 0x01); + } + } + + private int readFullInt(byte[] data, int offset) { + ByteBuffer buf = ByteBuffer.allocate(4).put(data, offset, 4); + buf.rewind(); + return buf.getInt(); + } + + private void writeFullInt(ByteArrayOutputStream baos, int checksum) + throws IOException { + byte[] crc = ByteBuffer.allocate(4).putInt(checksum).array(); + baos.write(crc); + + } + + private void writePokemonIndex(ByteArrayOutputStream baos, + JComboBox comboBox) { + baos.write(comboBox.getSelectedIndex() & 0xFF); + baos.write((comboBox.getSelectedIndex() >> 8) & 0xFF); + + } + + private int makeByteSelected(AbstractButton... switches) { + if (switches.length > 8) { + // No can do + return 0; + } + int initial = 0; + int state = 1; + for (AbstractButton b : switches) { + initial |= b.isSelected() ? state : 0; + state *= 2; + } + return initial; + } + + private int addToCV(int checkValue, int... values) { + for (int value : values) { + checkValue = Integer.rotateLeft(checkValue, 3); + checkValue ^= value; + } + return checkValue; + } + + private void updateCodeTweaksButtonText() { + if (currentCodeTweaks == 0 || !codeTweaksCB.isSelected()) { + codeTweaksBtn.setText(bundle + .getString("RandomizerGUI.codeTweaksBtn.text")); + } else { + int ctCount = 0; + for (int i = 0; i < 32; i++) { + if ((currentCodeTweaks & (1 << i)) > 0) { + ctCount++; + } + } + codeTweaksBtn.setText(String.format(bundle + .getString("RandomizerGUI.codeTweaksBtn.textWithActive"), + ctCount)); + } + } + + // public response methods + + public void updateFound(int newVersion, String changelog) { + new UpdateFoundDialog(this, newVersion, changelog); + } + + public void noUpdateFound() { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.noUpdates")); + } + + public static String getRootPath() { + return rootPath; + } + + // actions + + private void updateSettingsButtonActionPerformed( + java.awt.event.ActionEvent evt) {// GEN-FIRST:event_updateSettingsButtonActionPerformed + if (autoUpdateEnabled) { + toggleAutoUpdatesMenuItem.setText(bundle + .getString("RandomizerGUI.disableAutoUpdate")); + } else { + toggleAutoUpdatesMenuItem.setText(bundle + .getString("RandomizerGUI.enableAutoUpdate")); + } + updateSettingsMenu.show(updateSettingsButton, 0, + updateSettingsButton.getHeight()); + }// GEN-LAST:event_updateSettingsButtonActionPerformed + + private void toggleAutoUpdatesMenuItemActionPerformed( + java.awt.event.ActionEvent evt) {// GEN-FIRST:event_toggleAutoUpdatesMenuItemActionPerformed + autoUpdateEnabled = !autoUpdateEnabled; + if (autoUpdateEnabled) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.autoUpdateEnabled")); + } else { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.autoUpdateDisabled")); + } + attemptWriteConfig(); + }// GEN-LAST:event_toggleAutoUpdatesMenuItemActionPerformed + + private void manualUpdateMenuItemActionPerformed( + java.awt.event.ActionEvent evt) {// GEN-FIRST:event_manualUpdateMenuItemActionPerformed + new UpdateCheckThread(this, true).start(); + }// GEN-LAST:event_manualUpdateMenuItemActionPerformed + + private void loadQSButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_loadQSButtonActionPerformed + if (this.romHandler == null) { + return; + } + qsOpenChooser.setSelectedFile(null); + int returnVal = qsOpenChooser.showOpenDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File fh = qsOpenChooser.getSelectedFile(); + try { + FileInputStream fis = new FileInputStream(fh); + int version = fis.read(); + if (version < PRESET_FILE_VERSION) { + JOptionPane + .showMessageDialog( + this, + bundle.getString("RandomizerGUI.settingsFileOlder")); + fis.close(); + return; + } else if (version > PRESET_FILE_VERSION) { + JOptionPane + .showMessageDialog( + this, + bundle.getString("RandomizerGUI.settingsFileNewer")); + fis.close(); + return; + } + int cslength = fis.read(); + byte[] csBuf = new byte[cslength]; + fis.read(csBuf); + fis.close(); + String configString = new String(csBuf, "UTF-8"); + String romName = getValidRequiredROMName(configString, + new byte[] {}, new byte[] {}, new byte[] {}); + if (romName == null) { + JOptionPane.showMessageDialog(this, bundle + .getString("RandomizerGUI.invalidSettingsFile")); + } + // now we just load it + initialFormState(); + romLoaded(); + restoreFrom(configString); + JCheckBox[] checkboxes = new JCheckBox[] { this.brokenMovesCB, + this.codeTweaksCB, this.goLowerCaseNamesCheckBox, + this.goNationalDexCheckBox, + this.goRemoveTradeEvosCheckBox, + this.goUpdateMovesCheckBox, this.goUpdateTypesCheckBox, + this.spHeldItemsCB, this.paWonderGuardCB, + this.raceModeCB, this.randomizeHollowsCB, + this.tcnRandomizeCB, this.tnRandomizeCB, + this.tpNoEarlyShedinjaCB, this.tpNoLegendariesCB, + this.tpPowerLevelsCB, this.tpRivalCarriesStarterCB, + this.tpTypeWeightingCB, this.wpCatchRateCB, + this.wpNoLegendariesCB, this.wpUseTimeCB, + this.igtRandomItemCB, this.igtRandomIVsCB, + this.igtRandomNicknameCB, this.igtRandomOTCB }; + for (JCheckBox cb : checkboxes) { + if (!cb.isEnabled() || !cb.isVisible()) { + cb.setSelected(false); + } + } + + if (!this.romHandler.canChangeStaticPokemon()) { + this.stpUnchangedRB.setSelected(true); + } + + JOptionPane.showMessageDialog(this, String.format( + bundle.getString("RandomizerGUI.settingsLoaded"), + fh.getName())); + } catch (IOException ex) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.settingsLoadFailed")); + } catch (InvalidSupplementFilesException e) { + // not possible + } + } + }// GEN-LAST:event_loadQSButtonActionPerformed + + private void saveQSButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_saveQSButtonActionPerformed + if (this.romHandler == null) { + return; + } + qsSaveChooser.setSelectedFile(null); + int returnVal = qsSaveChooser.showSaveDialog(this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File fh = qsSaveChooser.getSelectedFile(); + // Fix or add extension + fh = FileFunctions.fixFilename(fh, "rnqs"); + // Save now? + try { + FileOutputStream fos = new FileOutputStream(fh); + fos.write(PRESET_FILE_VERSION); + byte[] configString = getConfigString().getBytes("UTF-8"); + fos.write(configString.length); + fos.write(configString); + fos.close(); + } catch (IOException ex) { + JOptionPane.showMessageDialog(this, + bundle.getString("RandomizerGUI.settingsSaveFailed")); + } + } + }// GEN-LAST:event_saveQSButtonActionPerformed + + private void codeTweaksBtnActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_codeTweaksBtnActionPerformed + CodeTweaksDialog ctd = new CodeTweaksDialog(this, + this.currentCodeTweaks, this.romHandler.codeTweaksAvailable()); + if (ctd.pressedOK()) { + this.currentCodeTweaks = ctd.getChoice(); + updateCodeTweaksButtonText(); + } + }// GEN-LAST:event_codeTweaksBtnActionPerformed + + private void pokeLimitBtnActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_pokeLimitBtnActionPerformed + GenerationLimitDialog gld = new GenerationLimitDialog(this, + this.currentRestrictions, this.romHandler.generationOfPokemon()); + if (gld.pressedOK()) { + this.currentRestrictions = gld.getChoice(); + } + }// GEN-LAST:event_pokeLimitBtnActionPerformed + + private void goUpdateMovesCheckBoxActionPerformed( + java.awt.event.ActionEvent evt) {// GEN-FIRST:event_goUpdateMovesCheckBoxActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_goUpdateMovesCheckBoxActionPerformed + + private void codeTweaksCBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_codeTweaksCBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_codeTweaksCBActionPerformed + + private void pokeLimitCBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_pokeLimitCBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_pokeLimitCBActionPerformed + + private void pmsMetronomeOnlyRBActionPerformed( + java.awt.event.ActionEvent evt) {// GEN-FIRST:event_pmsMetronomeOnlyRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_pmsMetronomeOnlyRBActionPerformed + + private void igtUnchangedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_igtUnchangedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_igtUnchangedRBActionPerformed + + private void igtGivenOnlyRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_igtGivenOnlyRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_igtGivenOnlyRBActionPerformed + + private void igtBothRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_igtBothRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_igtBothRBActionPerformed + + private void wpARNoneRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpARNoneRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpARNoneRBActionPerformed + + private void wpARSimilarStrengthRBActionPerformed( + java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpARSimilarStrengthRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpARSimilarStrengthRBActionPerformed + + private void wpARCatchEmAllRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpARCatchEmAllRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpARCatchEmAllRBActionPerformed + + private void wpARTypeThemedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpARTypeThemedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpARTypeThemedRBActionPerformed + + private void pmsUnchangedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_pmsUnchangedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_pmsUnchangedRBActionPerformed + + private void pmsRandomTypeRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_pmsRandomTypeRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_pmsRandomTypeRBActionPerformed + + private void pmsRandomTotalRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_pmsRandomTotalRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_pmsRandomTotalRBActionPerformed + + private void mtmUnchangedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_mtmUnchangedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_mtmUnchangedRBActionPerformed + + private void paUnchangedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_paUnchangedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_paUnchangedRBActionPerformed + + private void paRandomizeRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_paRandomizeRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_paRandomizeRBActionPerformed + + private void aboutButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_aboutButtonActionPerformed + new AboutDialog(this, true).setVisible(true); + }// GEN-LAST:event_aboutButtonActionPerformed + + private void openROMButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_openROMButtonActionPerformed + loadROM(); + }// GEN-LAST:event_openROMButtonActionPerformed + + private void saveROMButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_saveROMButtonActionPerformed + saveROM(); + }// GEN-LAST:event_saveROMButtonActionPerformed + + private void usePresetsButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_usePresetsButtonActionPerformed + presetLoader(); + }// GEN-LAST:event_usePresetsButtonActionPerformed + + private void wpUnchangedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpUnchangedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpUnchangedRBActionPerformed + + private void tpUnchangedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_tpUnchangedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_tpUnchangedRBActionPerformed + + private void tpRandomRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_tpRandomRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_tpRandomRBActionPerformed + + private void tpTypeThemedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_tpTypeThemedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_tpTypeThemedRBActionPerformed + + private void spUnchangedRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_spUnchangedRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_spUnchangedRBActionPerformed + + private void spCustomRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_spCustomRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_spCustomRBActionPerformed + + private void spRandomRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_spRandomRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_spRandomRBActionPerformed + + private void wpRandomRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpRandomRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpRandomRBActionPerformed + + private void wpArea11RBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpArea11RBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpArea11RBActionPerformed + + private void wpGlobalRBActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_wpGlobalRBActionPerformed + this.enableOrDisableSubControls(); + }// GEN-LAST:event_wpGlobalRBActionPerformed + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + pokeStatChangesButtonGroup = new javax.swing.ButtonGroup(); + pokeTypesButtonGroup = new javax.swing.ButtonGroup(); + pokeMovesetsButtonGroup = new javax.swing.ButtonGroup(); + trainerPokesButtonGroup = new javax.swing.ButtonGroup(); + wildPokesButtonGroup = new javax.swing.ButtonGroup(); + wildPokesARuleButtonGroup = new javax.swing.ButtonGroup(); + starterPokemonButtonGroup = new javax.swing.ButtonGroup(); + romOpenChooser = new javax.swing.JFileChooser(); + romSaveChooser = new JFileChooser() { + + private static final long serialVersionUID = 3244234325234511L; + + public void approveSelection() { + File fh = getSelectedFile(); + // Fix or add extension + List extensions = new ArrayList(Arrays.asList( + "sgb", "gbc", "gba", "nds")); + extensions.remove(RandomizerGUI.this.romHandler + .getDefaultExtension()); + fh = FileFunctions.fixFilename(fh, + RandomizerGUI.this.romHandler.getDefaultExtension(), + extensions); + if (fh.exists() && getDialogType() == SAVE_DIALOG) { + int result = JOptionPane.showConfirmDialog(this, + "The file exists, overwrite?", "Existing file", + JOptionPane.YES_NO_CANCEL_OPTION); + switch (result) { + case JOptionPane.YES_OPTION: + super.approveSelection(); + return; + case JOptionPane.CANCEL_OPTION: + cancelSelection(); + return; + default: + return; + } + } + super.approveSelection(); + } + }; + qsOpenChooser = new javax.swing.JFileChooser(); + qsSaveChooser = new javax.swing.JFileChooser(); + staticPokemonButtonGroup = new javax.swing.ButtonGroup(); + tmMovesButtonGroup = new javax.swing.ButtonGroup(); + tmHmCompatibilityButtonGroup = new javax.swing.ButtonGroup(); + pokeAbilitiesButtonGroup = new javax.swing.ButtonGroup(); + mtMovesButtonGroup = new javax.swing.ButtonGroup(); + mtCompatibilityButtonGroup = new javax.swing.ButtonGroup(); + ingameTradesButtonGroup = new javax.swing.ButtonGroup(); + fieldItemsButtonGroup = new javax.swing.ButtonGroup(); + updateSettingsMenu = new javax.swing.JPopupMenu(); + toggleAutoUpdatesMenuItem = new javax.swing.JMenuItem(); + manualUpdateMenuItem = new javax.swing.JMenuItem(); + generalOptionsPanel = new javax.swing.JPanel(); + goUpdateTypesCheckBox = new javax.swing.JCheckBox(); + goUpdateMovesCheckBox = new javax.swing.JCheckBox(); + goRemoveTradeEvosCheckBox = new javax.swing.JCheckBox(); + goLowerCaseNamesCheckBox = new javax.swing.JCheckBox(); + goNationalDexCheckBox = new javax.swing.JCheckBox(); + goUpdateMovesLegacyCheckBox = new javax.swing.JCheckBox(); + romInfoPanel = new javax.swing.JPanel(); + riRomNameLabel = new javax.swing.JLabel(); + riRomCodeLabel = new javax.swing.JLabel(); + riRomSupportLabel = new javax.swing.JLabel(); + optionsScrollPane = new javax.swing.JScrollPane(); + optionsScrollPane.getVerticalScrollBar().setUnitIncrement(16); + optionsContainerPanel = new javax.swing.JPanel(); + baseStatsPanel = new javax.swing.JPanel(); + pbsChangesUnchangedRB = new javax.swing.JRadioButton(); + pbsChangesShuffleRB = new javax.swing.JRadioButton(); + pbsChangesRandomEvosRB = new javax.swing.JRadioButton(); + pbsChangesRandomTotalRB = new javax.swing.JRadioButton(); + pbsStandardEXPCurvesCB = new javax.swing.JCheckBox(); + pokemonTypesPanel = new javax.swing.JPanel(); + ptUnchangedRB = new javax.swing.JRadioButton(); + ptRandomFollowEvosRB = new javax.swing.JRadioButton(); + ptRandomTotalRB = new javax.swing.JRadioButton(); + pokemonMovesetsPanel = new javax.swing.JPanel(); + pmsUnchangedRB = new javax.swing.JRadioButton(); + pmsRandomTypeRB = new javax.swing.JRadioButton(); + pmsRandomTotalRB = new javax.swing.JRadioButton(); + pmsMetronomeOnlyRB = new javax.swing.JRadioButton(); + pms4MovesCB = new javax.swing.JCheckBox(); + trainersPokemonPanel = new javax.swing.JPanel(); + tpUnchangedRB = new javax.swing.JRadioButton(); + tpRandomRB = new javax.swing.JRadioButton(); + tpTypeThemedRB = new javax.swing.JRadioButton(); + tpPowerLevelsCB = new javax.swing.JCheckBox(); + tpTypeWeightingCB = new javax.swing.JCheckBox(); + tpRivalCarriesStarterCB = new javax.swing.JCheckBox(); + tpNoLegendariesCB = new javax.swing.JCheckBox(); + tnRandomizeCB = new javax.swing.JCheckBox(); + tcnRandomizeCB = new javax.swing.JCheckBox(); + tpNoEarlyShedinjaCB = new javax.swing.JCheckBox(); + wildPokemonPanel = new javax.swing.JPanel(); + wpUnchangedRB = new javax.swing.JRadioButton(); + wpRandomRB = new javax.swing.JRadioButton(); + wpArea11RB = new javax.swing.JRadioButton(); + wpGlobalRB = new javax.swing.JRadioButton(); + wildPokemonARulePanel = new javax.swing.JPanel(); + wpARNoneRB = new javax.swing.JRadioButton(); + wpARCatchEmAllRB = new javax.swing.JRadioButton(); + wpARTypeThemedRB = new javax.swing.JRadioButton(); + wpARSimilarStrengthRB = new javax.swing.JRadioButton(); + wpUseTimeCB = new javax.swing.JCheckBox(); + wpNoLegendariesCB = new javax.swing.JCheckBox(); + wpCatchRateCB = new javax.swing.JCheckBox(); + wpHeldItemsCB = new javax.swing.JCheckBox(); + starterPokemonPanel = new javax.swing.JPanel(); + spUnchangedRB = new javax.swing.JRadioButton(); + spCustomRB = new javax.swing.JRadioButton(); + spCustomPoke1Chooser = new javax.swing.JComboBox(); + spCustomPoke2Chooser = new javax.swing.JComboBox(); + spCustomPoke3Chooser = new javax.swing.JComboBox(); + spRandomRB = new javax.swing.JRadioButton(); + spRandom2EvosRB = new javax.swing.JRadioButton(); + spHeldItemsCB = new javax.swing.JCheckBox(); + staticPokemonPanel = new javax.swing.JPanel(); + stpUnchangedRB = new javax.swing.JRadioButton(); + stpRandomL4LRB = new javax.swing.JRadioButton(); + stpRandomTotalRB = new javax.swing.JRadioButton(); + tmhmsPanel = new javax.swing.JPanel(); + tmMovesPanel = new javax.swing.JPanel(); + tmmUnchangedRB = new javax.swing.JRadioButton(); + tmmRandomRB = new javax.swing.JRadioButton(); + tmHmCompatPanel = new javax.swing.JPanel(); + thcUnchangedRB = new javax.swing.JRadioButton(); + thcRandomTypeRB = new javax.swing.JRadioButton(); + thcRandomTotalRB = new javax.swing.JRadioButton(); + abilitiesPanel = new javax.swing.JPanel(); + paUnchangedRB = new javax.swing.JRadioButton(); + paRandomizeRB = new javax.swing.JRadioButton(); + paWonderGuardCB = new javax.swing.JCheckBox(); + moveTutorsPanel = new javax.swing.JPanel(); + mtMovesPanel = new javax.swing.JPanel(); + mtmUnchangedRB = new javax.swing.JRadioButton(); + mtmRandomRB = new javax.swing.JRadioButton(); + mtCompatPanel = new javax.swing.JPanel(); + mtcUnchangedRB = new javax.swing.JRadioButton(); + mtcRandomTypeRB = new javax.swing.JRadioButton(); + mtcRandomTotalRB = new javax.swing.JRadioButton(); + mtNoExistLabel = new javax.swing.JLabel(); + inGameTradesPanel = new javax.swing.JPanel(); + igtUnchangedRB = new javax.swing.JRadioButton(); + igtGivenOnlyRB = new javax.swing.JRadioButton(); + igtBothRB = new javax.swing.JRadioButton(); + igtRandomNicknameCB = new javax.swing.JCheckBox(); + igtRandomOTCB = new javax.swing.JCheckBox(); + igtRandomIVsCB = new javax.swing.JCheckBox(); + igtRandomItemCB = new javax.swing.JCheckBox(); + fieldItemsPanel = new javax.swing.JPanel(); + fiUnchangedRB = new javax.swing.JRadioButton(); + fiShuffleRB = new javax.swing.JRadioButton(); + fiRandomRB = new javax.swing.JRadioButton(); + openROMButton = new javax.swing.JButton(); + saveROMButton = new javax.swing.JButton(); + usePresetsButton = new javax.swing.JButton(); + aboutButton = new javax.swing.JButton(); + otherOptionsPanel = new javax.swing.JPanel(); + codeTweaksCB = new javax.swing.JCheckBox(); + raceModeCB = new javax.swing.JCheckBox(); + randomizeHollowsCB = new javax.swing.JCheckBox(); + brokenMovesCB = new javax.swing.JCheckBox(); + codeTweaksBtn = new javax.swing.JButton(); + pokeLimitCB = new javax.swing.JCheckBox(); + pokeLimitBtn = new javax.swing.JButton(); + loadQSButton = new javax.swing.JButton(); + saveQSButton = new javax.swing.JButton(); + updateSettingsButton = new javax.swing.JButton(); + + romOpenChooser.setFileFilter(new ROMFilter()); + + romSaveChooser.setDialogType(javax.swing.JFileChooser.SAVE_DIALOG); + romSaveChooser.setFileFilter(new ROMFilter()); + + qsOpenChooser.setFileFilter(new QSFileFilter()); + + qsSaveChooser.setDialogType(javax.swing.JFileChooser.SAVE_DIALOG); + qsSaveChooser.setFileFilter(new QSFileFilter()); + + java.util.ResourceBundle bundle = java.util.ResourceBundle + .getBundle("com/dabomstew/pkrandom/gui/Bundle"); // NOI18N + toggleAutoUpdatesMenuItem.setText(bundle + .getString("RandomizerGUI.toggleAutoUpdatesMenuItem.text")); // NOI18N + toggleAutoUpdatesMenuItem + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + toggleAutoUpdatesMenuItemActionPerformed(evt); + } + }); + updateSettingsMenu.add(toggleAutoUpdatesMenuItem); + + manualUpdateMenuItem.setText(bundle + .getString("RandomizerGUI.manualUpdateMenuItem.text")); // NOI18N + manualUpdateMenuItem + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + manualUpdateMenuItemActionPerformed(evt); + } + }); + updateSettingsMenu.add(manualUpdateMenuItem); + + setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); + setTitle(bundle.getString("RandomizerGUI.title")); // NOI18N + + generalOptionsPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.generalOptionsPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + goUpdateTypesCheckBox.setText(bundle + .getString("RandomizerGUI.goUpdateTypesCheckBox.text")); // NOI18N + goUpdateTypesCheckBox.setToolTipText(bundle + .getString("RandomizerGUI.goUpdateTypesCheckBox.toolTipText")); // NOI18N + + goUpdateMovesCheckBox.setText(bundle + .getString("RandomizerGUI.goUpdateMovesCheckBox.text")); // NOI18N + goUpdateMovesCheckBox.setToolTipText(bundle + .getString("RandomizerGUI.goUpdateMovesCheckBox.toolTipText")); // NOI18N + goUpdateMovesCheckBox + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + goUpdateMovesCheckBoxActionPerformed(evt); + } + }); + + goRemoveTradeEvosCheckBox.setText(bundle + .getString("RandomizerGUI.goRemoveTradeEvosCheckBox.text")); // NOI18N + goRemoveTradeEvosCheckBox + .setToolTipText(bundle + .getString("RandomizerGUI.goRemoveTradeEvosCheckBox.toolTipText")); // NOI18N + + goLowerCaseNamesCheckBox.setText(bundle + .getString("RandomizerGUI.goLowerCaseNamesCheckBox.text")); // NOI18N + goLowerCaseNamesCheckBox + .setToolTipText(bundle + .getString("RandomizerGUI.goLowerCaseNamesCheckBox.toolTipText")); // NOI18N + + goNationalDexCheckBox.setText(bundle + .getString("RandomizerGUI.goNationalDexCheckBox.text")); // NOI18N + goNationalDexCheckBox.setToolTipText(bundle + .getString("RandomizerGUI.goNationalDexCheckBox.toolTipText")); // NOI18N + + goUpdateMovesLegacyCheckBox.setText(bundle + .getString("RandomizerGUI.goUpdateMovesLegacyCheckBox.text")); // NOI18N + goUpdateMovesLegacyCheckBox + .setToolTipText(bundle + .getString("RandomizerGUI.goUpdateMovesLegacyCheckBox.toolTipText")); // NOI18N + + javax.swing.GroupLayout generalOptionsPanelLayout = new javax.swing.GroupLayout( + generalOptionsPanel); + generalOptionsPanel.setLayout(generalOptionsPanelLayout); + generalOptionsPanelLayout + .setHorizontalGroup(generalOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + generalOptionsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + generalOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + generalOptionsPanelLayout + .createSequentialGroup() + .addGroup( + generalOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + goUpdateTypesCheckBox) + .addComponent( + goRemoveTradeEvosCheckBox) + .addComponent( + goNationalDexCheckBox)) + .addContainerGap( + 14, + Short.MAX_VALUE)) + .addGroup( + generalOptionsPanelLayout + .createSequentialGroup() + .addGroup( + generalOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + generalOptionsPanelLayout + .createSequentialGroup() + .addComponent( + goUpdateMovesCheckBox) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + goUpdateMovesLegacyCheckBox)) + .addComponent( + goLowerCaseNamesCheckBox)) + .addGap(0, + 0, + Short.MAX_VALUE))))); + generalOptionsPanelLayout + .setVerticalGroup(generalOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + generalOptionsPanelLayout + .createSequentialGroup() + .addComponent(goUpdateTypesCheckBox) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + generalOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + goUpdateMovesCheckBox) + .addComponent( + goUpdateMovesLegacyCheckBox)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(goRemoveTradeEvosCheckBox) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(goLowerCaseNamesCheckBox) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(goNationalDexCheckBox) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + romInfoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.romInfoPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + riRomNameLabel.setText(bundle + .getString("RandomizerGUI.riRomNameLabel.text")); // NOI18N + + riRomCodeLabel.setText(bundle + .getString("RandomizerGUI.riRomCodeLabel.text")); // NOI18N + + riRomSupportLabel.setText(bundle + .getString("RandomizerGUI.riRomSupportLabel.text")); // NOI18N + + javax.swing.GroupLayout romInfoPanelLayout = new javax.swing.GroupLayout( + romInfoPanel); + romInfoPanel.setLayout(romInfoPanelLayout); + romInfoPanelLayout + .setHorizontalGroup(romInfoPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + romInfoPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + romInfoPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + riRomNameLabel) + .addComponent( + riRomCodeLabel) + .addComponent( + riRomSupportLabel)) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + romInfoPanelLayout + .setVerticalGroup(romInfoPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + romInfoPanelLayout + .createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(riRomNameLabel) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(riRomCodeLabel) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(riRomSupportLabel) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + baseStatsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.baseStatsPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + pokeStatChangesButtonGroup.add(pbsChangesUnchangedRB); + pbsChangesUnchangedRB.setSelected(true); + pbsChangesUnchangedRB.setText(bundle + .getString("RandomizerGUI.pbsChangesUnchangedRB.text")); // NOI18N + pbsChangesUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.pbsChangesUnchangedRB.toolTipText")); // NOI18N + + pokeStatChangesButtonGroup.add(pbsChangesShuffleRB); + pbsChangesShuffleRB.setText(bundle + .getString("RandomizerGUI.pbsChangesShuffleRB.text")); // NOI18N + pbsChangesShuffleRB.setToolTipText(bundle + .getString("RandomizerGUI.pbsChangesShuffleRB.toolTipText")); // NOI18N + + pokeStatChangesButtonGroup.add(pbsChangesRandomEvosRB); + pbsChangesRandomEvosRB.setText(bundle + .getString("RandomizerGUI.pbsChangesRandomEvosRB.text")); // NOI18N + pbsChangesRandomEvosRB.setToolTipText(bundle + .getString("RandomizerGUI.pbsChangesRandomEvosRB.toolTipText")); // NOI18N + + pokeStatChangesButtonGroup.add(pbsChangesRandomTotalRB); + pbsChangesRandomTotalRB.setText(bundle + .getString("RandomizerGUI.pbsChangesRandomTotalRB.text")); // NOI18N + pbsChangesRandomTotalRB + .setToolTipText(bundle + .getString("RandomizerGUI.pbsChangesRandomTotalRB.toolTipText")); // NOI18N + + pbsStandardEXPCurvesCB.setText(bundle + .getString("RandomizerGUI.pbsStandardEXPCurvesCB.text")); // NOI18N + pbsStandardEXPCurvesCB.setToolTipText(bundle + .getString("RandomizerGUI.pbsStandardEXPCurvesCB.toolTipText")); // NOI18N + + javax.swing.GroupLayout baseStatsPanelLayout = new javax.swing.GroupLayout( + baseStatsPanel); + baseStatsPanel.setLayout(baseStatsPanelLayout); + baseStatsPanelLayout + .setHorizontalGroup(baseStatsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + baseStatsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + baseStatsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + baseStatsPanelLayout + .createSequentialGroup() + .addGroup( + baseStatsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + pbsChangesUnchangedRB) + .addComponent( + pbsChangesRandomEvosRB) + .addComponent( + pbsChangesRandomTotalRB)) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addGroup( + baseStatsPanelLayout + .createSequentialGroup() + .addComponent( + pbsChangesShuffleRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + 125, + Short.MAX_VALUE) + .addComponent( + pbsStandardEXPCurvesCB) + .addGap(38, + 38, + 38))))); + baseStatsPanelLayout + .setVerticalGroup(baseStatsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + baseStatsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(pbsChangesUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + baseStatsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + pbsChangesShuffleRB) + .addComponent( + pbsStandardEXPCurvesCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pbsChangesRandomEvosRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pbsChangesRandomTotalRB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + pokemonTypesPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.pokemonTypesPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + pokeTypesButtonGroup.add(ptUnchangedRB); + ptUnchangedRB.setSelected(true); + ptUnchangedRB.setText(bundle + .getString("RandomizerGUI.ptUnchangedRB.text")); // NOI18N + ptUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.ptUnchangedRB.toolTipText")); // NOI18N + + pokeTypesButtonGroup.add(ptRandomFollowEvosRB); + ptRandomFollowEvosRB.setText(bundle + .getString("RandomizerGUI.ptRandomFollowEvosRB.text")); // NOI18N + ptRandomFollowEvosRB.setToolTipText(bundle + .getString("RandomizerGUI.ptRandomFollowEvosRB.toolTipText")); // NOI18N + + pokeTypesButtonGroup.add(ptRandomTotalRB); + ptRandomTotalRB.setText(bundle + .getString("RandomizerGUI.ptRandomTotalRB.text")); // NOI18N + ptRandomTotalRB.setToolTipText(bundle + .getString("RandomizerGUI.ptRandomTotalRB.toolTipText")); // NOI18N + + javax.swing.GroupLayout pokemonTypesPanelLayout = new javax.swing.GroupLayout( + pokemonTypesPanel); + pokemonTypesPanel.setLayout(pokemonTypesPanelLayout); + pokemonTypesPanelLayout + .setHorizontalGroup(pokemonTypesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + pokemonTypesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + pokemonTypesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + ptUnchangedRB) + .addComponent( + ptRandomFollowEvosRB) + .addComponent( + ptRandomTotalRB)) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + pokemonTypesPanelLayout + .setVerticalGroup(pokemonTypesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + pokemonTypesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(ptUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ptRandomFollowEvosRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ptRandomTotalRB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + pokemonMovesetsPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.pokemonMovesetsPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + pokeMovesetsButtonGroup.add(pmsUnchangedRB); + pmsUnchangedRB.setSelected(true); + pmsUnchangedRB.setText(bundle + .getString("RandomizerGUI.pmsUnchangedRB.text")); // NOI18N + pmsUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.pmsUnchangedRB.toolTipText")); // NOI18N + pmsUnchangedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pmsUnchangedRBActionPerformed(evt); + } + }); + + pokeMovesetsButtonGroup.add(pmsRandomTypeRB); + pmsRandomTypeRB.setText(bundle + .getString("RandomizerGUI.pmsRandomTypeRB.text")); // NOI18N + pmsRandomTypeRB.setToolTipText(bundle + .getString("RandomizerGUI.pmsRandomTypeRB.toolTipText")); // NOI18N + pmsRandomTypeRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pmsRandomTypeRBActionPerformed(evt); + } + }); + + pokeMovesetsButtonGroup.add(pmsRandomTotalRB); + pmsRandomTotalRB.setText(bundle + .getString("RandomizerGUI.pmsRandomTotalRB.text")); // NOI18N + pmsRandomTotalRB.setToolTipText(bundle + .getString("RandomizerGUI.pmsRandomTotalRB.toolTipText")); // NOI18N + pmsRandomTotalRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pmsRandomTotalRBActionPerformed(evt); + } + }); + + pokeMovesetsButtonGroup.add(pmsMetronomeOnlyRB); + pmsMetronomeOnlyRB.setText(bundle + .getString("RandomizerGUI.pmsMetronomeOnlyRB.text")); // NOI18N + pmsMetronomeOnlyRB.setToolTipText(bundle + .getString("RandomizerGUI.pmsMetronomeOnlyRB.toolTipText")); // NOI18N + pmsMetronomeOnlyRB + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pmsMetronomeOnlyRBActionPerformed(evt); + } + }); + + pms4MovesCB.setText(bundle.getString("RandomizerGUI.pms4MovesCB.text")); // NOI18N + pms4MovesCB.setToolTipText(bundle + .getString("RandomizerGUI.pms4MovesCB.toolTipText")); // NOI18N + + javax.swing.GroupLayout pokemonMovesetsPanelLayout = new javax.swing.GroupLayout( + pokemonMovesetsPanel); + pokemonMovesetsPanel.setLayout(pokemonMovesetsPanelLayout); + pokemonMovesetsPanelLayout + .setHorizontalGroup(pokemonMovesetsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + pokemonMovesetsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + pokemonMovesetsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + pmsUnchangedRB) + .addGroup( + pokemonMovesetsPanelLayout + .createSequentialGroup() + .addComponent( + pmsRandomTypeRB) + .addGap(198, + 198, + 198) + .addComponent( + pms4MovesCB)) + .addComponent( + pmsRandomTotalRB) + .addComponent( + pmsMetronomeOnlyRB)) + .addContainerGap(134, Short.MAX_VALUE))); + pokemonMovesetsPanelLayout + .setVerticalGroup(pokemonMovesetsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + pokemonMovesetsPanelLayout + .createSequentialGroup() + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(pmsUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + pokemonMovesetsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + pmsRandomTypeRB) + .addComponent( + pms4MovesCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pmsRandomTotalRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pmsMetronomeOnlyRB))); + + trainersPokemonPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.trainersPokemonPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + trainerPokesButtonGroup.add(tpUnchangedRB); + tpUnchangedRB.setSelected(true); + tpUnchangedRB.setText(bundle + .getString("RandomizerGUI.tpUnchangedRB.text")); // NOI18N + tpUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.tpUnchangedRB.toolTipText")); // NOI18N + tpUnchangedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tpUnchangedRBActionPerformed(evt); + } + }); + + trainerPokesButtonGroup.add(tpRandomRB); + tpRandomRB.setText(bundle.getString("RandomizerGUI.tpRandomRB.text")); // NOI18N + tpRandomRB.setToolTipText(bundle + .getString("RandomizerGUI.tpRandomRB.toolTipText")); // NOI18N + tpRandomRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tpRandomRBActionPerformed(evt); + } + }); + + trainerPokesButtonGroup.add(tpTypeThemedRB); + tpTypeThemedRB.setText(bundle + .getString("RandomizerGUI.tpTypeThemedRB.text")); // NOI18N + tpTypeThemedRB.setToolTipText(bundle + .getString("RandomizerGUI.tpTypeThemedRB.toolTipText")); // NOI18N + tpTypeThemedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + tpTypeThemedRBActionPerformed(evt); + } + }); + + tpPowerLevelsCB.setText(bundle + .getString("RandomizerGUI.tpPowerLevelsCB.text")); // NOI18N + tpPowerLevelsCB.setToolTipText(bundle + .getString("RandomizerGUI.tpPowerLevelsCB.toolTipText")); // NOI18N + tpPowerLevelsCB.setEnabled(false); + + tpTypeWeightingCB.setText(bundle + .getString("RandomizerGUI.tpTypeWeightingCB.text")); // NOI18N + tpTypeWeightingCB.setToolTipText(bundle + .getString("RandomizerGUI.tpTypeWeightingCB.toolTipText")); // NOI18N + tpTypeWeightingCB.setEnabled(false); + + tpRivalCarriesStarterCB.setText(bundle + .getString("RandomizerGUI.tpRivalCarriesStarterCB.text")); // NOI18N + tpRivalCarriesStarterCB + .setToolTipText(bundle + .getString("RandomizerGUI.tpRivalCarriesStarterCB.toolTipText")); // NOI18N + tpRivalCarriesStarterCB.setEnabled(false); + + tpNoLegendariesCB.setText(bundle + .getString("RandomizerGUI.tpNoLegendariesCB.text")); // NOI18N + tpNoLegendariesCB.setEnabled(false); + + tnRandomizeCB.setText(bundle + .getString("RandomizerGUI.tnRandomizeCB.text")); // NOI18N + tnRandomizeCB.setToolTipText(bundle + .getString("RandomizerGUI.tnRandomizeCB.toolTipText")); // NOI18N + + tcnRandomizeCB.setText(bundle + .getString("RandomizerGUI.tcnRandomizeCB.text")); // NOI18N + tcnRandomizeCB.setToolTipText(bundle + .getString("RandomizerGUI.tcnRandomizeCB.toolTipText")); // NOI18N + + tpNoEarlyShedinjaCB.setText(bundle + .getString("RandomizerGUI.tpNoEarlyShedinjaCB.text")); // NOI18N + tpNoEarlyShedinjaCB.setToolTipText(bundle + .getString("RandomizerGUI.tpNoEarlyShedinjaCB.toolTipText")); // NOI18N + + javax.swing.GroupLayout trainersPokemonPanelLayout = new javax.swing.GroupLayout( + trainersPokemonPanel); + trainersPokemonPanel.setLayout(trainersPokemonPanelLayout); + trainersPokemonPanelLayout + .setHorizontalGroup(trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + trainersPokemonPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + tpTypeThemedRB) + .addGroup( + trainersPokemonPanelLayout + .createSequentialGroup() + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + tpUnchangedRB) + .addComponent( + tpRandomRB)) + .addGap(47, + 47, + 47) + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + tpNoEarlyShedinjaCB) + .addGroup( + trainersPokemonPanelLayout + .createSequentialGroup() + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING, + false) + .addComponent( + tpTypeWeightingCB, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + tpRivalCarriesStarterCB, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + tpPowerLevelsCB, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + tpNoLegendariesCB, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addGap(18, + 18, + 18) + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + tnRandomizeCB) + .addComponent( + tcnRandomizeCB)))))) + .addContainerGap(160, Short.MAX_VALUE))); + trainersPokemonPanelLayout + .setVerticalGroup(trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + trainersPokemonPanelLayout + .createSequentialGroup() + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + tpUnchangedRB) + .addComponent( + tpRivalCarriesStarterCB) + .addComponent( + tnRandomizeCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + tpRandomRB) + .addComponent( + tpPowerLevelsCB) + .addComponent( + tcnRandomizeCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + trainersPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + tpTypeThemedRB) + .addComponent( + tpTypeWeightingCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(tpNoLegendariesCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(tpNoEarlyShedinjaCB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + wildPokemonPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.wildPokemonPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + wildPokesButtonGroup.add(wpUnchangedRB); + wpUnchangedRB.setSelected(true); + wpUnchangedRB.setText(bundle + .getString("RandomizerGUI.wpUnchangedRB.text")); // NOI18N + wpUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.wpUnchangedRB.toolTipText")); // NOI18N + wpUnchangedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpUnchangedRBActionPerformed(evt); + } + }); + + wildPokesButtonGroup.add(wpRandomRB); + wpRandomRB.setText(bundle.getString("RandomizerGUI.wpRandomRB.text")); // NOI18N + wpRandomRB.setToolTipText(bundle + .getString("RandomizerGUI.wpRandomRB.toolTipText")); // NOI18N + wpRandomRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpRandomRBActionPerformed(evt); + } + }); + + wildPokesButtonGroup.add(wpArea11RB); + wpArea11RB.setText(bundle.getString("RandomizerGUI.wpArea11RB.text")); // NOI18N + wpArea11RB.setToolTipText(bundle + .getString("RandomizerGUI.wpArea11RB.toolTipText")); // NOI18N + wpArea11RB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpArea11RBActionPerformed(evt); + } + }); + + wildPokesButtonGroup.add(wpGlobalRB); + wpGlobalRB.setText(bundle.getString("RandomizerGUI.wpGlobalRB.text")); // NOI18N + wpGlobalRB.setToolTipText(bundle + .getString("RandomizerGUI.wpGlobalRB.toolTipText")); // NOI18N + wpGlobalRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpGlobalRBActionPerformed(evt); + } + }); + + wildPokemonARulePanel + .setBorder(javax.swing.BorderFactory.createTitledBorder(bundle + .getString("RandomizerGUI.wildPokemonARulePanel.border.title"))); // NOI18N + + wildPokesARuleButtonGroup.add(wpARNoneRB); + wpARNoneRB.setSelected(true); + wpARNoneRB.setText(bundle.getString("RandomizerGUI.wpARNoneRB.text")); // NOI18N + wpARNoneRB.setToolTipText(bundle + .getString("RandomizerGUI.wpARNoneRB.toolTipText")); // NOI18N + wpARNoneRB.setEnabled(false); + wpARNoneRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpARNoneRBActionPerformed(evt); + } + }); + + wildPokesARuleButtonGroup.add(wpARCatchEmAllRB); + wpARCatchEmAllRB.setText(bundle + .getString("RandomizerGUI.wpARCatchEmAllRB.text")); // NOI18N + wpARCatchEmAllRB.setToolTipText(bundle + .getString("RandomizerGUI.wpARCatchEmAllRB.toolTipText")); // NOI18N + wpARCatchEmAllRB.setEnabled(false); + wpARCatchEmAllRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpARCatchEmAllRBActionPerformed(evt); + } + }); + + wildPokesARuleButtonGroup.add(wpARTypeThemedRB); + wpARTypeThemedRB.setText(bundle + .getString("RandomizerGUI.wpARTypeThemedRB.text")); // NOI18N + wpARTypeThemedRB.setToolTipText(bundle + .getString("RandomizerGUI.wpARTypeThemedRB.toolTipText")); // NOI18N + wpARTypeThemedRB.setEnabled(false); + wpARTypeThemedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpARTypeThemedRBActionPerformed(evt); + } + }); + + wildPokesARuleButtonGroup.add(wpARSimilarStrengthRB); + wpARSimilarStrengthRB.setText(bundle + .getString("RandomizerGUI.wpARSimilarStrengthRB.text")); // NOI18N + wpARSimilarStrengthRB.setToolTipText(bundle + .getString("RandomizerGUI.wpARSimilarStrengthRB.toolTipText")); // NOI18N + wpARSimilarStrengthRB.setEnabled(false); + wpARSimilarStrengthRB + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + wpARSimilarStrengthRBActionPerformed(evt); + } + }); + + javax.swing.GroupLayout wildPokemonARulePanelLayout = new javax.swing.GroupLayout( + wildPokemonARulePanel); + wildPokemonARulePanel.setLayout(wildPokemonARulePanelLayout); + wildPokemonARulePanelLayout + .setHorizontalGroup(wildPokemonARulePanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + wildPokemonARulePanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + wildPokemonARulePanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + wildPokemonARulePanelLayout + .createSequentialGroup() + .addComponent( + wpARTypeThemedRB) + .addGap(0, + 0, + Short.MAX_VALUE)) + .addGroup( + wildPokemonARulePanelLayout + .createSequentialGroup() + .addGroup( + wildPokemonARulePanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + wpARSimilarStrengthRB) + .addComponent( + wpARNoneRB) + .addComponent( + wpARCatchEmAllRB)) + .addContainerGap( + 58, + Short.MAX_VALUE))))); + wildPokemonARulePanelLayout + .setVerticalGroup(wildPokemonARulePanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + wildPokemonARulePanelLayout + .createSequentialGroup() + .addComponent(wpARNoneRB) + .addGap(3, 3, 3) + .addComponent(wpARSimilarStrengthRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(wpARCatchEmAllRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + 3, Short.MAX_VALUE) + .addComponent( + wpARTypeThemedRB, + javax.swing.GroupLayout.PREFERRED_SIZE, + 30, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap())); + + wpUseTimeCB.setText(bundle.getString("RandomizerGUI.wpUseTimeCB.text")); // NOI18N + wpUseTimeCB.setToolTipText(bundle + .getString("RandomizerGUI.wpUseTimeCB.toolTipText")); // NOI18N + + wpNoLegendariesCB.setText(bundle + .getString("RandomizerGUI.wpNoLegendariesCB.text")); // NOI18N + + wpCatchRateCB.setText(bundle + .getString("RandomizerGUI.wpCatchRateCB.text")); // NOI18N + wpCatchRateCB.setToolTipText(bundle + .getString("RandomizerGUI.wpCatchRateCB.toolTipText")); // NOI18N + + wpHeldItemsCB.setText(bundle + .getString("RandomizerGUI.wpHeldItemsCB.text")); // NOI18N + wpHeldItemsCB.setToolTipText(bundle + .getString("RandomizerGUI.wpHeldItemsCB.toolTipText")); // NOI18N + + javax.swing.GroupLayout wildPokemonPanelLayout = new javax.swing.GroupLayout( + wildPokemonPanel); + wildPokemonPanel.setLayout(wildPokemonPanelLayout); + wildPokemonPanelLayout + .setHorizontalGroup(wildPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + wildPokemonPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + wildPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + wpUnchangedRB) + .addComponent( + wpRandomRB) + .addComponent( + wpArea11RB) + .addComponent( + wpGlobalRB)) + .addGap(18, 18, 18) + .addComponent( + wildPokemonARulePanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, 18, 18) + .addGroup( + wildPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + wpUseTimeCB) + .addComponent( + wpNoLegendariesCB) + .addComponent( + wpCatchRateCB) + .addComponent( + wpHeldItemsCB)) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + wildPokemonPanelLayout + .setVerticalGroup(wildPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.TRAILING) + .addGroup( + javax.swing.GroupLayout.Alignment.LEADING, + wildPokemonPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(wpUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(wpRandomRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(wpArea11RB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(wpGlobalRB)) + .addGroup( + javax.swing.GroupLayout.Alignment.LEADING, + wildPokemonPanelLayout + .createSequentialGroup() + .addGap(28, 28, 28) + .addComponent(wpUseTimeCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(wpNoLegendariesCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(wpCatchRateCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(wpHeldItemsCB)) + .addGroup( + javax.swing.GroupLayout.Alignment.LEADING, + wildPokemonPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent( + wildPokemonARulePanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE))); + + starterPokemonPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.starterPokemonPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + starterPokemonButtonGroup.add(spUnchangedRB); + spUnchangedRB.setSelected(true); + spUnchangedRB.setText(bundle + .getString("RandomizerGUI.spUnchangedRB.text")); // NOI18N + spUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.spUnchangedRB.toolTipText")); // NOI18N + spUnchangedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + spUnchangedRBActionPerformed(evt); + } + }); + + starterPokemonButtonGroup.add(spCustomRB); + spCustomRB.setText(bundle.getString("RandomizerGUI.spCustomRB.text")); // NOI18N + spCustomRB.setToolTipText(bundle + .getString("RandomizerGUI.spCustomRB.toolTipText")); // NOI18N + spCustomRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + spCustomRBActionPerformed(evt); + } + }); + + spCustomPoke1Chooser.setModel(new javax.swing.DefaultComboBoxModel( + new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + spCustomPoke1Chooser.setEnabled(false); + + spCustomPoke2Chooser.setModel(new javax.swing.DefaultComboBoxModel( + new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + spCustomPoke2Chooser.setEnabled(false); + + spCustomPoke3Chooser.setModel(new javax.swing.DefaultComboBoxModel( + new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); + spCustomPoke3Chooser.setEnabled(false); + + starterPokemonButtonGroup.add(spRandomRB); + spRandomRB.setText(bundle.getString("RandomizerGUI.spRandomRB.text")); // NOI18N + spRandomRB.setToolTipText(bundle + .getString("RandomizerGUI.spRandomRB.toolTipText")); // NOI18N + spRandomRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + spRandomRBActionPerformed(evt); + } + }); + + starterPokemonButtonGroup.add(spRandom2EvosRB); + spRandom2EvosRB.setText(bundle + .getString("RandomizerGUI.spRandom2EvosRB.text")); // NOI18N + spRandom2EvosRB.setToolTipText(bundle + .getString("RandomizerGUI.spRandom2EvosRB.toolTipText")); // NOI18N + + spHeldItemsCB.setText(bundle + .getString("RandomizerGUI.spHeldItemsCB.text")); // NOI18N + spHeldItemsCB.setToolTipText(bundle + .getString("RandomizerGUI.spHeldItemsCB.toolTipText")); // NOI18N + + javax.swing.GroupLayout starterPokemonPanelLayout = new javax.swing.GroupLayout( + starterPokemonPanel); + starterPokemonPanel.setLayout(starterPokemonPanelLayout); + starterPokemonPanelLayout + .setHorizontalGroup(starterPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + starterPokemonPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + starterPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + spUnchangedRB) + .addGroup( + starterPokemonPanelLayout + .createSequentialGroup() + .addComponent( + spCustomRB) + .addGap(18, + 18, + 18) + .addComponent( + spCustomPoke1Chooser, + javax.swing.GroupLayout.PREFERRED_SIZE, + 90, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + spCustomPoke2Chooser, + javax.swing.GroupLayout.PREFERRED_SIZE, + 90, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + spCustomPoke3Chooser, + javax.swing.GroupLayout.PREFERRED_SIZE, + 90, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(18, + 18, + 18) + .addComponent( + spHeldItemsCB)) + .addComponent( + spRandomRB) + .addComponent( + spRandom2EvosRB)) + .addContainerGap(162, Short.MAX_VALUE))); + starterPokemonPanelLayout + .setVerticalGroup(starterPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + starterPokemonPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(spUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + starterPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + spCustomRB) + .addComponent( + spCustomPoke1Chooser, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent( + spCustomPoke2Chooser, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent( + spCustomPoke3Chooser, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent( + spHeldItemsCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(spRandomRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(spRandom2EvosRB) + .addContainerGap(11, Short.MAX_VALUE))); + + staticPokemonPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.staticPokemonPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + staticPokemonButtonGroup.add(stpUnchangedRB); + stpUnchangedRB.setSelected(true); + stpUnchangedRB.setText(bundle + .getString("RandomizerGUI.stpUnchangedRB.text")); // NOI18N + stpUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.stpUnchangedRB.toolTipText")); // NOI18N + + staticPokemonButtonGroup.add(stpRandomL4LRB); + stpRandomL4LRB.setText(bundle + .getString("RandomizerGUI.stpRandomL4LRB.text")); // NOI18N + stpRandomL4LRB.setToolTipText(bundle + .getString("RandomizerGUI.stpRandomL4LRB.toolTipText")); // NOI18N + + staticPokemonButtonGroup.add(stpRandomTotalRB); + stpRandomTotalRB.setText(bundle + .getString("RandomizerGUI.stpRandomTotalRB.text")); // NOI18N + stpRandomTotalRB.setToolTipText(bundle + .getString("RandomizerGUI.stpRandomTotalRB.toolTipText")); // NOI18N + + javax.swing.GroupLayout staticPokemonPanelLayout = new javax.swing.GroupLayout( + staticPokemonPanel); + staticPokemonPanel.setLayout(staticPokemonPanelLayout); + staticPokemonPanelLayout + .setHorizontalGroup(staticPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + staticPokemonPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + staticPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + stpUnchangedRB) + .addComponent( + stpRandomL4LRB) + .addComponent( + stpRandomTotalRB)) + .addContainerGap(401, Short.MAX_VALUE))); + staticPokemonPanelLayout + .setVerticalGroup(staticPokemonPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + staticPokemonPanelLayout + .createSequentialGroup() + .addComponent(stpUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(stpRandomL4LRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(stpRandomTotalRB))); + + tmhmsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, + bundle.getString("RandomizerGUI.tmhmsPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + tmMovesPanel.setBorder(javax.swing.BorderFactory + .createTitledBorder(bundle + .getString("RandomizerGUI.tmMovesPanel.border.title"))); // NOI18N + + tmMovesButtonGroup.add(tmmUnchangedRB); + tmmUnchangedRB.setSelected(true); + tmmUnchangedRB.setText(bundle + .getString("RandomizerGUI.tmmUnchangedRB.text")); // NOI18N + tmmUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.tmmUnchangedRB.toolTipText")); // NOI18N + + tmMovesButtonGroup.add(tmmRandomRB); + tmmRandomRB.setText(bundle.getString("RandomizerGUI.tmmRandomRB.text")); // NOI18N + tmmRandomRB.setToolTipText(bundle + .getString("RandomizerGUI.tmmRandomRB.toolTipText")); // NOI18N + + javax.swing.GroupLayout tmMovesPanelLayout = new javax.swing.GroupLayout( + tmMovesPanel); + tmMovesPanel.setLayout(tmMovesPanelLayout); + tmMovesPanelLayout + .setHorizontalGroup(tmMovesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + tmMovesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + tmMovesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + tmmUnchangedRB) + .addComponent( + tmmRandomRB)) + .addContainerGap(118, Short.MAX_VALUE))); + tmMovesPanelLayout + .setVerticalGroup(tmMovesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + tmMovesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(tmmUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(tmmRandomRB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + tmHmCompatPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder(bundle + .getString("RandomizerGUI.tmHmCompatPanel.border.title"))); // NOI18N + + tmHmCompatibilityButtonGroup.add(thcUnchangedRB); + thcUnchangedRB.setSelected(true); + thcUnchangedRB.setText(bundle + .getString("RandomizerGUI.thcUnchangedRB.text")); // NOI18N + thcUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.thcUnchangedRB.toolTipText")); // NOI18N + + tmHmCompatibilityButtonGroup.add(thcRandomTypeRB); + thcRandomTypeRB.setText(bundle + .getString("RandomizerGUI.thcRandomTypeRB.text")); // NOI18N + thcRandomTypeRB.setToolTipText(bundle + .getString("RandomizerGUI.thcRandomTypeRB.toolTipText")); // NOI18N + + tmHmCompatibilityButtonGroup.add(thcRandomTotalRB); + thcRandomTotalRB.setText(bundle + .getString("RandomizerGUI.thcRandomTotalRB.text")); // NOI18N + thcRandomTotalRB.setToolTipText(bundle + .getString("RandomizerGUI.thcRandomTotalRB.toolTipText")); // NOI18N + + javax.swing.GroupLayout tmHmCompatPanelLayout = new javax.swing.GroupLayout( + tmHmCompatPanel); + tmHmCompatPanel.setLayout(tmHmCompatPanelLayout); + tmHmCompatPanelLayout + .setHorizontalGroup(tmHmCompatPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + tmHmCompatPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + tmHmCompatPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + thcUnchangedRB) + .addComponent( + thcRandomTypeRB) + .addComponent( + thcRandomTotalRB)) + .addContainerGap(79, Short.MAX_VALUE))); + tmHmCompatPanelLayout + .setVerticalGroup(tmHmCompatPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + tmHmCompatPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(thcUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(thcRandomTypeRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(thcRandomTotalRB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + javax.swing.GroupLayout tmhmsPanelLayout = new javax.swing.GroupLayout( + tmhmsPanel); + tmhmsPanel.setLayout(tmhmsPanelLayout); + tmhmsPanelLayout + .setHorizontalGroup(tmhmsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + tmhmsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent( + tmMovesPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + tmHmCompatPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap())); + tmhmsPanelLayout + .setVerticalGroup(tmhmsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + tmhmsPanelLayout + .createSequentialGroup() + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addGroup( + tmhmsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING, + false) + .addComponent( + tmHmCompatPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + tmMovesPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)))); + + abilitiesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.abilitiesPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + pokeAbilitiesButtonGroup.add(paUnchangedRB); + paUnchangedRB.setSelected(true); + paUnchangedRB.setText(bundle + .getString("RandomizerGUI.paUnchangedRB.text")); // NOI18N + paUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.paUnchangedRB.toolTipText")); // NOI18N + paUnchangedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + paUnchangedRBActionPerformed(evt); + } + }); + + pokeAbilitiesButtonGroup.add(paRandomizeRB); + paRandomizeRB.setText(bundle + .getString("RandomizerGUI.paRandomizeRB.text")); // NOI18N + paRandomizeRB.setToolTipText(bundle + .getString("RandomizerGUI.paRandomizeRB.toolTipText")); // NOI18N + paRandomizeRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + paRandomizeRBActionPerformed(evt); + } + }); + + paWonderGuardCB.setText(bundle + .getString("RandomizerGUI.paWonderGuardCB.text")); // NOI18N + paWonderGuardCB.setToolTipText(bundle + .getString("RandomizerGUI.paWonderGuardCB.toolTipText")); // NOI18N + + javax.swing.GroupLayout abilitiesPanelLayout = new javax.swing.GroupLayout( + abilitiesPanel); + abilitiesPanel.setLayout(abilitiesPanelLayout); + abilitiesPanelLayout + .setHorizontalGroup(abilitiesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + abilitiesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + abilitiesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + paUnchangedRB) + .addComponent( + paRandomizeRB) + .addComponent( + paWonderGuardCB)) + .addContainerGap(190, Short.MAX_VALUE))); + abilitiesPanelLayout + .setVerticalGroup(abilitiesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + abilitiesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(paUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(paRandomizeRB) + .addGap(18, 18, 18) + .addComponent(paWonderGuardCB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + moveTutorsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.moveTutorsPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + mtMovesPanel.setBorder(javax.swing.BorderFactory + .createTitledBorder(bundle + .getString("RandomizerGUI.mtMovesPanel.border.title"))); // NOI18N + + mtMovesButtonGroup.add(mtmUnchangedRB); + mtmUnchangedRB.setSelected(true); + mtmUnchangedRB.setText(bundle + .getString("RandomizerGUI.mtmUnchangedRB.text")); // NOI18N + mtmUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.mtmUnchangedRB.toolTipText")); // NOI18N + mtmUnchangedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + mtmUnchangedRBActionPerformed(evt); + } + }); + + mtMovesButtonGroup.add(mtmRandomRB); + mtmRandomRB.setText(bundle.getString("RandomizerGUI.mtmRandomRB.text")); // NOI18N + mtmRandomRB.setToolTipText(bundle + .getString("RandomizerGUI.mtmRandomRB.toolTipText")); // NOI18N + + javax.swing.GroupLayout mtMovesPanelLayout = new javax.swing.GroupLayout( + mtMovesPanel); + mtMovesPanel.setLayout(mtMovesPanelLayout); + mtMovesPanelLayout + .setHorizontalGroup(mtMovesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + mtMovesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + mtMovesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + mtmUnchangedRB) + .addComponent( + mtmRandomRB)) + .addContainerGap(118, Short.MAX_VALUE))); + mtMovesPanelLayout + .setVerticalGroup(mtMovesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + mtMovesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(mtmUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(mtmRandomRB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + mtCompatPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder(bundle + .getString("RandomizerGUI.mtCompatPanel.border.title"))); // NOI18N + + mtCompatibilityButtonGroup.add(mtcUnchangedRB); + mtcUnchangedRB.setSelected(true); + mtcUnchangedRB.setText(bundle + .getString("RandomizerGUI.mtcUnchangedRB.text")); // NOI18N + mtcUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.mtcUnchangedRB.toolTipText")); // NOI18N + + mtCompatibilityButtonGroup.add(mtcRandomTypeRB); + mtcRandomTypeRB.setText(bundle + .getString("RandomizerGUI.mtcRandomTypeRB.text")); // NOI18N + mtcRandomTypeRB.setToolTipText(bundle + .getString("RandomizerGUI.mtcRandomTypeRB.toolTipText")); // NOI18N + + mtCompatibilityButtonGroup.add(mtcRandomTotalRB); + mtcRandomTotalRB.setText(bundle + .getString("RandomizerGUI.mtcRandomTotalRB.text")); // NOI18N + mtcRandomTotalRB.setToolTipText(bundle + .getString("RandomizerGUI.mtcRandomTotalRB.toolTipText")); // NOI18N + + javax.swing.GroupLayout mtCompatPanelLayout = new javax.swing.GroupLayout( + mtCompatPanel); + mtCompatPanel.setLayout(mtCompatPanelLayout); + mtCompatPanelLayout + .setHorizontalGroup(mtCompatPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + mtCompatPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + mtCompatPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + mtcUnchangedRB) + .addComponent( + mtcRandomTypeRB) + .addComponent( + mtcRandomTotalRB)) + .addContainerGap(79, Short.MAX_VALUE))); + mtCompatPanelLayout + .setVerticalGroup(mtCompatPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + mtCompatPanelLayout + .createSequentialGroup() + .addContainerGap() + .addComponent(mtcUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(mtcRandomTypeRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(mtcRandomTotalRB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + mtNoExistLabel.setText(bundle + .getString("RandomizerGUI.mtNoExistLabel.text")); // NOI18N + + javax.swing.GroupLayout moveTutorsPanelLayout = new javax.swing.GroupLayout( + moveTutorsPanel); + moveTutorsPanel.setLayout(moveTutorsPanelLayout); + moveTutorsPanelLayout + .setHorizontalGroup(moveTutorsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + moveTutorsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + moveTutorsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + moveTutorsPanelLayout + .createSequentialGroup() + .addComponent( + mtMovesPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + mtCompatPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup( + moveTutorsPanelLayout + .createSequentialGroup() + .addComponent( + mtNoExistLabel) + .addGap(0, + 0, + Short.MAX_VALUE))) + .addContainerGap())); + moveTutorsPanelLayout + .setVerticalGroup(moveTutorsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + moveTutorsPanelLayout + .createSequentialGroup() + .addComponent(mtNoExistLabel) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addGroup( + moveTutorsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING, + false) + .addComponent( + mtCompatPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + mtMovesPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)))); + + inGameTradesPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.inGameTradesPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + ingameTradesButtonGroup.add(igtUnchangedRB); + igtUnchangedRB.setSelected(true); + igtUnchangedRB.setText(bundle + .getString("RandomizerGUI.igtUnchangedRB.text")); // NOI18N + igtUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.igtUnchangedRB.toolTipText")); // NOI18N + igtUnchangedRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + igtUnchangedRBActionPerformed(evt); + } + }); + + ingameTradesButtonGroup.add(igtGivenOnlyRB); + igtGivenOnlyRB.setText(bundle + .getString("RandomizerGUI.igtGivenOnlyRB.text")); // NOI18N + igtGivenOnlyRB.setToolTipText(bundle + .getString("RandomizerGUI.igtGivenOnlyRB.toolTipText")); // NOI18N + igtGivenOnlyRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + igtGivenOnlyRBActionPerformed(evt); + } + }); + + ingameTradesButtonGroup.add(igtBothRB); + igtBothRB.setText(bundle.getString("RandomizerGUI.igtBothRB.text")); // NOI18N + igtBothRB.setToolTipText(bundle + .getString("RandomizerGUI.igtBothRB.toolTipText")); // NOI18N + igtBothRB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + igtBothRBActionPerformed(evt); + } + }); + + igtRandomNicknameCB.setText(bundle + .getString("RandomizerGUI.igtRandomNicknameCB.text")); // NOI18N + igtRandomNicknameCB.setToolTipText(bundle + .getString("RandomizerGUI.igtRandomNicknameCB.toolTipText")); // NOI18N + + igtRandomOTCB.setText(bundle + .getString("RandomizerGUI.igtRandomOTCB.text")); // NOI18N + igtRandomOTCB.setToolTipText(bundle + .getString("RandomizerGUI.igtRandomOTCB.toolTipText")); // NOI18N + + igtRandomIVsCB.setText(bundle + .getString("RandomizerGUI.igtRandomIVsCB.text")); // NOI18N + igtRandomIVsCB.setToolTipText(bundle + .getString("RandomizerGUI.igtRandomIVsCB.toolTipText")); // NOI18N + + igtRandomItemCB.setText(bundle + .getString("RandomizerGUI.igtRandomItemCB.text")); // NOI18N + igtRandomItemCB.setToolTipText(bundle + .getString("RandomizerGUI.igtRandomItemCB.toolTipText")); // NOI18N + + javax.swing.GroupLayout inGameTradesPanelLayout = new javax.swing.GroupLayout( + inGameTradesPanel); + inGameTradesPanel.setLayout(inGameTradesPanelLayout); + inGameTradesPanelLayout + .setHorizontalGroup(inGameTradesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + javax.swing.GroupLayout.Alignment.TRAILING, + inGameTradesPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + inGameTradesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + igtUnchangedRB) + .addComponent( + igtGivenOnlyRB) + .addComponent(igtBothRB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addGroup( + inGameTradesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + igtRandomItemCB) + .addComponent( + igtRandomNicknameCB) + .addComponent( + igtRandomOTCB) + .addComponent( + igtRandomIVsCB)) + .addGap(113, 113, 113))); + inGameTradesPanelLayout + .setVerticalGroup(inGameTradesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + inGameTradesPanelLayout + .createSequentialGroup() + .addGroup( + inGameTradesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + igtUnchangedRB) + .addComponent( + igtRandomNicknameCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + inGameTradesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent( + igtGivenOnlyRB) + .addComponent( + igtRandomOTCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + inGameTradesPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(igtBothRB) + .addComponent( + igtRandomIVsCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(igtRandomItemCB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + fieldItemsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.fieldItemsPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + fieldItemsButtonGroup.add(fiUnchangedRB); + fiUnchangedRB.setSelected(true); + fiUnchangedRB.setText(bundle + .getString("RandomizerGUI.fiUnchangedRB.text")); // NOI18N + fiUnchangedRB.setToolTipText(bundle + .getString("RandomizerGUI.fiUnchangedRB.toolTipText")); // NOI18N + + fieldItemsButtonGroup.add(fiShuffleRB); + fiShuffleRB.setText(bundle.getString("RandomizerGUI.fiShuffleRB.text")); // NOI18N + fiShuffleRB.setToolTipText(bundle + .getString("RandomizerGUI.fiShuffleRB.toolTipText")); // NOI18N + + fieldItemsButtonGroup.add(fiRandomRB); + fiRandomRB.setText(bundle.getString("RandomizerGUI.fiRandomRB.text")); // NOI18N + fiRandomRB.setToolTipText(bundle + .getString("RandomizerGUI.fiRandomRB.toolTipText")); // NOI18N + + javax.swing.GroupLayout fieldItemsPanelLayout = new javax.swing.GroupLayout( + fieldItemsPanel); + fieldItemsPanel.setLayout(fieldItemsPanelLayout); + fieldItemsPanelLayout + .setHorizontalGroup(fieldItemsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + fieldItemsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + fieldItemsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + fiUnchangedRB) + .addComponent( + fiShuffleRB) + .addComponent( + fiRandomRB)) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + fieldItemsPanelLayout + .setVerticalGroup(fieldItemsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + fieldItemsPanelLayout + .createSequentialGroup() + .addComponent(fiUnchangedRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(fiShuffleRB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(fiRandomRB))); + + javax.swing.GroupLayout optionsContainerPanelLayout = new javax.swing.GroupLayout( + optionsContainerPanel); + optionsContainerPanel.setLayout(optionsContainerPanelLayout); + optionsContainerPanelLayout + .setHorizontalGroup(optionsContainerPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(pokemonTypesPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(pokemonMovesetsPanel, + javax.swing.GroupLayout.Alignment.TRAILING, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(trainersPokemonPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(wildPokemonPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(starterPokemonPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(staticPokemonPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(tmhmsPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addGroup( + optionsContainerPanelLayout + .createSequentialGroup() + .addComponent( + baseStatsPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + abilitiesPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addComponent(moveTutorsPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(inGameTradesPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(fieldItemsPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)); + optionsContainerPanelLayout + .setVerticalGroup(optionsContainerPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + optionsContainerPanelLayout + .createSequentialGroup() + .addGroup( + optionsContainerPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING, + false) + .addComponent( + baseStatsPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + abilitiesPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + starterPokemonPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + pokemonTypesPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + pokemonMovesetsPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + trainersPokemonPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + wildPokemonPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + staticPokemonPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + tmhmsPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + moveTutorsPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + inGameTradesPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + fieldItemsPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + optionsScrollPane.setViewportView(optionsContainerPanel); + + openROMButton.setText(bundle + .getString("RandomizerGUI.openROMButton.text")); // NOI18N + openROMButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + openROMButtonActionPerformed(evt); + } + }); + + saveROMButton.setText(bundle + .getString("RandomizerGUI.saveROMButton.text")); // NOI18N + saveROMButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + saveROMButtonActionPerformed(evt); + } + }); + + usePresetsButton.setText(bundle + .getString("RandomizerGUI.usePresetsButton.text")); // NOI18N + usePresetsButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + usePresetsButtonActionPerformed(evt); + } + }); + + aboutButton.setText(bundle.getString("RandomizerGUI.aboutButton.text")); // NOI18N + aboutButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + aboutButtonActionPerformed(evt); + } + }); + + otherOptionsPanel + .setBorder(javax.swing.BorderFactory.createTitledBorder( + null, + bundle.getString("RandomizerGUI.otherOptionsPanel.border.title"), + javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, + javax.swing.border.TitledBorder.DEFAULT_POSITION, + new java.awt.Font("Tahoma", 1, 11))); // NOI18N + + codeTweaksCB.setText(bundle + .getString("RandomizerGUI.codeTweaksCB.text")); // NOI18N + codeTweaksCB.setToolTipText(bundle + .getString("RandomizerGUI.codeTweaksCB.toolTipText")); // NOI18N + codeTweaksCB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + codeTweaksCBActionPerformed(evt); + } + }); + + raceModeCB.setText(bundle.getString("RandomizerGUI.raceModeCB.text")); // NOI18N + raceModeCB.setToolTipText(bundle + .getString("RandomizerGUI.raceModeCB.toolTipText")); // NOI18N + + randomizeHollowsCB.setText(bundle + .getString("RandomizerGUI.randomizeHollowsCB.text")); // NOI18N + randomizeHollowsCB.setToolTipText(bundle + .getString("RandomizerGUI.randomizeHollowsCB.toolTipText")); // NOI18N + + brokenMovesCB.setText(bundle + .getString("RandomizerGUI.brokenMovesCB.text")); // NOI18N + brokenMovesCB.setToolTipText(bundle + .getString("RandomizerGUI.brokenMovesCB.toolTipText")); // NOI18N + + codeTweaksBtn.setText(bundle + .getString("RandomizerGUI.codeTweaksBtn.text")); // NOI18N + codeTweaksBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + codeTweaksBtnActionPerformed(evt); + } + }); + + pokeLimitCB.setText(bundle.getString("RandomizerGUI.pokeLimitCB.text")); // NOI18N + pokeLimitCB.setToolTipText(bundle + .getString("RandomizerGUI.pokeLimitCB.toolTipText")); // NOI18N + pokeLimitCB.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pokeLimitCBActionPerformed(evt); + } + }); + + pokeLimitBtn.setText(bundle + .getString("RandomizerGUI.pokeLimitBtn.text")); // NOI18N + pokeLimitBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + pokeLimitBtnActionPerformed(evt); + } + }); + + javax.swing.GroupLayout otherOptionsPanelLayout = new javax.swing.GroupLayout( + otherOptionsPanel); + otherOptionsPanel.setLayout(otherOptionsPanelLayout); + otherOptionsPanelLayout + .setHorizontalGroup(otherOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + otherOptionsPanelLayout + .createSequentialGroup() + .addContainerGap() + .addGroup( + otherOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + raceModeCB) + .addComponent( + brokenMovesCB) + .addGroup( + otherOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.TRAILING, + false) + .addGroup( + javax.swing.GroupLayout.Alignment.LEADING, + otherOptionsPanelLayout + .createSequentialGroup() + .addComponent( + codeTweaksCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + codeTweaksBtn, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addGroup( + javax.swing.GroupLayout.Alignment.LEADING, + otherOptionsPanelLayout + .createSequentialGroup() + .addComponent( + pokeLimitCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + pokeLimitBtn)))) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addGroup( + javax.swing.GroupLayout.Alignment.TRAILING, + otherOptionsPanelLayout + .createSequentialGroup() + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent(randomizeHollowsCB) + .addContainerGap())); + otherOptionsPanelLayout + .setVerticalGroup(otherOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + otherOptionsPanelLayout + .createSequentialGroup() + .addGroup( + otherOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + codeTweaksCB) + .addComponent( + codeTweaksBtn)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + otherOptionsPanelLayout + .createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + pokeLimitBtn) + .addComponent( + pokeLimitCB)) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(raceModeCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(randomizeHollowsCB) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(brokenMovesCB) + .addContainerGap( + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE))); + + loadQSButton.setText(bundle + .getString("RandomizerGUI.loadQSButton.text")); // NOI18N + loadQSButton.setToolTipText(bundle + .getString("RandomizerGUI.loadQSButton.toolTipText")); // NOI18N + loadQSButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + loadQSButtonActionPerformed(evt); + } + }); + + saveQSButton.setText(bundle + .getString("RandomizerGUI.saveQSButton.text")); // NOI18N + saveQSButton.setToolTipText(bundle + .getString("RandomizerGUI.saveQSButton.toolTipText")); // NOI18N + saveQSButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + saveQSButtonActionPerformed(evt); + } + }); + + updateSettingsButton.setText(bundle + .getString("RandomizerGUI.updateSettingsButton.text")); // NOI18N + updateSettingsButton + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + updateSettingsButtonActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout( + getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup(layout + .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + optionsScrollPane, + javax.swing.GroupLayout.DEFAULT_SIZE, + 747, Short.MAX_VALUE) + .addGroup( + layout.createSequentialGroup() + .addComponent( + generalOptionsPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent( + otherOptionsPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING, + false) + .addGroup( + layout.createSequentialGroup() + .addComponent( + loadQSButton) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + saveQSButton)) + .addComponent( + romInfoPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)) + .addGap(18, 18, + 18) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + saveROMButton, + javax.swing.GroupLayout.Alignment.TRAILING, + javax.swing.GroupLayout.DEFAULT_SIZE, + 147, + Short.MAX_VALUE) + .addComponent( + usePresetsButton, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + openROMButton, + javax.swing.GroupLayout.Alignment.TRAILING, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + updateSettingsButton, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + aboutButton, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE)))) + .addContainerGap())); + layout.setVerticalGroup(layout + .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING, + false) + .addComponent( + generalOptionsPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + otherOptionsPanel, + javax.swing.GroupLayout.DEFAULT_SIZE, + 0, + Short.MAX_VALUE)) + .addGroup( + layout.createSequentialGroup() + .addComponent( + romInfoPanel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent( + loadQSButton) + .addComponent( + saveQSButton))) + .addGroup( + layout.createSequentialGroup() + .addComponent( + openROMButton) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + saveROMButton) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + usePresetsButton) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + updateSettingsButton) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent( + aboutButton))) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(optionsScrollPane, + javax.swing.GroupLayout.DEFAULT_SIZE, + 380, Short.MAX_VALUE).addContainerGap())); + + pack(); + }// //GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel abilitiesPanel; + private javax.swing.JButton aboutButton; + private javax.swing.JPanel baseStatsPanel; + private javax.swing.JCheckBox brokenMovesCB; + private javax.swing.JButton codeTweaksBtn; + private javax.swing.JCheckBox codeTweaksCB; + private javax.swing.JRadioButton fiRandomRB; + private javax.swing.JRadioButton fiShuffleRB; + private javax.swing.JRadioButton fiUnchangedRB; + private javax.swing.ButtonGroup fieldItemsButtonGroup; + private javax.swing.JPanel fieldItemsPanel; + private javax.swing.JPanel generalOptionsPanel; + private javax.swing.JCheckBox goLowerCaseNamesCheckBox; + private javax.swing.JCheckBox goNationalDexCheckBox; + private javax.swing.JCheckBox goRemoveTradeEvosCheckBox; + private javax.swing.JCheckBox goUpdateMovesCheckBox; + private javax.swing.JCheckBox goUpdateMovesLegacyCheckBox; + private javax.swing.JCheckBox goUpdateTypesCheckBox; + private javax.swing.JRadioButton igtBothRB; + private javax.swing.JRadioButton igtGivenOnlyRB; + private javax.swing.JCheckBox igtRandomIVsCB; + private javax.swing.JCheckBox igtRandomItemCB; + private javax.swing.JCheckBox igtRandomNicknameCB; + private javax.swing.JCheckBox igtRandomOTCB; + private javax.swing.JRadioButton igtUnchangedRB; + private javax.swing.JPanel inGameTradesPanel; + private javax.swing.ButtonGroup ingameTradesButtonGroup; + private javax.swing.JButton loadQSButton; + private javax.swing.JMenuItem manualUpdateMenuItem; + private javax.swing.JPanel moveTutorsPanel; + private javax.swing.JPanel mtCompatPanel; + private javax.swing.ButtonGroup mtCompatibilityButtonGroup; + private javax.swing.ButtonGroup mtMovesButtonGroup; + private javax.swing.JPanel mtMovesPanel; + private javax.swing.JLabel mtNoExistLabel; + private javax.swing.JRadioButton mtcRandomTotalRB; + private javax.swing.JRadioButton mtcRandomTypeRB; + private javax.swing.JRadioButton mtcUnchangedRB; + private javax.swing.JRadioButton mtmRandomRB; + private javax.swing.JRadioButton mtmUnchangedRB; + private javax.swing.JButton openROMButton; + private javax.swing.JPanel optionsContainerPanel; + private javax.swing.JScrollPane optionsScrollPane; + private javax.swing.JPanel otherOptionsPanel; + private javax.swing.JRadioButton paRandomizeRB; + private javax.swing.JRadioButton paUnchangedRB; + private javax.swing.JCheckBox paWonderGuardCB; + private javax.swing.JRadioButton pbsChangesRandomEvosRB; + private javax.swing.JRadioButton pbsChangesRandomTotalRB; + private javax.swing.JRadioButton pbsChangesShuffleRB; + private javax.swing.JRadioButton pbsChangesUnchangedRB; + private javax.swing.JCheckBox pbsStandardEXPCurvesCB; + private javax.swing.JCheckBox pms4MovesCB; + private javax.swing.JRadioButton pmsMetronomeOnlyRB; + private javax.swing.JRadioButton pmsRandomTotalRB; + private javax.swing.JRadioButton pmsRandomTypeRB; + private javax.swing.JRadioButton pmsUnchangedRB; + private javax.swing.ButtonGroup pokeAbilitiesButtonGroup; + private javax.swing.JButton pokeLimitBtn; + private javax.swing.JCheckBox pokeLimitCB; + private javax.swing.ButtonGroup pokeMovesetsButtonGroup; + private javax.swing.ButtonGroup pokeStatChangesButtonGroup; + private javax.swing.ButtonGroup pokeTypesButtonGroup; + private javax.swing.JPanel pokemonMovesetsPanel; + private javax.swing.JPanel pokemonTypesPanel; + private javax.swing.JRadioButton ptRandomFollowEvosRB; + private javax.swing.JRadioButton ptRandomTotalRB; + private javax.swing.JRadioButton ptUnchangedRB; + private javax.swing.JFileChooser qsOpenChooser; + private javax.swing.JFileChooser qsSaveChooser; + private javax.swing.JCheckBox raceModeCB; + private javax.swing.JCheckBox randomizeHollowsCB; + private javax.swing.JLabel riRomCodeLabel; + private javax.swing.JLabel riRomNameLabel; + private javax.swing.JLabel riRomSupportLabel; + private javax.swing.JPanel romInfoPanel; + private javax.swing.JFileChooser romOpenChooser; + private javax.swing.JFileChooser romSaveChooser; + private javax.swing.JButton saveQSButton; + private javax.swing.JButton saveROMButton; + private javax.swing.JComboBox spCustomPoke1Chooser; + private javax.swing.JComboBox spCustomPoke2Chooser; + private javax.swing.JComboBox spCustomPoke3Chooser; + private javax.swing.JRadioButton spCustomRB; + private javax.swing.JCheckBox spHeldItemsCB; + private javax.swing.JRadioButton spRandom2EvosRB; + private javax.swing.JRadioButton spRandomRB; + private javax.swing.JRadioButton spUnchangedRB; + private javax.swing.ButtonGroup starterPokemonButtonGroup; + private javax.swing.JPanel starterPokemonPanel; + private javax.swing.ButtonGroup staticPokemonButtonGroup; + private javax.swing.JPanel staticPokemonPanel; + private javax.swing.JRadioButton stpRandomL4LRB; + private javax.swing.JRadioButton stpRandomTotalRB; + private javax.swing.JRadioButton stpUnchangedRB; + private javax.swing.JCheckBox tcnRandomizeCB; + private javax.swing.JRadioButton thcRandomTotalRB; + private javax.swing.JRadioButton thcRandomTypeRB; + private javax.swing.JRadioButton thcUnchangedRB; + private javax.swing.JPanel tmHmCompatPanel; + private javax.swing.ButtonGroup tmHmCompatibilityButtonGroup; + private javax.swing.ButtonGroup tmMovesButtonGroup; + private javax.swing.JPanel tmMovesPanel; + private javax.swing.JPanel tmhmsPanel; + private javax.swing.JRadioButton tmmRandomRB; + private javax.swing.JRadioButton tmmUnchangedRB; + private javax.swing.JCheckBox tnRandomizeCB; + private javax.swing.JMenuItem toggleAutoUpdatesMenuItem; + private javax.swing.JCheckBox tpNoEarlyShedinjaCB; + private javax.swing.JCheckBox tpNoLegendariesCB; + private javax.swing.JCheckBox tpPowerLevelsCB; + private javax.swing.JRadioButton tpRandomRB; + private javax.swing.JCheckBox tpRivalCarriesStarterCB; + private javax.swing.JRadioButton tpTypeThemedRB; + private javax.swing.JCheckBox tpTypeWeightingCB; + private javax.swing.JRadioButton tpUnchangedRB; + private javax.swing.ButtonGroup trainerPokesButtonGroup; + private javax.swing.JPanel trainersPokemonPanel; + private javax.swing.JButton updateSettingsButton; + private javax.swing.JPopupMenu updateSettingsMenu; + private javax.swing.JButton usePresetsButton; + private javax.swing.JPanel wildPokemonARulePanel; + private javax.swing.JPanel wildPokemonPanel; + private javax.swing.ButtonGroup wildPokesARuleButtonGroup; + private javax.swing.ButtonGroup wildPokesButtonGroup; + private javax.swing.JRadioButton wpARCatchEmAllRB; + private javax.swing.JRadioButton wpARNoneRB; + private javax.swing.JRadioButton wpARSimilarStrengthRB; + private javax.swing.JRadioButton wpARTypeThemedRB; + private javax.swing.JRadioButton wpArea11RB; + private javax.swing.JCheckBox wpCatchRateCB; + private javax.swing.JRadioButton wpGlobalRB; + private javax.swing.JCheckBox wpHeldItemsCB; + private javax.swing.JCheckBox wpNoLegendariesCB; + private javax.swing.JRadioButton wpRandomRB; + private javax.swing.JRadioButton wpUnchangedRB; + private javax.swing.JCheckBox wpUseTimeCB; + // End of variables declaration//GEN-END:variables +} diff --git a/src/com/dabomstew/pkrandom/gui/UpdateCheckThread.java b/src/com/dabomstew/pkrandom/gui/UpdateCheckThread.java new file mode 100755 index 000000000..c108fabf2 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/UpdateCheckThread.java @@ -0,0 +1,75 @@ +package com.dabomstew.pkrandom.gui; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Scanner; + +import javax.swing.SwingUtilities; + +import com.dabomstew.pkrandom.FileFunctions; + +public class UpdateCheckThread extends Thread { + private RandomizerGUI mainWindow; + private boolean doNotifyNone; + + public UpdateCheckThread(RandomizerGUI mainWindow, boolean doNotifyNone) { + this.mainWindow = mainWindow; + this.doNotifyNone = doNotifyNone; + } + + @Override + public void run() { + boolean found = false; + try { + byte[] versionCheck = FileFunctions + .downloadFile("http://pokehacks.dabomstew.com/randomizer/autoupdate/latest.txt"); + int version = Integer.parseInt(new String(versionCheck)); + if (version > RandomizerGUI.UPDATE_VERSION) { + byte[] output = FileFunctions + .downloadFile("http://pokehacks.dabomstew.com/randomizer/autoupdate/version.txt"); + Scanner sc = new Scanner(new ByteArrayInputStream(output)); + final int newVersion = Integer.parseInt(sc.nextLine()); + if (newVersion > RandomizerGUI.UPDATE_VERSION) { + // Update found, parse changelog + StringBuilder changelog = new StringBuilder(); + int clid = Integer.parseInt(sc.nextLine()); + String nl = System.getProperty("line.separator"); + boolean first = true; + while (clid > RandomizerGUI.UPDATE_VERSION && sc.hasNext()) { + if (!first) { + changelog.append(nl); + } + first = false; + String line = sc.nextLine(); + while (line.equals("EOV") == false && sc.hasNext()) { + changelog.append(line + nl); + line = sc.nextLine(); + } + clid = Integer.parseInt(sc.nextLine()); + } + final String doneCL = changelog.toString(); + found = true; + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainWindow.updateFound(newVersion, doneCL); + } + }); + } + } + } catch (IOException ex) { + ex.printStackTrace(); + } catch (NumberFormatException ex) { + ex.printStackTrace(); + } + if (!found && this.doNotifyNone) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainWindow.noUpdateFound(); + } + }); + } + } + +} diff --git a/src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.form b/src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.form new file mode 100755 index 000000000..bee7442c0 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.form @@ -0,0 +1,105 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.java b/src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.java new file mode 100755 index 000000000..305acfa11 --- /dev/null +++ b/src/com/dabomstew/pkrandom/gui/UpdateFoundDialog.java @@ -0,0 +1,281 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.dabomstew.pkrandom.gui; + +import java.awt.Desktop; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.util.Scanner; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import javax.swing.JOptionPane; + +import com.dabomstew.pkrandom.FileFunctions; + +/** + * + * @author Stewart + */ +public class UpdateFoundDialog extends javax.swing.JDialog { + + /** + * + */ + private static final long serialVersionUID = -1641494584047215623L; + private int targetVersion; + + /** + * Creates new form UpdateFoundDialog + */ + public UpdateFoundDialog(RandomizerGUI parent, int newVersion, + String changelog) { + super(parent, true); + initComponents(); + targetVersion = newVersion; + Scanner sc = new Scanner(changelog); + String firstLine = htmlspecialchars(sc.nextLine()); + sc.close(); + updateFoundLabel + .setText("An update to the Randomizer has been found: " + + firstLine + + ".
You can see the changes made since your last update below."); + while (changelog.trim().toLowerCase().startsWith("")) { + changelog = changelog.trim().substring(6); + } + updateChangelogArea.setText(changelog); + updateChangelogArea.setCaretPosition(0); + this.setLocationRelativeTo(parent); + this.setVisible(true); + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + // //GEN-BEGIN:initComponents + private void initComponents() { + + updateFoundLabel = new javax.swing.JLabel(); + clScroller = new javax.swing.JScrollPane(); + updateChangelogArea = new javax.swing.JTextArea(); + downloadUpdateBtn = new javax.swing.JButton(); + closeBtn = new javax.swing.JButton(); + + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + + updateFoundLabel + .setText("An update to the Randomizer has been found: x.x.x, released blah.
You can see the changes made below."); + + clScroller + .setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + + updateChangelogArea.setEditable(false); + updateChangelogArea.setColumns(40); + updateChangelogArea.setLineWrap(true); + updateChangelogArea.setRows(5); + updateChangelogArea.setWrapStyleWord(true); + updateChangelogArea.setEnabled(false); + clScroller.setViewportView(updateChangelogArea); + + downloadUpdateBtn.setText("Download Update Now"); + downloadUpdateBtn + .addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + downloadUpdateBtnActionPerformed(evt); + } + }); + + closeBtn.setText("Close Without Updating"); + closeBtn.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + closeBtnActionPerformed(evt); + } + }); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout( + getContentPane()); + getContentPane().setLayout(layout); + layout.setHorizontalGroup(layout + .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(clScroller) + .addGroup( + layout.createSequentialGroup() + .addComponent( + updateFoundLabel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, + 161, + Short.MAX_VALUE)) + .addGroup( + layout.createSequentialGroup() + .addComponent( + downloadUpdateBtn) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addComponent( + closeBtn))) + .addContainerGap())); + layout.setVerticalGroup(layout + .createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup( + layout.createSequentialGroup() + .addContainerGap() + .addComponent(updateFoundLabel, + javax.swing.GroupLayout.PREFERRED_SIZE, + javax.swing.GroupLayout.DEFAULT_SIZE, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(clScroller, + javax.swing.GroupLayout.PREFERRED_SIZE, + 209, + javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap( + javax.swing.LayoutStyle.ComponentPlacement.RELATED, + javax.swing.GroupLayout.DEFAULT_SIZE, + Short.MAX_VALUE) + .addGroup( + layout.createParallelGroup( + javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(downloadUpdateBtn) + .addComponent(closeBtn)) + .addContainerGap())); + + pack(); + }//
//GEN-END:initComponents + + private void downloadUpdateBtnActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_downloadUpdateBtnActionPerformed + // External: download delta file + String deltaFile = "delta_" + RandomizerGUI.UPDATE_VERSION + "_" + + targetVersion + ".zip"; + try { + byte[] zip = FileFunctions + .downloadFile("http://pokehacks.dabomstew.com/randomizer/autoupdate/" + + deltaFile); + extract(zip, new File("./")); + JOptionPane + .showMessageDialog( + this, + "Update complete - the randomizer will now close.\n" + + "You should now re-open the program and begin using the new version."); + System.exit(0); + } catch (IOException ex) { + JOptionPane + .showMessageDialog( + this, + "Automatic update not available.\n" + + "You will now be taken to the website to download it manually."); + attemptOpenBrowser(); + } + }// GEN-LAST:event_downloadUpdateBtnActionPerformed + + private void attemptOpenBrowser() { + String targetURL = "http://pokehacks.dabomstew.com/randomizer/downloads.php"; + Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() + : null; + + if (desktop == null || !desktop.isSupported(Desktop.Action.BROWSE)) { + JOptionPane + .showMessageDialog( + this, + "The downloads page could not be opened automatically.\n" + + "Open a browser and navigate to the below link to download the update:\n" + + targetURL); + } else { + try { + desktop.browse(new URI(targetURL)); + } catch (Exception e) { + JOptionPane + .showMessageDialog( + this, + "The downloads page could not be opened automatically.\n" + + "Open a browser and navigate to the below link to download the update:\n" + + targetURL); + } + } + this.setVisible(false); + } + + private void extractFile(ZipInputStream in, File outdir, String name) + throws IOException { + byte[] buffer = new byte[4096]; + BufferedOutputStream out = new BufferedOutputStream( + new FileOutputStream(new File(outdir, name))); + int count = -1; + while ((count = in.read(buffer)) != -1) + out.write(buffer, 0, count); + out.close(); + } + + private void mkdirs(File outdir, String path) { + File d = new File(outdir, path); + if (!d.exists()) + d.mkdirs(); + } + + private String dirpart(String name) { + int s = name.lastIndexOf(File.separatorChar); + return s == -1 ? null : name.substring(0, s); + } + + private String htmlspecialchars(String original) { + return original.replace("&", "&").replace("<", "<") + .replace(">", ">"); + } + + public void extract(byte[] zipfile, File outdir) throws IOException { + ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream( + zipfile)); + ZipEntry entry; + String name, dir; + while ((entry = zin.getNextEntry()) != null) { + name = entry.getName(); + if (entry.isDirectory()) { + mkdirs(outdir, name); + continue; + } + /* + * this part is necessary because file entry can come before + * directory entry where is file located i.e.: /foo/foo.txt /foo/ + */ + dir = dirpart(name); + if (dir != null) + mkdirs(outdir, dir); + + extractFile(zin, outdir, name); + } + zin.close(); + } + + private void closeBtnActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_closeBtnActionPerformed + this.setVisible(false); + }// GEN-LAST:event_closeBtnActionPerformed + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JScrollPane clScroller; + private javax.swing.JButton closeBtn; + private javax.swing.JButton downloadUpdateBtn; + private javax.swing.JTextArea updateChangelogArea; + private javax.swing.JLabel updateFoundLabel; + // End of variables declaration//GEN-END:variables +} diff --git a/src/com/dabomstew/pkrandom/gui/loading.gif b/src/com/dabomstew/pkrandom/gui/loading.gif new file mode 100755 index 0000000000000000000000000000000000000000..3288d1035d70bb86517e2c233f1a904e41f06b29 GIT binary patch literal 3208 zcmc(iX;4#H9>pJdFE7h`I{IF)0|5<6L}(j=N}5%L009EB2nYfyF)E0PvIqo$u!IC; z4PgyY5|S9AEh38G)(9eq4TbH7_UHg@yWrlIJ$6smIADL7s^P;_O;ykRc9soXl`UC*LwQJXkii*0rx|*7rI2=x7WaRkx_~XZqFJ8R3c=2Kg zf@aSAv8+BJ8+^hyay>(QR@t*blbKzsf0}bscEqRc5Hd3o(-N5RyW=zWB*zQw6Zh>* z2CROCDAbu#D`)S|J_o(lL9Yn3l*+8RdiRD_>iNz$#_IAzCna&Wl5 zSF_(rRCDD!wi#i8oAm&jYtn2_@VB%2-H*G%bN#|(6R6N?wM)3u`PiGzwuX7qmTgyF zpE)h0kuoxQ9?=kW7Y!=R@DmhU9)vwT*EZWzJ zrt+=2tqFts72yIp?|gvdLhs8Hfku^Z(){gmN%Y=K#P|%fkvgUj~HfIp3CuXqCtYGtJ#me+n+-LmP( z*XNuk%!aH8bIE@_Bj46>M*dSro|7<6vZ7WUHh5YQzN$>IJFqCb|CT!wj~R2C2%=q{ zpt8rzY$aw?W?=Ustv{jo?Ow@ZRkLe<)NItY>Cyhle*wR59dTdF6(@{5^ zAQBOB*hNtc3bkY-8{Cm$nFS@elbTtSqrt7MB{h_4y+~`!mVa}?c&N>&?P}GqdMuhQ z&@TD5Czd((DcG_Su~dKKV)Pj$-qi1WHM8_vc^O4?^!oY|tmK~i!{fjd&@_1E(T~r7 z_REZy&hMT^ySJB3W7l$4YhR`M(J7S5S~+4Q&3HPa)z%zPpisOp$^ zTEe99ig2$5_qFr!$;7A6CJ}PJmRhli>w?LC}Y`#HLGy6 zMU4EhL~dKCN5Ut;U2jd*83ShBNiu zcJB0l9>1Modc?-oM<R4?}3g}UJ%@K);kriq>)e*rh%hdqM)5Q)*+O8 zXm;SEbs@koiYS!9YXIclSg+5m_s~yrW#kKMdiRszg(gCP5HPmP7L)vCf8@fxUh6qY z@Z#TmkjzAZX{rwE+q|K~F2v5{_@vt%>yT_a#fF03SFt{0RXvDAiaY~K9CgS1O>frXgAjBCS}mEd4mIWZ$=ovd5| zR?GRdU}d6+Q`+JRW)|=v7$)XNkn3yE`!nAiSCvOB1jKT zG<1aK3s<0b0m==egTD#8i(Of=1pGDTOCho0XpIOMQ&P87cVKY1W=C6kIg z9cH=@a&zbm2+`|{(_?YC9fdm?1TY~-pwlBn?>=(~1pDKbco6jloP;0-cqRiwV1A_S zEyV0Dj8Pwy!nekzaN>{)7rgZ&_QLxK{~1yRe865^yx>}+a!ECd>#MMwddow z@CU{l+Rt$xuXuf}?ga{3IAr?Raql^c@a%sI0U5m}HvJ5O1#I%_MMPt#BH>OqUZ{-k zt>4Xzz=%jT*FVW(uYkWyx}9Gw$HdN*qU?Bit#ji(Wi7p-u|_8?h^%szIS^s^fNM}b zgGy>|=cbEufpguY5_6w~&ZLv=Bo06UF9EYIY;Er-1VK)SyF&!|J{axiE1z^(hXwVq zsFS=K-#zC}CcOs^8W{KAt+kK)jYDgDYbCXv{{rwsgqtIU3<910$CJi)s?? z_t8k{>7*0~4l~LLF7$WXT5OSq5QCTbP_l!SN|{R}3D&eWA8~0ltWh1IL+ZBX4rRSt zWF6Om3WDMu4xK^1(BF`2cL}rUCzhHAB`@j5&R-yk_l*t;mPGY|u2^o|myvcOdrg0W z%=lX;f^Vkqfp?u7*4qQq%A3Mpf!xspWBSKS@O%r*TSM}?dl(@*%{0Jm_8;(h{R__M Bt. --*/ +/*----------------------------------------------------------------------------*/ + +public class CRC16 { + private static final int[] table = { 0x0000, 0xC0C1, 0xC181, 0x0140, + 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, + 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, + 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, + 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, + 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, + 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, + 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, + 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, + 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, + 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, + 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, + 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, + 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, + 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, + 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, + 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, + 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, + 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, + 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, + 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, + 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, + 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, + 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, + 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, + 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, + 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, + 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, + 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, + 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, + 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, + 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, + 0x4100, 0x81C1, 0x8081, 0x4040 }; + + public static short calculate(byte[] data, int offset, int length) { + int crc = 0xFFFF; + for (int i = 0; i < length; i++) { + crc = ((crc >>> 8) ^ table[(crc ^ data[i + offset]) & 0xff]); + } + return (short) crc; + } +} \ No newline at end of file diff --git a/src/com/dabomstew/pkrandom/newnds/NDSFile.java b/src/com/dabomstew/pkrandom/newnds/NDSFile.java new file mode 100755 index 000000000..074c1affc --- /dev/null +++ b/src/com/dabomstew/pkrandom/newnds/NDSFile.java @@ -0,0 +1,122 @@ +package com.dabomstew.pkrandom.newnds; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +/*----------------------------------------------------------------------------*/ +/*-- NDSFile.java - an entry in the FAT/FNT filesystem --*/ +/*-- Code based on "Nintendo DS rom tool", copyright (C) DevkitPro --*/ +/*-- Original Code by Rafael Vuijk, Dave Murphy, Alexei Karpenko --*/ +/*-- --*/ +/*-- Ported to Java by Dabomstew under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class NDSFile { + + private NDSRom parent; + public int offset, size; + public int fileID; + public String fullPath; + public Extracted status = Extracted.NOT; + public String extFilename; + public byte[] data; + + public NDSFile(NDSRom parent) { + this.parent = parent; + } + + public byte[] getContents() throws IOException { + if (this.status == Extracted.NOT) { + // extract file + parent.reopenROM(); + RandomAccessFile rom = parent.getBaseRom(); + byte[] buf = new byte[this.size]; + rom.seek(this.offset); + rom.readFully(buf); + if (parent.isWritingEnabled()) { + // make a file + String tmpDir = parent.getTmpFolder(); + String tmpFilename = fullPath.replaceAll("[^A-Za-z0-9_]+", ""); + this.extFilename = tmpFilename; + File tmpFile = new File(tmpDir + extFilename); + FileOutputStream fos = new FileOutputStream(tmpFile); + fos.write(buf); + fos.close(); + tmpFile.deleteOnExit(); + this.status = Extracted.TO_FILE; + this.data = null; + return buf; + } else { + this.status = Extracted.TO_RAM; + this.data = buf; + byte[] newcopy = new byte[buf.length]; + System.arraycopy(buf, 0, newcopy, 0, buf.length); + return newcopy; + } + } else if (this.status == Extracted.TO_RAM) { + byte[] newcopy = new byte[this.data.length]; + System.arraycopy(this.data, 0, newcopy, 0, this.data.length); + return newcopy; + } else { + String tmpDir = parent.getTmpFolder(); + FileInputStream fis = new FileInputStream(tmpDir + this.extFilename); + byte[] file = new byte[fis.available()]; + fis.read(file); + fis.close(); + return file; + } + } + + public void writeOverride(byte[] data) throws IOException { + if (status == Extracted.NOT) { + // temp extract + getContents(); + } + if (status == Extracted.TO_FILE) { + String tmpDir = parent.getTmpFolder(); + FileOutputStream fos = new FileOutputStream(new File(tmpDir + + this.extFilename)); + fos.write(data); + fos.close(); + } else { + if (this.data.length == data.length) { + // copy new in + System.arraycopy(data, 0, this.data, 0, data.length); + } else { + // make new array + this.data = null; + this.data = new byte[data.length]; + System.arraycopy(data, 0, this.data, 0, data.length); + } + } + } + + // returns null if no override + public byte[] getOverrideContents() throws IOException { + if (status == Extracted.NOT) { + return null; + } + return getContents(); + } + + private enum Extracted { + NOT, TO_FILE, TO_RAM; + } + +} diff --git a/src/com/dabomstew/pkrandom/newnds/NDSRom.java b/src/com/dabomstew/pkrandom/newnds/NDSRom.java new file mode 100755 index 000000000..db8cd36ba --- /dev/null +++ b/src/com/dabomstew/pkrandom/newnds/NDSRom.java @@ -0,0 +1,699 @@ +package com.dabomstew.pkrandom.newnds; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import com.dabomstew.pkrandom.RomFunctions; +import com.dabomstew.pkrandom.gui.RandomizerGUI; + +import cuecompressors.BLZCoder; + +/*----------------------------------------------------------------------------*/ +/*-- NDSRom.java - base class for opening/saving ROMs --*/ +/*-- Code based on "Nintendo DS rom tool", copyright (C) DevkitPro --*/ +/*-- Original Code by Rafael Vuijk, Dave Murphy, Alexei Karpenko --*/ +/*-- --*/ +/*-- Ported to Java by Dabomstew under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class NDSRom { + + private String romFilename; + private RandomAccessFile baseRom; + private boolean romOpen; + private Map files; + private Map filesByID; + private Map arm9overlaysByFileID; + private NDSY9Entry[] arm9overlays; + private byte[] fat; + private String tmpFolder; + private boolean writingEnabled; + private boolean arm9_open, arm9_changed, arm9_has_footer; + private boolean arm9_compressed; + private int arm9_szmode, arm9_szoffset; + private byte[] arm9_footer; + private byte[] arm9_ramstored; + + private static final int arm9_align = 0x1FF, arm7_align = 0x1FF; + private static final int fnt_align = 0x1FF, fat_align = 0x1FF; + private static final int banner_align = 0x1FF, file_align = 0x1FF; + + public NDSRom(String filename) throws IOException { + this.romFilename = filename; + this.baseRom = new RandomAccessFile(filename, "r"); + this.romOpen = true; + // TMP folder? + String rawFilename = new File(filename).getName(); + String dataFolder = "tmp_" + + rawFilename.substring(0, rawFilename.lastIndexOf('.')); + // remove nonsensical chars + dataFolder = dataFolder.replaceAll("[^A-Za-z0-9_]+", ""); + File tmpFolder = new File(RandomizerGUI.getRootPath() + dataFolder); + tmpFolder.mkdir(); + if (tmpFolder.canWrite()) { + writingEnabled = true; + this.tmpFolder = RandomizerGUI.getRootPath() + dataFolder + + File.separator; + tmpFolder.deleteOnExit(); + } else { + writingEnabled = false; + } + readFileSystem(); + arm9_open = false; + arm9_changed = false; + arm9_ramstored = null; + } + + public void reopenROM() throws IOException { + if (!this.romOpen) { + this.baseRom = new RandomAccessFile(this.romFilename, "r"); + this.romOpen = true; + } + } + + public void closeROM() throws IOException { + if (this.romOpen && this.baseRom != null) { + this.baseRom.close(); + this.baseRom = null; + this.romOpen = false; + } + } + + private void readFileSystem() throws IOException { + baseRom.seek(0x40); + int fntOffset = readFromFile(baseRom, 4); + readFromFile(baseRom, 4); // fntSize not needed + int fatOffset = readFromFile(baseRom, 4); + int fatSize = readFromFile(baseRom, 4); + + // Read full FAT table + baseRom.seek(fatOffset); + fat = new byte[fatSize]; + baseRom.readFully(fat); + + Map directoryPaths = new HashMap(); + directoryPaths.put(0xF000, ""); + int dircount = readFromFile(baseRom, fntOffset + 0x6, 2); + files = new HashMap(); + filesByID = new HashMap(); + + // read fnt table + baseRom.seek(fntOffset); + int[] subTableOffsets = new int[dircount]; + int[] firstFileIDs = new int[dircount]; + int[] parentDirIDs = new int[dircount]; + for (int i = 0; i < dircount && i < 0x1000; i++) { + subTableOffsets[i] = readFromFile(baseRom, 4) + fntOffset; + firstFileIDs[i] = readFromFile(baseRom, 2); + parentDirIDs[i] = readFromFile(baseRom, 2); + } + + // get dirnames + String[] directoryNames = new String[dircount]; + Map filenames = new TreeMap(); + Map fileDirectories = new HashMap(); + for (int i = 0; i < dircount && i < 0x1000; i++) { + firstPassDirectory(i, subTableOffsets[i], firstFileIDs[i], + directoryNames, filenames, fileDirectories); + } + + // get full dirnames + for (int i = 1; i < dircount && i < 0x1000; i++) { + String dirname = directoryNames[i]; + if (dirname != null) { + String fullDirName = ""; + int curDir = i; + while (dirname != null && !dirname.isEmpty()) { + if (!fullDirName.isEmpty()) { + fullDirName = "/" + fullDirName; + } + fullDirName = dirname + fullDirName; + int parentDir = parentDirIDs[curDir]; + if (parentDir >= 0xF001 && parentDir <= 0xFFFF) { + curDir = parentDir - 0xF000; + dirname = directoryNames[curDir]; + } else { + break; + } + } + directoryPaths.put(i + 0xF000, fullDirName); + } else { + directoryPaths.put(i + 0xF000, ""); + } + } + + // parse files + for (int fileID : filenames.keySet()) { + String filename = filenames.get(fileID); + int directory = fileDirectories.get(fileID); + String dirPath = directoryPaths.get(directory + 0xF000); + String fullFilename = filename; + if (!dirPath.isEmpty()) { + fullFilename = dirPath + "/" + filename; + } + NDSFile nf = new NDSFile(this); + int start = readFromByteArr(fat, fileID * 8, 4); + int end = readFromByteArr(fat, fileID * 8 + 4, 4); + nf.offset = start; + nf.size = end - start; + nf.fullPath = fullFilename; + nf.fileID = fileID; + files.put(fullFilename, nf); + filesByID.put(fileID, nf); + } + + // arm9 overlays + int arm9_ovl_table_offset = readFromFile(baseRom, 0x50, 4); + int arm9_ovl_table_size = readFromFile(baseRom, 0x54, 4); + int arm9_ovl_count = arm9_ovl_table_size / 32; + byte[] y9table = new byte[arm9_ovl_table_size]; + arm9overlays = new NDSY9Entry[arm9_ovl_count]; + arm9overlaysByFileID = new HashMap(); + baseRom.seek(arm9_ovl_table_offset); + baseRom.readFully(y9table); + + // parse overlays + for (int i = 0; i < arm9_ovl_count; i++) { + NDSY9Entry overlay = new NDSY9Entry(this); + int fileID = readFromByteArr(y9table, i * 32 + 24, 4); + int start = readFromByteArr(fat, fileID * 8, 4); + int end = readFromByteArr(fat, fileID * 8 + 4, 4); + overlay.offset = start; + overlay.size = end - start; + overlay.original_size = end - start; + overlay.fileID = fileID; + overlay.overlay_id = i; + overlay.ram_address = readFromByteArr(y9table, i * 32 + 4, 4); + overlay.ram_size = readFromByteArr(y9table, i * 32 + 8, 4); + overlay.bss_size = readFromByteArr(y9table, i * 32 + 12, 4); + overlay.static_start = readFromByteArr(y9table, i * 32 + 16, 4); + overlay.static_end = readFromByteArr(y9table, i * 32 + 20, 4); + overlay.compressed_size = readFromByteArr(y9table, i * 32 + 28, 3); + overlay.compress_flag = y9table[i * 32 + 31] & 0xFF; + arm9overlays[i] = overlay; + arm9overlaysByFileID.put(fileID, overlay); + } + } + + public void saveTo(String filename) throws IOException { + this.reopenROM(); + + // Initialise new ROM + RandomAccessFile fNew = new RandomAccessFile(filename, "rw"); + + int headersize = readFromFile(this.baseRom, 0x84, 4); + this.baseRom.seek(0); + copy(this.baseRom, fNew, headersize); + + // arm9 + int arm9_offset = ((int) (fNew.getFilePointer() + arm9_align)) + & (~arm9_align); + int old_arm9_offset = readFromFile(this.baseRom, 0x20, 4); + int arm9_size = readFromFile(this.baseRom, 0x2C, 4); + if (arm9_open && arm9_changed) { + // custom arm9 + byte[] newARM9 = getARM9(); + if (arm9_compressed) { + newARM9 = new BLZCoder(null).BLZ_EncodePub(newARM9, true, + false, "arm9.bin"); + if (arm9_szoffset > 0) { + int newValue = arm9_szmode == 1 ? newARM9.length + : newARM9.length + 0x4000; + writeToByteArr(newARM9, arm9_szoffset, 3, newValue); + } + } + arm9_size = newARM9.length; + // copy new arm9 + fNew.seek(arm9_offset); + fNew.write(newARM9); + // footer? + if (arm9_has_footer) { + fNew.write(arm9_footer); + } + + } else { + // copy arm9+footer + this.baseRom.seek(old_arm9_offset); + fNew.seek(arm9_offset); + copy(this.baseRom, fNew, arm9_size + 12); + } + + // arm9 ovl + int arm9_ovl_offset = (int) fNew.getFilePointer(); + int arm9_ovl_size = arm9overlays.length * 32; + + // don't actually write arm9 ovl yet + + // arm7 + int arm7_offset = ((int) (arm9_ovl_offset + arm9_ovl_size + arm7_align)) + & (~arm7_align); + int old_arm7_offset = readFromFile(this.baseRom, 0x30, 4); + int arm7_size = readFromFile(this.baseRom, 0x3C, 4); + // copy arm7 + this.baseRom.seek(old_arm7_offset); + fNew.seek(arm7_offset); + copy(this.baseRom, fNew, arm7_size); + + // arm7 ovl + int arm7_ovl_offset = (int) fNew.getFilePointer(); + int old_arm7_ovl_offset = readFromFile(this.baseRom, 0x58, 4); + int arm7_ovl_size = readFromFile(this.baseRom, 0x5C, 4); + + // copy arm7 ovl + this.baseRom.seek(old_arm7_ovl_offset); + fNew.seek(arm7_ovl_offset); + copy(this.baseRom, fNew, arm7_ovl_size); + + // banner + int banner_offset = ((int) (fNew.getFilePointer() + banner_align)) + & (~banner_align); + int old_banner_offset = readFromFile(this.baseRom, 0x68, 4); + int banner_size = 0x840; + // copy banner + this.baseRom.seek(old_banner_offset); + fNew.seek(banner_offset); + copy(this.baseRom, fNew, banner_size); + + // filename table (doesn't change) + int fnt_offset = ((int) (fNew.getFilePointer() + fnt_align)) + & (~fnt_align); + int old_fnt_offset = readFromFile(this.baseRom, 0x40, 4); + int fnt_size = readFromFile(this.baseRom, 0x44, 4); + // copy fnt + this.baseRom.seek(old_fnt_offset); + fNew.seek(fnt_offset); + copy(this.baseRom, fNew, fnt_size); + + // make space for the FAT table + int fat_offset = ((int) (fNew.getFilePointer() + fat_align)) + & (~fat_align); + int fat_size = fat.length; + + // Now for actual files + // Make a new FAT as needed + // also make a new y9 table + byte[] newfat = new byte[fat.length]; + byte[] y9table = new byte[arm9overlays.length * 32]; + int base_offset = fat_offset + fat_size; + int filecount = fat.length / 8; + for (int fid = 0; fid < filecount; fid++) { + int offset_of_file = (base_offset + file_align) & (~file_align); + int file_len = 0; + boolean copiedCustom = false; + if (filesByID.containsKey(fid)) { + byte[] customContents = filesByID.get(fid) + .getOverrideContents(); + if (customContents != null) { + // copy custom + fNew.seek(offset_of_file); + fNew.write(customContents); + copiedCustom = true; + file_len = customContents.length; + } + } + if (arm9overlaysByFileID.containsKey(fid)) { + NDSY9Entry entry = arm9overlaysByFileID.get(fid); + int overlay_id = entry.overlay_id; + byte[] customContents = entry.getOverrideContents(); + if (customContents != null) { + // copy custom + fNew.seek(offset_of_file); + fNew.write(customContents); + copiedCustom = true; + file_len = customContents.length; + } + // regardless, fill in y9 table + writeToByteArr(y9table, overlay_id * 32, 4, overlay_id); + writeToByteArr(y9table, overlay_id * 32 + 4, 4, + entry.ram_address); + writeToByteArr(y9table, overlay_id * 32 + 8, 4, entry.ram_size); + writeToByteArr(y9table, overlay_id * 32 + 12, 4, entry.bss_size); + writeToByteArr(y9table, overlay_id * 32 + 16, 4, + entry.static_start); + writeToByteArr(y9table, overlay_id * 32 + 20, 4, + entry.static_end); + writeToByteArr(y9table, overlay_id * 32 + 24, 4, fid); + writeToByteArr(y9table, overlay_id * 32 + 28, 3, + entry.compressed_size); + writeToByteArr(y9table, overlay_id * 32 + 31, 1, + entry.compress_flag); + } + if (!copiedCustom) { + // copy from original ROM + int file_starts = readFromByteArr(fat, fid * 8, 4); + int file_ends = readFromByteArr(fat, fid * 8 + 4, 4); + file_len = file_ends - file_starts; + this.baseRom.seek(file_starts); + fNew.seek(offset_of_file); + copy(this.baseRom, fNew, file_len); + } + // write to new FAT + writeToByteArr(newfat, fid * 8, 4, offset_of_file); + writeToByteArr(newfat, fid * 8 + 4, 4, offset_of_file + file_len); + // update base_offset + base_offset = offset_of_file + file_len; + } + + // write new FAT table + fNew.seek(fat_offset); + fNew.write(newfat); + + // write y9 table + fNew.seek(arm9_ovl_offset); + fNew.write(y9table); + + // tidy up ending + // base_offset is the end of the last file + int newfilesize = base_offset; + newfilesize = (newfilesize + 3) & ~3; + int application_end_offset = newfilesize; + if (newfilesize != base_offset) { + fNew.seek(newfilesize - 1); + fNew.write(0); + } + + // calculate device capacity; + newfilesize |= newfilesize >> 16; + newfilesize |= newfilesize >> 8; + newfilesize |= newfilesize >> 4; + newfilesize |= newfilesize >> 2; + newfilesize |= newfilesize >> 1; + newfilesize++; + if (newfilesize <= 128 * 1024) { + newfilesize = 128 * 1024; + } + int devcap = -18; + int x = newfilesize; + while (x != 0) { + x >>= 1; + devcap++; + } + int devicecap = ((devcap < 0) ? 0 : devcap); + + // Update offsets in ROM header + writeToFile(fNew, 0x20, 4, arm9_offset); + writeToFile(fNew, 0x2C, 4, arm9_size); + writeToFile(fNew, 0x30, 4, arm7_offset); + writeToFile(fNew, 0x3C, 4, arm7_size); + writeToFile(fNew, 0x40, 4, fnt_offset); + writeToFile(fNew, 0x48, 4, fat_offset); + writeToFile(fNew, 0x50, 4, arm9_ovl_offset); + writeToFile(fNew, 0x58, 4, arm7_ovl_offset); + writeToFile(fNew, 0x68, 4, banner_offset); + writeToFile(fNew, 0x80, 4, application_end_offset); + writeToFile(fNew, 0x14, 1, devicecap); + + // Update header CRC + fNew.seek(0); + byte[] headerForCRC = new byte[0x15E]; + fNew.readFully(headerForCRC); + short crc = CRC16.calculate(headerForCRC, 0, 0x15E); + writeToFile(fNew, 0x15E, 2, (crc & 0xFFFF)); + + // done + fNew.close(); + closeROM(); + } + + private void copy(RandomAccessFile from, RandomAccessFile to, int bytes) + throws IOException { + int sizeof_copybuf = Math.min(256 * 1024, bytes); + byte[] copybuf = new byte[sizeof_copybuf]; + while (bytes > 0) { + int size2 = (bytes >= sizeof_copybuf) ? sizeof_copybuf : bytes; + int read = from.read(copybuf, 0, size2); + to.write(copybuf, 0, read); + bytes -= read; + } + copybuf = null; + } + + // returns null if file doesn't exist + public byte[] getFile(String filename) throws IOException { + if (files.containsKey(filename)) { + return files.get(filename).getContents(); + } else { + return null; + } + } + + public byte[] getOverlay(int number) throws IOException { + if (number >= 0 && number <= arm9overlays.length) { + return arm9overlays[number].getContents(); + } else { + return null; + } + } + + public byte[] getARM9() throws IOException { + if (!arm9_open) { + arm9_open = true; + this.reopenROM(); + int arm9_offset = readFromFile(this.baseRom, 0x20, 4); + int arm9_size = readFromFile(this.baseRom, 0x2C, 4); + byte[] arm9 = new byte[arm9_size]; + this.baseRom.seek(arm9_offset); + this.baseRom.readFully(arm9); + // footer check + int nitrocode = readFromFile(this.baseRom, 4); + if (nitrocode == 0xDEC00621) { + // found a footer + arm9_footer = new byte[12]; + writeToByteArr(arm9_footer, 0, 4, 0xDEC00621); + this.baseRom.read(arm9_footer, 4, 8); + arm9_has_footer = true; + } else { + arm9_has_footer = false; + } + // Any extras? + while ((readFromByteArr(arm9, arm9.length - 12, 4) == 0xDEC00621) + || ((readFromByteArr(arm9, arm9.length - 12, 4) == 0 + && readFromByteArr(arm9, arm9.length - 8, 4) == 0 && readFromByteArr( + arm9, arm9.length - 4, 4) == 0))) { + if (!arm9_has_footer) { + arm9_has_footer = true; + arm9_footer = new byte[0]; + } + byte[] newfooter = new byte[arm9_footer.length + 12]; + System.arraycopy(arm9, arm9.length - 12, newfooter, 0, 12); + System.arraycopy(arm9_footer, 0, newfooter, 12, + arm9_footer.length); + arm9_footer = newfooter; + byte[] newarm9 = new byte[arm9.length - 12]; + System.arraycopy(arm9, 0, newarm9, 0, arm9.length - 12); + arm9 = newarm9; + } + // Compression? + arm9_compressed = false; + arm9_szoffset = 0; + if (((int) arm9[arm9.length - 5]) >= 0x08 + && ((int) arm9[arm9.length - 5]) <= 0x0B) { + int compSize = readFromByteArr(arm9, arm9.length - 8, 3); + if (compSize > (arm9.length * 9 / 10) + && compSize < (arm9.length * 11 / 10)) { + arm9_compressed = true; + byte[] compLength = new byte[3]; + writeToByteArr(compLength, 0, 3, arm9.length); + List foundOffsets = RomFunctions.search(arm9, + compLength); + if (foundOffsets.size() == 1) { + arm9_szmode = 1; + arm9_szoffset = foundOffsets.get(0); + } else { + byte[] compLength2 = new byte[3]; + writeToByteArr(compLength2, 0, 3, arm9.length + 0x4000); + List foundOffsets2 = RomFunctions.search(arm9, + compLength2); + if (foundOffsets2.size() == 1) { + arm9_szmode = 2; + arm9_szoffset = foundOffsets2.get(0); + } else { + } + } + } + } + + if (arm9_compressed) { + arm9 = new BLZCoder(null).BLZ_DecodePub(arm9, "arm9.bin"); + } + + // Now actually make the copy or w/e + if (writingEnabled) { + File arm9file = new File(tmpFolder + "arm9.bin"); + FileOutputStream fos = new FileOutputStream(arm9file); + fos.write(arm9); + fos.close(); + arm9file.deleteOnExit(); + this.arm9_ramstored = null; + return arm9; + } else { + this.arm9_ramstored = arm9; + byte[] newcopy = new byte[arm9.length]; + System.arraycopy(arm9, 0, newcopy, 0, arm9.length); + return newcopy; + } + } else { + if (writingEnabled) { + FileInputStream fis = new FileInputStream(tmpFolder + + "arm9.bin"); + byte[] file = new byte[fis.available()]; + fis.read(file); + fis.close(); + return file; + } else { + byte[] newcopy = new byte[this.arm9_ramstored.length]; + System.arraycopy(this.arm9_ramstored, 0, newcopy, 0, + this.arm9_ramstored.length); + return newcopy; + } + } + } + + // returns null if file doesn't exist + public void writeFile(String filename, byte[] data) throws IOException { + if (files.containsKey(filename)) { + files.get(filename).writeOverride(data); + } + } + + public void writeOverlay(int number, byte[] data) throws IOException { + if (number >= 0 && number <= arm9overlays.length) { + arm9overlays[number].writeOverride(data); + } + } + + public void writeARM9(byte[] arm9) throws IOException { + if (!arm9_open) { + getARM9(); + } + arm9_changed = true; + if (writingEnabled) { + FileOutputStream fos = new FileOutputStream(new File(tmpFolder + + "arm9.bin")); + fos.write(arm9); + fos.close(); + } else { + if (this.arm9_ramstored.length == arm9.length) { + // copy new in + System.arraycopy(arm9, 0, this.arm9_ramstored, 0, arm9.length); + } else { + // make new array + this.arm9_ramstored = null; + this.arm9_ramstored = new byte[arm9.length]; + System.arraycopy(arm9, 0, this.arm9_ramstored, 0, arm9.length); + } + } + } + + private void firstPassDirectory(int dir, int subTableOffset, + int firstFileID, String[] directoryNames, + Map filenames, + Map fileDirectories) throws IOException { + // read subtable + baseRom.seek(subTableOffset); + while (true) { + int control = baseRom.read(); + if (control == 0x00) { + // done + break; + } + int namelen = control & 0x7F; + byte[] rawname = new byte[namelen]; + baseRom.readFully(rawname); + String name = new String(rawname, "US-ASCII"); + if ((control & 0x80) > 0x00) { + // sub-directory + int subDirectoryID = readFromFile(baseRom, 2); + directoryNames[subDirectoryID - 0xF000] = name; + } else { + int fileID = firstFileID++; + filenames.put(fileID, name); + fileDirectories.put(fileID, dir); + } + } + } + + // Helper methods to get variable-size ints out of files + + public String getTmpFolder() { + return tmpFolder; + } + + public RandomAccessFile getBaseRom() { + return baseRom; + } + + public boolean isWritingEnabled() { + return writingEnabled; + } + + public int readFromByteArr(byte[] data, int offset, int size) { + int result = 0; + for (int i = 0; i < size; i++) { + result |= (data[i + offset] & 0xFF) << (i * 8); + } + return result; + } + + public void writeToByteArr(byte[] data, int offset, int size, int value) { + for (int i = 0; i < size; i++) { + data[offset + i] = (byte) ((value >> (i * 8)) & 0xFF); + } + } + + public int readFromFile(RandomAccessFile file, int size) throws IOException { + return readFromFile(file, -1, size); + } + + // use -1 offset to read from current position + // useful if you want to read blocks + public int readFromFile(RandomAccessFile file, int offset, int size) + throws IOException { + byte[] buf = new byte[size]; + if (offset >= 0) + file.seek(offset); + file.readFully(buf); + int result = 0; + for (int i = 0; i < size; i++) { + result |= (buf[i] & 0xFF) << (i * 8); + } + return result; + } + + public void writeToFile(RandomAccessFile file, int size, int value) + throws IOException { + writeToFile(file, -1, size, value); + } + + public void writeToFile(RandomAccessFile file, int offset, int size, + int value) throws IOException { + byte[] buf = new byte[size]; + for (int i = 0; i < size; i++) { + buf[i] = (byte) ((value >> (i * 8)) & 0xFF); + } + if (offset >= 0) + file.seek(offset); + file.write(buf); + } + +} diff --git a/src/com/dabomstew/pkrandom/newnds/NDSY9Entry.java b/src/com/dabomstew/pkrandom/newnds/NDSY9Entry.java new file mode 100755 index 000000000..8791167f7 --- /dev/null +++ b/src/com/dabomstew/pkrandom/newnds/NDSY9Entry.java @@ -0,0 +1,147 @@ +package com.dabomstew.pkrandom.newnds; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +import cuecompressors.BLZCoder; + +/*----------------------------------------------------------------------------*/ +/*-- NDSY9Entry.java - an entry in the arm9 overlay system --*/ +/*-- Code based on "Nintendo DS rom tool", copyright (C) DevkitPro --*/ +/*-- Original Code by Rafael Vuijk, Dave Murphy, Alexei Karpenko --*/ +/*-- --*/ +/*-- Ported to Java by Dabomstew under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class NDSY9Entry { + + private NDSRom parent; + public int offset, size, original_size; + public int fileID; + public int overlay_id; + public int ram_address, ram_size; + public int bss_size; + public int static_start, static_end; + public int compressed_size; + public int compress_flag; + public Extracted status = Extracted.NOT; + public String extFilename; + public byte[] data; + private boolean decompressed_data = false; + + public NDSY9Entry(NDSRom parent) { + this.parent = parent; + } + + public byte[] getContents() throws IOException { + if (this.status == Extracted.NOT) { + // extract file + parent.reopenROM(); + RandomAccessFile rom = parent.getBaseRom(); + byte[] buf = new byte[this.original_size]; + rom.seek(this.offset); + rom.readFully(buf); + // Compression? + if (compress_flag != 0 + && this.original_size == this.compressed_size + && this.compressed_size != 0) { + buf = new BLZCoder(null).BLZ_DecodePub(buf, "overlay " + + overlay_id); + decompressed_data = true; + } + if (parent.isWritingEnabled()) { + // make a file + String tmpDir = parent.getTmpFolder(); + String fullPath = String.format("overlay_%04d", overlay_id); + String tmpFilename = fullPath.replaceAll("[^A-Za-z0-9_]+", ""); + this.extFilename = tmpFilename; + File tmpFile = new File(tmpDir + extFilename); + FileOutputStream fos = new FileOutputStream(tmpFile); + fos.write(buf); + fos.close(); + tmpFile.deleteOnExit(); + this.status = Extracted.TO_FILE; + this.data = null; + return buf; + } else { + this.status = Extracted.TO_RAM; + this.data = buf; + byte[] newcopy = new byte[buf.length]; + System.arraycopy(buf, 0, newcopy, 0, buf.length); + return newcopy; + } + } else if (this.status == Extracted.TO_RAM) { + byte[] newcopy = new byte[this.data.length]; + System.arraycopy(this.data, 0, newcopy, 0, this.data.length); + return newcopy; + } else { + String tmpDir = parent.getTmpFolder(); + FileInputStream fis = new FileInputStream(tmpDir + this.extFilename); + byte[] file = new byte[fis.available()]; + fis.read(file); + fis.close(); + return file; + } + } + + public void writeOverride(byte[] data) throws IOException { + if (status == Extracted.NOT) { + // temp extract + getContents(); + } + size = data.length; + if (status == Extracted.TO_FILE) { + String tmpDir = parent.getTmpFolder(); + FileOutputStream fos = new FileOutputStream(new File(tmpDir + + this.extFilename)); + fos.write(data); + fos.close(); + } else { + if (this.data.length == data.length) { + // copy new in + System.arraycopy(data, 0, this.data, 0, data.length); + } else { + // make new array + this.data = null; + this.data = new byte[data.length]; + System.arraycopy(data, 0, this.data, 0, data.length); + } + } + } + + // returns null if no override + public byte[] getOverrideContents() throws IOException { + if (status == Extracted.NOT) { + return null; + } + byte[] buf = getContents(); + if (this.decompressed_data) { + buf = new BLZCoder(null).BLZ_EncodePub(buf, false, false, + "overlay " + overlay_id); + // update our compressed size + this.compressed_size = buf.length; + } + return buf; + } + + private enum Extracted { + NOT, TO_FILE, TO_RAM; + } + +} diff --git a/src/com/dabomstew/pkrandom/patches/crystal_en_bwxp.ips b/src/com/dabomstew/pkrandom/patches/crystal_en_bwxp.ips new file mode 100755 index 0000000000000000000000000000000000000000..5c47f28b39f7418d552260236206fe7b5810a426 GIT binary patch literal 1552 zcmZWmYiL_#7=BK&)HJKLX>o1W>5^D%m#MTtXLmc@wVCJ^C(;x}9I_!|+HRm`YuB`s zUD_#iLv*OvF^8_&YVh28n?GVrA;hEZxWN>}FK8JAao><4oFA+2Ia#69^YOml`@Q$) zIZtnWcKa^p-(O+dY}$`k#pixZ<)$5-rpI8KS!^)XbQ?@b>FW-q^^Y4;eh-RUx%OtL zm8+*oDW~HNE^$5(h)-|sbl&vBIX%_k|I}&ELgh6kua|bz|BrC!=VYUfU?T?ieZZ&H3F1mjhf0a5ccS zxNxH5L6}8G=bGXK6)G_<&2f2-D|1|(KoWO9%xL8c~{CY)TFE;ETMOy(+6xW?2Z z(^6dPv$WdJ)D&0x2|1VhnVjNMKa*&|u((>UT%RBS4sn0qj3>dRc~ADI!DCW2a$^9L|RETSGQ518Fo$9=^u=FzL;BI;dwEG7#1{+~p_ zo{;!H7UNppV{kQJY;Yy-Hf~Y-OnS#|0@fAi4jc{~i7$)~b1kw2@4i=3nY8G%DHR>`RY8ACBb;%!_3Uc~>&xyjQ@c`)aD2OzPtfCz%D0O|VHe#V+%) zY%?2c2=dDjt{tQIRD`PoY>H1txH7;d`9y@v18jmIN4PXVm9Bap*Rpj?<7&2&sazqg zaGA8sCDKyW2)|6xDT+-}WP-^yiK&bLh@cKVx8W;0N@{sGsiH1YLPo?a0T0j@_up;b zq7cvZl6sC}E6wVr%nM|FMq>3C zxA)Bkha@EsL!GmGVwA*cM@Ndv{LURRi7al z!U>8E)1SFW$MXMUYlU8I)bl*?w2QbL#XiRZiZeC}3&$$Q7(8yH3BAY9y>p{&l|x_r I+!<;44?J3W82|tP literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/patches/gs_en_bwxp.ips b/src/com/dabomstew/pkrandom/patches/gs_en_bwxp.ips new file mode 100755 index 0000000000000000000000000000000000000000..db8f16cca984fc6abddb5d1015e3b32f210ded46 GIT binary patch literal 1552 zcmZWm4QP~Q9RIyLZ@IOGbEKP+-d)nRGh#ckmdxQm(c*TJP7`2V84|<9hQQAy^PSM~HA(d(L%zyo&4pzUM&fefa;M|MUO; z{od!czp$gpaXp2Gt21uACcbzno|@hiHWv)0nKulkni+#BaeZ&cjNV!kcNe0dg=_El zTDW?elyW*!?GzV1p2+lpu;cfga7_OZa?C$KH;y_Mfui(H<96Pabo1PFX~(KzIFFdO zjNAKbonl-1mT_vfP(K;J1TlZ|CpUXk6?B(jKTMt?;@4oNlHkFxJtl1^hMl% z*|#V}Qb*ER<8b;pI+L|W9n?bXHZtf9Zu?lvu5cFipRZtqWa-5Zpi}A#h^!Mz{3YRCB zu+CMUGahuU$Qebh`*I}?%BGrVvceZluD)9$8;tU?gr`SaM`$#gh(>YY(QFu{9$hP4# z)DjaZLf48h^poX71W96f18uv9^3K|mn`x&Y@!3b)Z6?%b(1LG>mjSwC_?o1JddH}` zpJV_hC^kfY<^mndzhG;LUaiz~ka%h$E=RC0ZwW=ejl#lFzOD*|8KV|QkDq=2T5I{b L`n#XI0*(Ix2qCH~ literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/patches/rb_en_bwxp.ips b/src/com/dabomstew/pkrandom/patches/rb_en_bwxp.ips new file mode 100755 index 0000000000000000000000000000000000000000..e8ba4b70bec35a045ab89caccd90af4ff92b8759 GIT binary patch literal 1345 zcmZ8fZAe>Z6n>&!zbaBIn^9?(k1?%-;oOGVrq{T(N&iTO2-)_hV_1gNkNGPaYv$OX z$vzyT_sd2z%AnncBTPndj4@KH5W?M^h{Fy>Ul~#ebH1)~>+-|w+?xu;_wb(gdCz&y zx#yhszTDk+>a5Uv6~#-qAoNX|=csT2B~`h5#{PMaAogeP8JAWn^^XQ~!Jj*Cg$K#O zX7qW_a&DhMX^H-wM-(1_6xsHi+Xk0iTyb&L#kG*_^6(SLZ5bmcD6qln+PH`#9 zBr4GFEJJ;S>Ms)er&cQc>s$_UL{LgH z{sgA1I{(Vo``o!#*A^WWzT9hTiz1o@Jv=DdvOR{|?e=jkyJJkX`M8?>(}>ckWVelI zsI^SYyIdiAk=wmXpIZ8_clmS=wHbCzxW-)Lp@U-lTnilP{`G-WdRZ*C1}qjGOf|h8 zOfrS3VWwH0tv8;6eq(U6@DAe91Kw&{%-7h^alEnKlcPN4{vmKL!^jk{=toPS%VXbWvl zW_OK?S#C_wRKqk;qSQ!v-80-;?c6o=fnn!;Q`_%4rF_k{p$o&URW(}P^lcC6w}vQ{ z9vSwOQyiW4+v)Y(KgRXu${>}rK&99p&HYRF(%{&F-(H!!Pq-vFz7SYVHz>y%pKM4R zR_Wyyf5W2Zq~q~g3pgB&1yJWWRshwGirR?D5t@syr_$~8dKRE?)eBZ^`CGLKwLXcQ5652A^PxqyquV*G+!2n=Xo+1{dBEaKwxEu|bR& mHiS{2ihi8^qK)i{)ung)%Hunz+%`OT`#tsJyRV!H`2Gi`pGWHe literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/patches/rb_en_critrate.ips b/src/com/dabomstew/pkrandom/patches/rb_en_critrate.ips new file mode 100755 index 0000000000000000000000000000000000000000..f08ef39a4f92c3bee3215fd341ff9cb02b0d4eb7 GIT binary patch literal 80 zcmWG=3~}~gexS_Yy#7_(1vY^XpB1m17fimO!KQfbykN!!tK}!O{iIHZD=!D^%M6T~|^JMT0*2=2IG#|YGY($(J$01A8{g8%>k literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/patches/rb_en_xaccnerf.ips b/src/com/dabomstew/pkrandom/patches/rb_en_xaccnerf.ips new file mode 100755 index 0000000000000000000000000000000000000000..426753a4b7973abd3bf0de7fd93c011819a47a09 GIT binary patch literal 48 zcmWG=3~}~getMjN`7lF0^M3{g>0g)6|5G^iDfOQkh|mHOzmhMUc0X|VQ>v@K8vr3) B7~TK? literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/patches/yellow_en_bwxp.ips b/src/com/dabomstew/pkrandom/patches/yellow_en_bwxp.ips new file mode 100755 index 0000000000000000000000000000000000000000..96be6556784ece8f6c749e03fc2b808dbb7d544b GIT binary patch literal 1345 zcmZ8feP~-%6#u2|>(@$ISF~wMH6LQT3R0`msO@X&wq$=;BD0wJ*F-v`tRMETwCNTr z2upNUbnmNc8xh&&MNyXXAQ zdFPz_UhU~Sa$Gp^9g3H5M(Dd>o}Ms)eCs!){>s$_U=UQN2_iqj)Q_EtpHDIx5Z>p*F zV1g-34KvO1+vhGbh?{O zx3%(kfNSs3eJ;S&7@Om>0j|W@ET0K*ImTva00Az=C{k!~a4p@;G_I!Wn93E>3YSUC zTp}&mzu|H6&XI4HJTpwTMw(3701=eo+%6mk&yqSYL8>%N%9avwiyC*)3OWC^PSFO@7N`{aqq+a+UK$)*@Y^eM_Xw8+#}@*tsRrdx<5Las z{VKKG>Tg)|9CkcWYXOI&u>k5EhYFzDQBfN)IYLYE)nuxJUe97SJ6r|AV~+X)c*JqA z0ID2ig^*%mQ%oe?+#X5SJ-kC?>~=-ck3YOaDd20&qXVOO4lm;-;VE20FM3F}$bJ)E z9K(;ej%wRa=)w_tfqoptT_g~MxqQT)K|8u}0Wo}No5JV#jAWWJc`4`b#QB!?Jieef zP>vovk9lFF7EKd$tEN{qmxxY^5^ScN#*63?MmjLviSIFh-z-KaF2YZ_Uc#jZ_qcUG zg`X+sq9t8;hGJj9d8)n{$E`1$^-Lj-D^^s&`X}K8z9LUM_0?i|5~QzC7T`;)Q@xiU z6P}{V{y-ROG#4+n@dMeshzlc|aG#(Y&*BqIl5`?q`FhB2cGFFfQ{cio0uFfb0XB#c m!-g;lRMC&KTeOjBT3!0IuOhdJ%5B4gci&e(zJ2mo!1q5P-$w}m literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/patches/yellow_en_critrate.ips b/src/com/dabomstew/pkrandom/patches/yellow_en_critrate.ips new file mode 100755 index 0000000000000000000000000000000000000000..a691f04f9ac7be9e56948dc550fd0cce68df06b3 GIT binary patch literal 80 zcmWG=3~}~gemIrEdHt){3v2=(J}X{6FPL;egH7@5dBOAxR?AOl`$?S+S74H0I<26{ gkfgO!gVjdiCx~}ez2G!E2=2IG#|YGY($(J$01uiU@&Et; literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/patches/yellow_en_xaccnerf.ips b/src/com/dabomstew/pkrandom/patches/yellow_en_xaccnerf.ips new file mode 100755 index 0000000000000000000000000000000000000000..e968abe1f380647c401cd142d7db3c62275e1c20 GIT binary patch literal 48 zcmWG=3~}~ge(uY_e3+qz`40nw^sh_j|0$fRNdKn>BD8?SucQm7-47hDNO$#j0|4*_ B7JdK# literal 0 HcmV?d00001 diff --git a/src/com/dabomstew/pkrandom/pokemon/Encounter.java b/src/com/dabomstew/pkrandom/pokemon/Encounter.java new file mode 100755 index 000000000..d6af69cc9 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/Encounter.java @@ -0,0 +1,43 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- Encounter.java - contains one wild Pokemon slot --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class Encounter { + + public int level; + public int maxLevel; + public Pokemon pokemon; + + public String toString() { + if (pokemon == null) { + return "ERROR"; + } + if (maxLevel == 0) { + return pokemon.name + " Lv" + level; + } else { + return pokemon.name + " Lvs " + level + "-" + maxLevel; + } + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/EncounterSet.java b/src/com/dabomstew/pkrandom/pokemon/EncounterSet.java new file mode 100755 index 000000000..33c264dbb --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/EncounterSet.java @@ -0,0 +1,42 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- EncounterSet.java - contains a group of wild Pokemon --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.ArrayList; +import java.util.List; + +public class EncounterSet { + + public int rate; + public List encounters = new ArrayList(); + public boolean battleTrappersBanned = false; + public String displayName; + public int offset; + + public String toString() { + return "Encounter [Rate = " + rate + ", Encounters = " + encounters + + "]"; + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/Evolution.java b/src/com/dabomstew/pkrandom/pokemon/Evolution.java new file mode 100755 index 000000000..503daec07 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/Evolution.java @@ -0,0 +1,82 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- Evolution.java - represents an evolution between 2 Pokemon. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class Evolution implements Comparable { + + public int from; + public int to; + public boolean carryStats; + public EvolutionType type; + public int extraInfo; + + public Evolution(int from, int to, boolean carryStats, EvolutionType type, int extra) { + this.from = from; + this.to = to; + this.carryStats = carryStats; + this.type = type; + this.extraInfo = extra; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + from; + result = prime * result + to; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Evolution other = (Evolution) obj; + if (from != other.from) + return false; + if (to != other.to) + return false; + return true; + } + + @Override + public int compareTo(Evolution o) { + if (this.from < o.from) { + return -1; + } else if (this.from > o.from) { + return 1; + } else if (this.to < o.to) { + return -1; + } else if (this.to > o.to) { + return 1; + } else { + return 0; + } + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/EvolutionType.java b/src/com/dabomstew/pkrandom/pokemon/EvolutionType.java new file mode 100755 index 000000000..ea362f8db --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/EvolutionType.java @@ -0,0 +1,42 @@ +package com.dabomstew.pkrandom.pokemon; + +public enum EvolutionType { + LEVEL(1, 1, 4, 4, 4), STONE(2, 2, 7, 7, 8), TRADE(3, 3, 5, 5, 5), TRADE_ITEM( + -1, 3, 6, 6, 6), HAPPINESS(-1, 4, 1, 1, 1), HAPPINESS_DAY(-1, 4, 2, + 2, 2), HAPPINESS_NIGHT(-1, 4, 3, 3, 3), LEVEL_ATTACK_HIGHER(-1, 5, + 8, 8, 9), LEVEL_DEFENSE_HIGHER(-1, 5, 10, 10, 11), LEVEL_ATK_DEF_SAME( + -1, 5, 9, 9, 10), LEVEL_LOW_PV(-1, -1, 11, 11, 12), LEVEL_HIGH_PV( + -1, -1, 12, 12, 13), LEVEL_CREATE_EXTRA(-1, -1, 13, 13, 14), LEVEL_IS_EXTRA( + -1, -1, 14, 14, 15), LEVEL_HIGH_BEAUTY(-1, -1, 15, 15, 16), STONE_MALE_ONLY( + -1, -1, -1, 16, 17), STONE_FEMALE_ONLY(-1, -1, -1, 17, 18), LEVEL_ITEM_DAY( + -1, -1, -1, 18, 19), LEVEL_ITEM_NIGHT(-1, -1, -1, 19, 20), LEVEL_WITH_MOVE( + -1, -1, -1, 20, 21), LEVEL_WITH_OTHER(-1, -1, -1, 21, 22), LEVEL_MALE_ONLY( + -1, -1, -1, 22, 23), LEVEL_FEMALE_ONLY(-1, -1, -1, 23, 24), LEVEL_ELECTRIFIED_AREA( + -1, -1, -1, 24, 25), LEVEL_MOSS_ROCK(-1, -1, -1, 25, 26), LEVEL_ICY_ROCK( + -1, -1, -1, 26, 27), TRADE_SPECIAL(-1, -1, -1, -1, 7); + private int[] indexNumbers; + private static EvolutionType[][] reverseIndexes = new EvolutionType[5][30]; + + static { + for (EvolutionType et : EvolutionType.values()) { + for (int i = 0; i < et.indexNumbers.length; i++) { + if (et.indexNumbers[i] > 0 + && reverseIndexes[i][et.indexNumbers[i]] == null) { + reverseIndexes[i][et.indexNumbers[i]] = et; + } + } + } + } + + private EvolutionType(int... indexes) { + this.indexNumbers = indexes; + } + + public int toIndex(int generation) { + return indexNumbers[generation - 1]; + } + + public static EvolutionType fromIndex(int generation, int index) { + return reverseIndexes[generation - 1][index]; + } +} diff --git a/src/com/dabomstew/pkrandom/pokemon/ExpCurve.java b/src/com/dabomstew/pkrandom/pokemon/ExpCurve.java new file mode 100755 index 000000000..399867044 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/ExpCurve.java @@ -0,0 +1,43 @@ +package com.dabomstew.pkrandom.pokemon; + +public enum ExpCurve { + + SLOW, MEDIUM_SLOW, MEDIUM_FAST, FAST, ERRATIC, FLUCTUATING; + + public static ExpCurve fromByte(byte curve) { + switch (curve) { + case 0: + return MEDIUM_FAST; + case 1: + return ERRATIC; + case 2: + return FLUCTUATING; + case 3: + return MEDIUM_SLOW; + case 4: + return FAST; + case 5: + return SLOW; + } + return null; + } + + public byte toByte() { + switch (this) { + case SLOW: + return 5; + case MEDIUM_SLOW: + return 3; + case MEDIUM_FAST: + return 0; + case FAST: + return 4; + case ERRATIC: + return 1; + case FLUCTUATING: + return 2; + } + return 0; // default + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/GenRestrictions.java b/src/com/dabomstew/pkrandom/pokemon/GenRestrictions.java new file mode 100755 index 000000000..1d6ca97b1 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/GenRestrictions.java @@ -0,0 +1,88 @@ +package com.dabomstew.pkrandom.pokemon; + +public class GenRestrictions { + + public boolean allow_gen1, allow_gen2, allow_gen3, allow_gen4, allow_gen5; + + public boolean assoc_g1_g2, assoc_g1_g4; + public boolean assoc_g2_g1, assoc_g2_g3, assoc_g2_g4; + public boolean assoc_g3_g2, assoc_g3_g4; + public boolean assoc_g4_g1, assoc_g4_g2, assoc_g4_g3; + + public GenRestrictions() { + } + + public GenRestrictions(int state) { + allow_gen1 = (state & 1) > 0; + allow_gen2 = (state & 2) > 0; + allow_gen3 = (state & 4) > 0; + allow_gen4 = (state & 8) > 0; + allow_gen5 = (state & 16) > 0; + + assoc_g1_g2 = (state & 32) > 0; + assoc_g1_g4 = (state & 64) > 0; + + assoc_g2_g1 = (state & 128) > 0; + assoc_g2_g3 = (state & 256) > 0; + assoc_g2_g4 = (state & 512) > 0; + + assoc_g3_g2 = (state & 1024) > 0; + assoc_g3_g4 = (state & 2048) > 0; + + assoc_g4_g1 = (state & 4096) > 0; + assoc_g4_g2 = (state & 8192) > 0; + assoc_g4_g3 = (state & 16384) > 0; + } + + public boolean nothingSelected() { + return !allow_gen1 && !allow_gen2 && !allow_gen3 && !allow_gen4 + && !allow_gen5; + } + + public int toInt() { + return makeIntSelected(allow_gen1, allow_gen2, allow_gen3, allow_gen4, + allow_gen5, assoc_g1_g2, assoc_g1_g4, assoc_g2_g1, assoc_g2_g3, + assoc_g2_g4, assoc_g3_g2, assoc_g3_g4, assoc_g4_g1, + assoc_g4_g2, assoc_g4_g3); + } + + public void limitToGen(int generation) { + if (generation < 2) { + allow_gen2 = false; + assoc_g1_g2 = false; + assoc_g2_g1 = false; + } + if (generation < 3) { + allow_gen3 = false; + assoc_g2_g3 = false; + assoc_g3_g2 = false; + } + if (generation < 4) { + allow_gen4 = false; + assoc_g1_g4 = false; + assoc_g2_g4 = false; + assoc_g3_g4 = false; + assoc_g4_g1 = false; + assoc_g4_g2 = false; + assoc_g4_g3 = false; + } + if (generation < 5) { + allow_gen5 = false; + } + } + + private int makeIntSelected(boolean... switches) { + if (switches.length > 32) { + // No can do + return 0; + } + int initial = 0; + int state = 1; + for (boolean b : switches) { + initial |= b ? state : 0; + state *= 2; + } + return initial; + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/IngameTrade.java b/src/com/dabomstew/pkrandom/pokemon/IngameTrade.java new file mode 100755 index 000000000..c37f6cb92 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/IngameTrade.java @@ -0,0 +1,17 @@ +package com.dabomstew.pkrandom.pokemon; + +public class IngameTrade { + + public int id; + + public Pokemon requestedPokemon, givenPokemon; + + public String nickname, otName; + + public int otId; + + public int[] ivs = new int[0]; + + public int item = 0; + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/ItemList.java b/src/com/dabomstew/pkrandom/pokemon/ItemList.java new file mode 100755 index 000000000..04f65c2cb --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/ItemList.java @@ -0,0 +1,74 @@ +package com.dabomstew.pkrandom.pokemon; + +import com.dabomstew.pkrandom.RandomSource; + +public class ItemList { + + private boolean[] items; + private boolean[] tms; + + public ItemList(int highestIndex) { + items = new boolean[highestIndex + 1]; + tms = new boolean[highestIndex + 1]; + for (int i = 1; i <= highestIndex; i++) { + items[i] = true; + } + } + + public boolean isTM(int index) { + if (index < 0 || index >= tms.length) { + return false; + } + return tms[index]; + } + + public boolean isAllowed(int index) { + if (index < 0 || index >= tms.length) { + return false; + } + return items[index]; + } + + public void banSingles(int... indexes) { + for (int index : indexes) { + items[index] = false; + } + } + + public void banRange(int startIndex, int length) { + for (int i = 0; i < length; i++) { + items[i + startIndex] = false; + } + } + + public void tmRange(int startIndex, int length) { + for (int i = 0; i < length; i++) { + tms[i + startIndex] = true; + } + } + + public int randomItem() { + int chosen = 0; + while (!items[chosen]) { + chosen = RandomSource.nextInt(items.length); + } + return chosen; + } + + public int randomNonTM() { + int chosen = 0; + while (!items[chosen] || tms[chosen]) { + chosen = RandomSource.nextInt(items.length); + } + return chosen; + } + + public int randomTM() { + int chosen = 0; + while (!tms[chosen]) { + chosen = RandomSource.nextInt(items.length); + } + return chosen; + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/Move.java b/src/com/dabomstew/pkrandom/pokemon/Move.java new file mode 100755 index 000000000..eb253b832 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/Move.java @@ -0,0 +1,47 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- Move.java - represents a move usable by Pokemon. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class Move { + + public String name; + public int number; + public int internalId; + public int power; + public int pp; + public double hitratio; + public Type type; + public int effectIndex; + + public String toString() { + return "#" + number + " " + name + " - Power: " + power + ", Base PP: " + + pp + ", Type: " + type + ", Hit%: " + (hitratio) + + ", Effect: " + effectIndex; + } + + public void setAccuracy(double percent) { + hitratio = percent; + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/MoveLearnt.java b/src/com/dabomstew/pkrandom/pokemon/MoveLearnt.java new file mode 100755 index 000000000..c9b87a562 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/MoveLearnt.java @@ -0,0 +1,35 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- MoveLearnt.java - represents a move learnt by a Pokemon at a level. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class MoveLearnt { + + public int move; + public int level; + + public String toString() { + return "move "+move+" at level "+level; + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/Pokemon.java b/src/com/dabomstew/pkrandom/pokemon/Pokemon.java new file mode 100755 index 000000000..b3ef526d9 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/Pokemon.java @@ -0,0 +1,221 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- Pokemon.java - represents an individual Pokemon, and contains --*/ +/*-- common Pokemon-related functions. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.dabomstew.pkrandom.RandomSource; + +public class Pokemon implements Comparable { + + public String name; + public int number; + + public Type primaryType, secondaryType; + + public int hp, attack, defense, spatk, spdef, speed, special; + + public int ability1, ability2, ability3; + + public int catchRate; + + public int guaranteedHeldItem, commonHeldItem, rareHeldItem, + darkGrassHeldItem; + + public ExpCurve growthCurve; + + public Pokemon() { + } + + public void shuffleStats() { + List stats = Arrays.asList(hp, attack, defense, spatk, spdef, + speed); + Collections.shuffle(stats, RandomSource.instance()); + + // Copy in new stats + hp = stats.get(0); + attack = stats.get(1); + defense = stats.get(2); + spatk = stats.get(3); + spdef = stats.get(4); + speed = stats.get(5); + + // make special the average of spatk and spdef + special = (int) Math.ceil((spatk + spdef) / 2.0f); + + // Copy special from a random one of spatk or spdef +// if (RandomSource.random() < 0.5) { +// special = spatk; +// } else { +// special = spdef; +// } + } + + public void randomizeStatsWithinBST() { + if (number == 292) { + // Shedinja is horribly broken unless we restrict him to 1HP. + int bst = bst() - 51; + + // Make weightings + double atkW = RandomSource.random(), defW = RandomSource.random(); + double spaW = RandomSource.random(), spdW = RandomSource.random(), speW = RandomSource + .random(); + + double totW = atkW + defW + spaW + spdW + speW; + + hp = 1; + attack = (int) Math.max(1, Math.round(atkW / totW * bst)) + 10; + defense = (int) Math.max(1, Math.round(defW / totW * bst)) + 10; + spatk = (int) Math.max(1, Math.round(spaW / totW * bst)) + 10; + spdef = (int) Math.max(1, Math.round(spdW / totW * bst)) + 10; + speed = (int) Math.max(1, Math.round(speW / totW * bst)) + 10; + + // Fix up special too + special = (int) Math.ceil((spatk + spdef) / 2.0f); + + } else { + // Minimum 20 HP, 10 everything else + int bst = bst() - 70; + + // Make weightings + double hpW = RandomSource.random(), atkW = RandomSource.random(), defW = RandomSource + .random(); + double spaW = RandomSource.random(), spdW = RandomSource.random(), speW = RandomSource + .random(); + + double totW = hpW + atkW + defW + spaW + spdW + speW; + + hp = (int) Math.max(1, Math.round(hpW / totW * bst)) + 20; + attack = (int) Math.max(1, Math.round(atkW / totW * bst)) + 10; + defense = (int) Math.max(1, Math.round(defW / totW * bst)) + 10; + spatk = (int) Math.max(1, Math.round(spaW / totW * bst)) + 10; + spdef = (int) Math.max(1, Math.round(spdW / totW * bst)) + 10; + speed = (int) Math.max(1, Math.round(speW / totW * bst)) + 10; + + // Fix up special too + special = (int) Math.ceil((spatk + spdef) / 2.0f); + } + + // Check for something we can't store + if (hp > 255 || attack > 255 || defense > 255 || spatk > 255 + || spdef > 255 || speed > 255) { + // re roll + randomizeStatsWithinBST(); + } + + } + + public void copyRandomizedStatsUpEvolution(Pokemon evolvesFrom) { + double ourBST = bst(); + double theirBST = evolvesFrom.bst(); + + double bstRatio = ourBST / theirBST; + + hp = (int) Math.min(255, + Math.max(1, Math.round(evolvesFrom.hp * bstRatio))); + attack = (int) Math.min(255, + Math.max(1, Math.round(evolvesFrom.attack * bstRatio))); + defense = (int) Math.min(255, + Math.max(1, Math.round(evolvesFrom.defense * bstRatio))); + speed = (int) Math.min(255, + Math.max(1, Math.round(evolvesFrom.speed * bstRatio))); + spatk = (int) Math.min(255, + Math.max(1, Math.round(evolvesFrom.spatk * bstRatio))); + spdef = (int) Math.min(255, + Math.max(1, Math.round(evolvesFrom.spdef * bstRatio))); + + special = (int) Math.ceil((spatk + spdef) / 2.0f); + } + + public int bst() { + return hp + attack + defense + spatk + spdef + speed; + } + + public int bstForPowerLevels() { + // Take into account Shedinja's purposefully nerfed HP + if (number == 292) { + return (attack + defense + spatk + spdef + speed) * 6 / 5; + } else { + return hp + attack + defense + spatk + spdef + speed; + } + } + + @Override + public String toString() { + return "Pokemon [name=" + name + ", number=" + number + + ", primaryType=" + primaryType + ", secondaryType=" + + secondaryType + ", hp=" + hp + ", attack=" + attack + + ", defense=" + defense + ", spatk=" + spatk + ", spdef=" + + spdef + ", speed=" + speed + "]"; + } + + public String toStringRBY() { + return "Pokemon [name=" + name + ", number=" + number + + ", primaryType=" + primaryType + ", secondaryType=" + + secondaryType + ", hp=" + hp + ", attack=" + attack + + ", defense=" + defense + ", special=" + special + ", speed=" + + speed + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + number; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Pokemon other = (Pokemon) obj; + if (number != other.number) + return false; + return true; + } + + @Override + public int compareTo(Pokemon o) { + return number - o.number; + } + + private static final List legendaries = Arrays.asList(144, 145, + 146, 150, 151, 243, 244, 245, 249, 250, 251, 377, 378, 379, 380, + 381, 382, 383, 384, 385, 386, 479, 480, 481, 482, 483, 484, 485, + 486, 487, 488, 489, 490, 491, 492, 493, 494, 638, 639, 640, 641, + 642, 643, 644, 645, 646, 647, 648, 649); + + public boolean isLegendary() { + return legendaries.contains(this.number); + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/Trainer.java b/src/com/dabomstew/pkrandom/pokemon/Trainer.java new file mode 100755 index 000000000..31d5b0228 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/Trainer.java @@ -0,0 +1,92 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- Trainer.java - represents a Trainer's pokemon set/other details. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.ArrayList; +import java.util.List; + +public class Trainer implements Comparable { + public int offset; + public List pokemon = new ArrayList(); + public String tag; + public boolean importantTrainer; + public int poketype; + public String name; + public int trainerclass; + public String fullDisplayName; + + public String toString() { + StringBuilder sb = new StringBuilder("["); + if (fullDisplayName != null) { + sb.append(fullDisplayName + " "); + } else if (name != null) { + sb.append(name + " "); + } + if (trainerclass != 0) { + sb.append("(" + trainerclass + ") - "); + } + sb.append(String.format("%x", offset)); + sb.append(" => "); + boolean first = true; + for (TrainerPokemon p : pokemon) { + if (!first) { + sb.append(','); + } + sb.append(p.pokemon.name + " Lv" + p.level); + first = false; + } + sb.append(']'); + if (tag != null) { + sb.append(" (" + tag + ")"); + } + return sb.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + offset; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Trainer other = (Trainer) obj; + if (offset != other.offset) + return false; + return true; + } + + @Override + public int compareTo(Trainer o) { + return offset - o.offset; + } +} diff --git a/src/com/dabomstew/pkrandom/pokemon/TrainerPokemon.java b/src/com/dabomstew/pkrandom/pokemon/TrainerPokemon.java new file mode 100755 index 000000000..5fca5ac37 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/TrainerPokemon.java @@ -0,0 +1,44 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- TrainerPokemon.java - represents a Pokemon owned by a trainer. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class TrainerPokemon { + + public Pokemon pokemon; + public int level; + + public int move1; + public int move2; + public int move3; + public int move4; + + public int AILevel; + public int heldItem; + public int ability; + + public String toString() { + return pokemon.name + " Lv" + level; + } + +} diff --git a/src/com/dabomstew/pkrandom/pokemon/Type.java b/src/com/dabomstew/pkrandom/pokemon/Type.java new file mode 100755 index 000000000..bf6f2a752 --- /dev/null +++ b/src/com/dabomstew/pkrandom/pokemon/Type.java @@ -0,0 +1,60 @@ +package com.dabomstew.pkrandom.pokemon; + +/*----------------------------------------------------------------------------*/ +/*-- Type.java - represents a Pokemon or move type. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.dabomstew.pkrandom.RandomSource; +import com.dabomstew.pkrandom.RomFunctions; + +public enum Type { + + NORMAL, FIGHTING, FLYING, GRASS, WATER, FIRE, ROCK, GROUND, PSYCHIC, BUG, DRAGON, ELECTRIC, GHOST, POISON, ICE, STEEL, DARK, + GAS(true), FAIRY(true), WOOD(true), ABNORMAL(true), WIND(true), SOUND(true), LIGHT(true); + + public boolean isHackOnly; + + private Type() { + this.isHackOnly = false; + } + + private Type(boolean isHackOnly) { + this.isHackOnly = isHackOnly; + } + + private static final List VALUES = Collections + .unmodifiableList(Arrays.asList(values())); + private static final int SIZE = VALUES.size(); + + public static Type randomType() { + return VALUES.get(RandomSource.nextInt(SIZE)); + } + + public String camelCase() { + return RomFunctions.camelCase(this.toString()); + } + +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/AbstractDSRomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/AbstractDSRomHandler.java new file mode 100755 index 000000000..c36dae75f --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/AbstractDSRomHandler.java @@ -0,0 +1,406 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- AbstractDSRomHandler.java - a base class for DS rom handlers --*/ +/*-- which standardises common DS functions. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Map; +import java.util.TreeMap; + +import com.dabomstew.pkrandom.newnds.NDSRom; +import com.dabomstew.pkrandom.pokemon.Type; + +public abstract class AbstractDSRomHandler extends AbstractRomHandler { + + protected String dataFolder; + private NDSRom baseRom; + private String loadedFN; + + @Override + public boolean detectRom(String filename) { + try { + FileInputStream fis = new FileInputStream(filename); + fis.skip(0x0C); + byte[] sig = new byte[4]; + fis.read(sig); + fis.close(); + String ndsCode = new String(sig, "US-ASCII"); + return detectNDSRom(ndsCode); + } catch (Exception e) { + return false; + } + } + + protected abstract boolean detectNDSRom(String ndsCode); + + @Override + public boolean loadRom(String filename) { + if (!detectRom(filename)) { + return false; + } + // Load inner rom + try { + baseRom = new NDSRom(filename); + } catch (IOException e) { + throw new RuntimeException(e); + } + loadedFN = filename; + loadedROM(); + return true; + } + + @Override + public String loadedFilename() { + return loadedFN; + } + + protected byte[] get3byte(int amount) { + byte[] ret = new byte[3]; + ret[0] = (byte) (amount & 0xFF); + ret[1] = (byte) ((amount >> 8) & 0xFF); + ret[2] = (byte) ((amount >> 16) & 0xFF); + return ret; + } + + protected abstract void loadedROM(); + + protected abstract void savingROM(); + + @Override + public boolean saveRom(String filename) { + savingROM(); + try { + baseRom.saveTo(filename); + } catch (Exception e) { + throw new RuntimeException(e); + } + return true; + } + + public void closeInnerRom() throws IOException { + baseRom.closeROM(); + } + + @Override + public boolean canChangeStaticPokemon() { + return false; + } + + public NARCContents readNARC(String subpath) throws IOException { + Map frames = readNitroFrames(subpath); + if (!frames.containsKey("FATB") || !frames.containsKey("FNTB") + || !frames.containsKey("FIMG")) { + System.err.println("Not a valid narc file"); + return null; + } + // File contents + NARCContents narc = new NARCContents(); + byte[] fatbframe = frames.get("FATB"); + byte[] fimgframe = frames.get("FIMG"); + int fileCount = readLong(fatbframe, 0); + for (int i = 0; i < fileCount; i++) { + int startOffset = readLong(fatbframe, 4 + i * 8); + int endOffset = readLong(fatbframe, 8 + i * 8); + int length = (endOffset - startOffset); + byte[] thisFile = new byte[length]; + try { + System.arraycopy(fimgframe, startOffset, thisFile, 0, length); + } catch (ArrayIndexOutOfBoundsException ex) { + System.out.printf( + "AIOBEX: start %d length %d size of frame %d\n", + startOffset, length, fimgframe.length); + } + narc.files.add(thisFile); + } + // Filenames? + byte[] fntbframe = frames.get("FNTB"); + int unk1 = readLong(fntbframe, 0); + if (unk1 == 8) { + // Filenames exist + narc.hasFilenames = true; + int offset = 8; + for (int i = 0; i < fileCount; i++) { + int fnLength = (fntbframe[offset] & 0xFF); + offset++; + byte[] filenameBA = new byte[fnLength]; + System.arraycopy(fntbframe, offset, filenameBA, 0, fnLength); + String filename = new String(filenameBA, "US-ASCII"); + narc.filenames.add(filename); + } + } else { + narc.hasFilenames = false; + for (int i = 0; i < fileCount; i++) { + narc.filenames.add(null); + } + } + return narc; + } + + public void writeNARC(String subpath, NARCContents narc) throws IOException { + // Get bytes required for FIMG frame + int bytesRequired = 0; + for (byte[] file : narc.files) { + bytesRequired += Math.ceil(file.length / 4.0) * 4; + } + // FIMG frame & FATB frame build + + // 4 for numentries, 8*size for entries, 8 for nitro header + byte[] fatbFrame = new byte[4 + narc.files.size() * 8 + 8]; + // bytesRequired + 8 for nitro header + byte[] fimgFrame = new byte[bytesRequired + 8]; + + // Nitro headers + fatbFrame[0] = 'B'; + fatbFrame[1] = 'T'; + fatbFrame[2] = 'A'; + fatbFrame[3] = 'F'; + writeLong(fatbFrame, 4, fatbFrame.length); + + fimgFrame[0] = 'G'; + fimgFrame[1] = 'M'; + fimgFrame[2] = 'I'; + fimgFrame[3] = 'F'; + writeLong(fimgFrame, 4, fimgFrame.length); + int offset = 0; + + writeLong(fatbFrame, 8, narc.files.size()); + for (int i = 0; i < narc.files.size(); i++) { + byte[] file = narc.files.get(i); + int bytesRequiredForFile = (int) (Math.ceil(file.length / 4.0) * 4); + System.arraycopy(file, 0, fimgFrame, offset + 8, file.length); + for (int filler = file.length; filler < bytesRequiredForFile; filler++) { + fimgFrame[offset + 8 + filler] = (byte) 0xFF; + } + writeLong(fatbFrame, 12 + i * 8, offset); + writeLong(fatbFrame, 16 + i * 8, offset + file.length); + offset += bytesRequiredForFile; + } + + // FNTB Frame + int bytesForFNTBFrame = 16; + if (narc.hasFilenames) { + for (String filename : narc.filenames) { + bytesForFNTBFrame += filename.getBytes("US-ASCII").length + 1; + } + } + byte[] fntbFrame = new byte[bytesForFNTBFrame]; + + fntbFrame[0] = 'B'; + fntbFrame[1] = 'T'; + fntbFrame[2] = 'N'; + fntbFrame[3] = 'F'; + writeLong(fntbFrame, 4, fntbFrame.length); + + if (narc.hasFilenames) { + writeLong(fntbFrame, 8, 8); + writeLong(fntbFrame, 12, 0x10000); + int fntbOffset = 16; + for (String filename : narc.filenames) { + byte[] fntbfilename = filename.getBytes("US-ASCII"); + fntbFrame[fntbOffset] = (byte) fntbfilename.length; + System.arraycopy(fntbfilename, 0, fntbFrame, fntbOffset + 1, + fntbfilename.length); + fntbOffset += 1 + fntbfilename.length; + } + } else { + writeLong(fntbFrame, 8, 4); + writeLong(fntbFrame, 12, 0x10000); + } + + // Now for the actual Nitro file + int nitrolength = 16 + fatbFrame.length + fntbFrame.length + + fimgFrame.length; + byte[] nitroFile = new byte[nitrolength]; + nitroFile[0] = 'N'; + nitroFile[1] = 'A'; + nitroFile[2] = 'R'; + nitroFile[3] = 'C'; + writeWord(nitroFile, 4, 0xFFFE); + writeWord(nitroFile, 6, 0x0100); + writeLong(nitroFile, 8, nitrolength); + writeWord(nitroFile, 12, 0x10); + writeWord(nitroFile, 14, 3); + System.arraycopy(fatbFrame, 0, nitroFile, 16, fatbFrame.length); + System.arraycopy(fntbFrame, 0, nitroFile, 16 + fatbFrame.length, + fntbFrame.length); + System.arraycopy(fimgFrame, 0, nitroFile, 16 + fatbFrame.length + + fntbFrame.length, fimgFrame.length); + this.writeFile(subpath, nitroFile); + } + + private Map readNitroFrames(String filename) + throws IOException { + byte[] wholeFile = this.readFile(filename); + + // Read the number of frames + int frameCount = readWord(wholeFile, 0x0E); + + // each frame + int offset = 0x10; + Map frames = new TreeMap(); + for (int i = 0; i < frameCount; i++) { + byte[] magic = new byte[] { wholeFile[offset + 3], + wholeFile[offset + 2], wholeFile[offset + 1], + wholeFile[offset] }; + String magicS = new String(magic, "US-ASCII"); + + int frame_size = readLong(wholeFile, offset + 4); + // Patch for BB/VW and other DS hacks which don't update + // the size of their expanded NARCs correctly + if (i == frameCount - 1 && offset + frame_size < wholeFile.length) { + frame_size = wholeFile.length - offset; + } + byte[] frame = new byte[frame_size - 8]; + System.arraycopy(wholeFile, offset + 8, frame, 0, frame_size - 8); + frames.put(magicS, frame); + offset += frame_size; + } + return frames; + } + + protected int readWord(byte[] data, int offset) { + return (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8); + } + + protected int readLong(byte[] data, int offset) { + return (data[offset] & 0xFF) | ((data[offset + 1] & 0xFF) << 8) + | ((data[offset + 2] & 0xFF) << 16) + | ((data[offset + 3] & 0xFF) << 24); + } + + protected int readRelativePointer(byte[] data, int offset) { + return readLong(data, offset) + offset + 4; + } + + protected void writeWord(byte[] data, int offset, int value) { + data[offset] = (byte) (value & 0xFF); + data[offset + 1] = (byte) ((value >> 8) & 0xFF); + } + + protected void writeLong(byte[] data, int offset, int value) { + data[offset] = (byte) (value & 0xFF); + data[offset + 1] = (byte) ((value >> 8) & 0xFF); + data[offset + 2] = (byte) ((value >> 16) & 0xFF); + data[offset + 3] = (byte) ((value >> 24) & 0xFF); + } + + protected void writeRelativePointer(byte[] data, int offset, int pointer) { + int relPointer = pointer - (offset + 4); + writeLong(data, offset, relPointer); + } + + protected byte[] readFile(String location) throws IOException { + return baseRom.getFile(location); + } + + protected void writeFile(String location, byte[] data) throws IOException { + writeFile(location, data, 0, data.length); + } + + protected void writeFile(String location, byte[] data, int offset, + int length) throws IOException { + if (offset != 0 || length != data.length) { + byte[] newData = new byte[length]; + System.arraycopy(data, offset, newData, 0, length); + data = newData; + } + baseRom.writeFile(location, data); + } + + protected byte[] readARM9() throws IOException { + return baseRom.getARM9(); + } + + protected void writeARM9(byte[] data) throws IOException { + baseRom.writeARM9(data); + } + + protected byte[] readOverlay(int number) throws IOException { + return baseRom.getOverlay(number); + } + + protected void writeOverlay(int number, byte[] data) throws IOException { + baseRom.writeOverlay(number, data); + } + + protected void readByteIntoFlags(byte[] data, boolean[] flags, + int offsetIntoFlags, int offsetIntoData) { + int thisByte = data[offsetIntoData] & 0xFF; + for (int i = 0; i < 8 && (i + offsetIntoFlags) < flags.length; i++) { + flags[offsetIntoFlags + i] = ((thisByte >> i) & 0x01) == 0x01; + } + } + + protected byte getByteFromFlags(boolean[] flags, int offsetIntoFlags) { + int thisByte = 0; + for (int i = 0; i < 8 && (i + offsetIntoFlags) < flags.length; i++) { + thisByte |= (flags[offsetIntoFlags + i] ? 1 : 0) << i; + } + return (byte) thisByte; + } + + protected int typeTMPaletteNumber(Type t) { + if (t == null) { + return 411; // CURSE + } + switch (t) { + case FIGHTING: + return 398; + case DRAGON: + return 399; + case WATER: + return 400; + case PSYCHIC: + return 401; + case NORMAL: + return 402; + case POISON: + return 403; + case ICE: + return 404; + case GRASS: + return 405; + case FIRE: + return 406; + case DARK: + return 407; + case STEEL: + return 408; + case ELECTRIC: + return 409; + case GROUND: + return 410; + case GHOST: + default: + return 411; // for CURSE + case ROCK: + return 412; + case FLYING: + return 413; + case BUG: + return 610; + } + } + +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/AbstractGBRomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/AbstractGBRomHandler.java new file mode 100755 index 000000000..63e507bb0 --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/AbstractGBRomHandler.java @@ -0,0 +1,135 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- AbstractGBRomHandler.java - a base class for GB/GBA rom handlers --*/ +/*-- which standardises common GB(A) functions.--*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +public abstract class AbstractGBRomHandler extends AbstractRomHandler { + + protected byte[] rom; + private String loadedFN; + + public boolean detectRom(String filename) { + if (new File(filename).length() > 32 * 1024 * 1024) { + return false; + } + byte[] loaded = loadFile(filename); + if (loaded.length == 0) { + // nope + return false; + } + return detectRom(loaded); + } + + @Override + public boolean loadRom(String filename) { + byte[] loaded = loadFile(filename); + if (!detectRom(loaded)) { + return false; + } + this.rom = loaded; + loadedFN = filename; + loadedRom(); + return true; + } + + @Override + public String loadedFilename() { + return loadedFN; + } + + @Override + public boolean saveRom(String filename) { + savingRom(); + try { + FileOutputStream fos = new FileOutputStream(filename); + fos.write(rom); + fos.close(); + return true; + } catch (IOException ex) { + return false; + } + } + + @Override + public boolean canChangeStaticPokemon() { + return true; + } + + public abstract boolean detectRom(byte[] rom); + + public abstract void loadedRom(); + + public abstract void savingRom(); + + private byte[] loadFile(String filename) { + try { + FileInputStream fis = new FileInputStream(filename); + byte[] file = new byte[fis.available()]; + fis.read(file); + fis.close(); + return file; + } catch (IOException ex) { + return new byte[0]; + } + } + + protected void readByteIntoFlags(boolean[] flags, int offsetIntoFlags, + int offsetIntoROM) { + int thisByte = rom[offsetIntoROM] & 0xFF; + for (int i = 0; i < 8 && (i + offsetIntoFlags) < flags.length; i++) { + flags[offsetIntoFlags + i] = ((thisByte >> i) & 0x01) == 0x01; + } + } + + protected byte getByteFromFlags(boolean[] flags, int offsetIntoFlags) { + int thisByte = 0; + for (int i = 0; i < 8 && (i + offsetIntoFlags) < flags.length; i++) { + thisByte |= (flags[offsetIntoFlags + i] ? 1 : 0) << i; + } + return (byte) thisByte; + } + + protected int readWord(int offset) { + return readWord(rom, offset); + } + + protected int readWord(byte[] data, int offset) { + return (data[offset] & 0xFF) + ((data[offset + 1] & 0xFF) << 8); + } + + protected void writeWord(int offset, int value) { + writeWord(rom, offset, value); + } + + protected void writeWord(byte[] data, int offset, int value) { + data[offset] = (byte) (value % 0x100); + data[offset + 1] = (byte) ((value / 0x100) % 0x100); + } + +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/AbstractRomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/AbstractRomHandler.java new file mode 100755 index 000000000..7e1d4d7bd --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/AbstractRomHandler.java @@ -0,0 +1,2841 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- AbstractRomHandler.java - a base class for all rom handlers which --*/ +/*-- implements the majority of the actual --*/ +/*-- randomizer logic by building on the base --*/ +/*-- getters & setters provided by each concrete --*/ +/*-- handler. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import java.util.Stack; +import java.util.TreeMap; +import java.util.TreeSet; + +import javax.swing.JOptionPane; + +import com.dabomstew.pkrandom.FileFunctions; +import com.dabomstew.pkrandom.RandomSource; +import com.dabomstew.pkrandom.RomFunctions; +import com.dabomstew.pkrandom.gui.RandomizerGUI; +import com.dabomstew.pkrandom.pokemon.Encounter; +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.ExpCurve; +import com.dabomstew.pkrandom.pokemon.GenRestrictions; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.ItemList; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.TrainerPokemon; +import com.dabomstew.pkrandom.pokemon.Type; + +public abstract class AbstractRomHandler implements RomHandler { + + private static final String tnamesFile = "trainernames.txt"; + private static final String tclassesFile = "trainerclasses.txt"; + private static final String nnamesFile = "nicknames.txt"; + + private boolean restrictionsSet; + protected List mainPokemonList; + protected List noLegendaryList, onlyLegendaryList; + + /* Constructor */ + + public AbstractRomHandler() { + } + + /* Public Methods, implemented here for all gens */ + + protected void checkPokemonRestrictions() { + if (!restrictionsSet) { + setPokemonPool(null); + } + } + + public void setPokemonPool(GenRestrictions restrictions) { + restrictionsSet = true; + mainPokemonList = this.allPokemonWithoutNull(); + if (restrictions != null) { + mainPokemonList = new ArrayList(); + List allPokemon = this.getPokemon(); + List evos = this.getEvolutions(); + + if (restrictions.allow_gen1) { + addPokesFromRange(mainPokemonList, allPokemon, 1, 151); + if (restrictions.assoc_g1_g2 && allPokemon.size() > 251) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 1, 151, + 152, 251); + } + if (restrictions.assoc_g1_g4 && allPokemon.size() > 493) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 1, 151, + 387, 493); + } + } + + if (restrictions.allow_gen2 && allPokemon.size() > 251) { + addPokesFromRange(mainPokemonList, allPokemon, 152, 251); + if (restrictions.assoc_g2_g1) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 152, + 251, 1, 151); + } + if (restrictions.assoc_g2_g3 && allPokemon.size() > 386) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 152, + 251, 252, 386); + } + if (restrictions.assoc_g2_g4 && allPokemon.size() > 493) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 152, + 251, 387, 493); + } + } + + if (restrictions.allow_gen3 && allPokemon.size() > 386) { + addPokesFromRange(mainPokemonList, allPokemon, 252, 386); + if (restrictions.assoc_g3_g2) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 252, + 386, 152, 251); + } + if (restrictions.assoc_g3_g4 && allPokemon.size() > 493) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 252, + 386, 387, 493); + } + } + + if (restrictions.allow_gen4 && allPokemon.size() > 493) { + addPokesFromRange(mainPokemonList, allPokemon, 387, 493); + if (restrictions.assoc_g4_g1) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 387, + 493, 1, 151); + } + if (restrictions.assoc_g4_g2) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 387, + 493, 152, 251); + } + if (restrictions.assoc_g4_g3) { + addEvosFromRange(mainPokemonList, allPokemon, evos, 387, + 493, 252, 386); + } + } + + if (restrictions.allow_gen5 && allPokemon.size() > 649) { + addPokesFromRange(mainPokemonList, allPokemon, 494, 649); + } + } + + noLegendaryList = new ArrayList(); + onlyLegendaryList = new ArrayList(); + + for (Pokemon p : mainPokemonList) { + if (p.isLegendary()) { + onlyLegendaryList.add(p); + } else { + noLegendaryList.add(p); + } + } + } + + private void addPokesFromRange(List pokemonPool, + List allPokemon, int range_min, int range_max) { + for (int i = range_min; i <= range_max; i++) { + if (!pokemonPool.contains(allPokemon.get(i))) { + pokemonPool.add(allPokemon.get(i)); + } + } + } + + private void addEvosFromRange(List pokemonPool, + List allPokemon, List evos, int first_min, + int first_max, int second_min, int second_max) { + for (Evolution e : evos) { + Pokemon potential = null; + if ((e.from >= first_min && e.from <= first_max + && e.to >= second_min && e.to <= second_max)) { + potential = allPokemon.get(e.to); + } else if ((e.from >= second_min && e.from <= second_max + && e.to >= first_min && e.to <= first_max)) { + potential = allPokemon.get(e.from); + } + if (potential != null && !pokemonPool.contains(potential)) { + pokemonPool.add(potential); + } + } + } + + @Override + public void randomizePokemonStats(boolean evolutionSanity) { + List allPokes = this.getPokemon(); + List evolutions = this.getEvolutions(); + if (evolutionSanity) { + // Spread stats up MOST evolutions. + Set dontCopyPokes = RomFunctions + .getBasicOrNoCopyPokemon(this); + + for (Pokemon pk : dontCopyPokes) { + pk.randomizeStatsWithinBST(); + } + // go "up" evolutions looking for pre-evos to do first + for (Evolution evo : evolutions) { + if (evo.carryStats) { + Stack currentStack = new Stack(); + Evolution current = evo; + while (current != null) { + Evolution last = current; + currentStack.push(last); + current = null; + for (Evolution evo2 : evolutions) { + if (last.from == evo2.to && evo2.carryStats) { + current = evo2; + break; + } + } + } + // now we have a stack of evolutions + while (!currentStack.isEmpty()) { + Evolution useEvo = currentStack.pop(); + useEvo.carryStats = false; // so we don't waste time + // later + Pokemon to = allPokes.get(useEvo.to); + Pokemon from = allPokes.get(useEvo.from); + to.copyRandomizedStatsUpEvolution(from); + } + } + } + } else { + for (Pokemon pk : allPokes) { + if (pk != null) { + pk.randomizeStatsWithinBST(); + } + } + } + + } + + @Override + public void applyCamelCaseNames() { + List pokes = getPokemon(); + for (Pokemon pkmn : pokes) { + if (pkmn == null) { + continue; + } + pkmn.name = RomFunctions.camelCase(pkmn.name); + } + + } + + @Override + public void minimumCatchRate(int rateNonLegendary, int rateLegendary) { + List pokes = getPokemon(); + for (Pokemon pkmn : pokes) { + if (pkmn == null) { + continue; + } + int minCatchRate = pkmn.isLegendary() ? rateLegendary + : rateNonLegendary; + pkmn.catchRate = Math.max(pkmn.catchRate, minCatchRate); + } + + } + + @Override + public void standardizeEXPCurves() { + List pokes = getPokemon(); + for (Pokemon pkmn : pokes) { + if (pkmn == null) { + continue; + } + pkmn.growthCurve = pkmn.isLegendary() ? ExpCurve.SLOW + : ExpCurve.MEDIUM_FAST; + } + } + + @Override + public void randomizePokemonTypes(boolean evolutionSanity) { + if (evolutionSanity) { + Set dontCopyPokes = RomFunctions + .getBasicOrNoCopyPokemon(this); + // Type randomization + // Step 1: Basic or Excluded From Copying Pokemon + // A Basic/EFC pokemon has a 35% chance of a second type if it has + // an evolution, a 50% chance otherwise + for (Pokemon pk : dontCopyPokes) { + pk.primaryType = randomType(); + pk.secondaryType = null; + if (RomFunctions.pokemonHasEvo(this, pk)) { + if (RandomSource.random() < 0.35) { + pk.secondaryType = randomType(); + while (pk.secondaryType == pk.primaryType) { + pk.secondaryType = randomType(); + } + } + } else { + if (RandomSource.random() < 0.5) { + pk.secondaryType = randomType(); + while (pk.secondaryType == pk.primaryType) { + pk.secondaryType = randomType(); + } + } + } + } + // Step 2: First Evolutions + // A first evolution has a 15% chance of adding a type if there's a + // 3rd stage, Or a 25% chance if there's not + Set firstEvos = RomFunctions.getFirstEvolutions(this); + for (Pokemon pk : firstEvos) { + Pokemon evolvedFrom = RomFunctions.evolvesFrom(this, pk); + pk.primaryType = evolvedFrom.primaryType; + pk.secondaryType = evolvedFrom.secondaryType; + if (pk.secondaryType == null) { + if (RomFunctions.pokemonHasEvo(this, pk)) { + if (RandomSource.random() < 0.15) { + pk.secondaryType = randomType(); + while (pk.secondaryType == pk.primaryType) { + pk.secondaryType = randomType(); + } + } + } else { + if (RandomSource.random() < 0.25) { + pk.secondaryType = randomType(); + while (pk.secondaryType == pk.primaryType) { + pk.secondaryType = randomType(); + } + } + } + } + } + + // Step 3: Second Evolutions + // A second evolution has a 25% chance of adding a type + Set secondEvos = RomFunctions.getSecondEvolutions(this); + for (Pokemon pk : secondEvos) { + Pokemon evolvedFrom = RomFunctions.evolvesFrom(this, pk); + pk.primaryType = evolvedFrom.primaryType; + pk.secondaryType = evolvedFrom.secondaryType; + if (pk.secondaryType == null) { + if (RandomSource.random() < 0.25) { + pk.secondaryType = randomType(); + while (pk.secondaryType == pk.primaryType) { + pk.secondaryType = randomType(); + } + } + } + } + } else { + // Entirely random types + List allPokes = this.getPokemon(); + for (Pokemon pkmn : allPokes) { + if (pkmn != null) { + pkmn.primaryType = randomType(); + pkmn.secondaryType = null; + if (RandomSource.random() < 0.5) { + pkmn.secondaryType = randomType(); + while (pkmn.secondaryType == pkmn.primaryType) { + pkmn.secondaryType = randomType(); + } + } + } + } + } + } + + private static final int WONDER_GUARD_INDEX = 25; + + @Override + public void randomizeAbilities(boolean allowWonderGuard) { + // Abilities don't exist in some games... + if (this.abilitiesPerPokemon() == 0) { + return; + } + + // Deal with "natural" abilities first regardless + List allPokes = this.getPokemon(); + int maxAbility = this.highestAbilityIndex(); + for (Pokemon pk : allPokes) { + if (pk == null) { + continue; + } + + // Wonder Guard? + if (pk.ability1 != WONDER_GUARD_INDEX + && pk.ability2 != WONDER_GUARD_INDEX + && pk.ability3 != WONDER_GUARD_INDEX) { + // Pick first ability + pk.ability1 = RandomSource.nextInt(maxAbility) + 1; + // Wonder guard block + if (!allowWonderGuard) { + while (pk.ability1 == WONDER_GUARD_INDEX) { + pk.ability1 = RandomSource.nextInt(maxAbility) + 1; + } + } + + // Second ability? + if (RandomSource.nextDouble() < 0.5) { + // Yes, second ability + pk.ability2 = RandomSource.nextInt(maxAbility) + 1; + // Wonder guard? Also block first ability from reappearing + if (allowWonderGuard) { + while (pk.ability2 == pk.ability1) { + pk.ability2 = RandomSource.nextInt(maxAbility) + 1; + } + } else { + while (pk.ability2 == WONDER_GUARD_INDEX + || pk.ability2 == pk.ability1) { + pk.ability2 = RandomSource.nextInt(maxAbility) + 1; + } + } + } else { + // Nope + pk.ability2 = 0; + } + } + } + + // DW Abilities? + if (this.abilitiesPerPokemon() == 3) { + // Give a random DW ability to every Pokemon + for (Pokemon pk : allPokes) { + if (pk == null) { + continue; + } + if (pk.ability1 != WONDER_GUARD_INDEX + && pk.ability2 != WONDER_GUARD_INDEX + && pk.ability3 != WONDER_GUARD_INDEX) { + pk.ability3 = RandomSource.nextInt(maxAbility) + 1; + // Wonder guard? Also block other abilities from reappearing + if (allowWonderGuard) { + while (pk.ability3 == pk.ability1 + || pk.ability3 == pk.ability2) { + pk.ability3 = RandomSource.nextInt(maxAbility) + 1; + } + } else { + while (pk.ability3 == WONDER_GUARD_INDEX + || pk.ability3 == pk.ability1 + || pk.ability3 == pk.ability2) { + pk.ability3 = RandomSource.nextInt(maxAbility) + 1; + } + } + } + } + } + } + + public Pokemon randomPokemon() { + checkPokemonRestrictions(); + return mainPokemonList + .get(RandomSource.nextInt(mainPokemonList.size())); + } + + @Override + public Pokemon randomNonLegendaryPokemon() { + checkPokemonRestrictions(); + return noLegendaryList + .get(RandomSource.nextInt(noLegendaryList.size())); + } + + @Override + public Pokemon randomLegendaryPokemon() { + checkPokemonRestrictions(); + return onlyLegendaryList.get(RandomSource.nextInt(onlyLegendaryList + .size())); + } + + private List twoEvoPokes; + + @Override + public Pokemon random2EvosPokemon() { + if (twoEvoPokes == null) { + // Prepare the list + List allPokes = this.getPokemon(); + List remainingPokes = allPokemonWithoutNull(); + List allEvos = this.getEvolutions(); + Map reverseKeepPokemon = new TreeMap(); + for (Evolution e : allEvos) { + reverseKeepPokemon + .put(allPokes.get(e.to), allPokes.get(e.from)); + } + remainingPokes.retainAll(reverseKeepPokemon.values()); + // All pokemon with evolutions are left + // Look for the evolutions themselves again in the evo-list + Set keepFor2Evos = new TreeSet(); + for (Evolution e : allEvos) { + Pokemon from = allPokes.get(e.from); + if (reverseKeepPokemon.containsKey(from)) { + keepFor2Evos.add(reverseKeepPokemon.get(from)); + } + } + remainingPokes.retainAll(keepFor2Evos); + twoEvoPokes = remainingPokes; + } + return twoEvoPokes.get(RandomSource.nextInt(twoEvoPokes.size())); + } + + @Override + public void randomEncounters(boolean useTimeOfDay, boolean catchEmAll, + boolean typeThemed, boolean usePowerLevels, boolean noLegendaries) { + checkPokemonRestrictions(); + List currentEncounters = this.getEncounters(useTimeOfDay); + List banned = this.bannedForWildEncounters(); + // Assume EITHER catch em all OR type themed OR match strength for now + if (catchEmAll) { + + List allPokes = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList(mainPokemonList); + allPokes.removeAll(banned); + for (EncounterSet area : currentEncounters) { + for (Encounter enc : area.encounters) { + // Pick a random pokemon + int picked = RandomSource.nextInt(allPokes.size()); + enc.pokemon = allPokes.get(picked); + if (area.battleTrappersBanned + && hasBattleTrappingAbility(enc.pokemon)) { + // Skip past this Pokemon for now and just pick a random + // one + List pickable = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList( + mainPokemonList); + pickable.removeAll(banned); + if (pickable.size() == 0) { + JOptionPane.showMessageDialog(null, + "ERROR: Couldn't replace a Pokemon!"); + return; + } + while (hasBattleTrappingAbility(enc.pokemon)) { + picked = RandomSource.nextInt(pickable.size()); + enc.pokemon = pickable.get(picked); + } + } else { + // Picked this Pokemon, remove it + allPokes.remove(picked); + if (allPokes.size() == 0) { + // Start again + allPokes.addAll(noLegendaries ? noLegendaryList + : mainPokemonList); + allPokes.removeAll(banned); + } + } + } + } + } else if (typeThemed) { + Map> cachedPokeLists = new TreeMap>(); + for (EncounterSet area : currentEncounters) { + Type areaTheme = randomType(); + if (!cachedPokeLists.containsKey(areaTheme)) { + cachedPokeLists.put(areaTheme, + pokemonOfType(areaTheme, noLegendaries)); + } + List possiblePokemon = cachedPokeLists.get(areaTheme); + for (Encounter enc : area.encounters) { + // Pick a random themed pokemon + enc.pokemon = possiblePokemon.get(RandomSource + .nextInt(possiblePokemon.size())); + while (banned.contains(enc.pokemon) + || (area.battleTrappersBanned && hasBattleTrappingAbility(enc.pokemon))) { + enc.pokemon = possiblePokemon.get(RandomSource + .nextInt(possiblePokemon.size())); + } + } + } + } else if (usePowerLevels) { + List allowedPokes = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList(mainPokemonList); + allowedPokes.removeAll(banned); + for (EncounterSet area : currentEncounters) { + for (Encounter enc : area.encounters) { + enc.pokemon = pickWildPowerLvlReplacement(allowedPokes, + enc.pokemon, area.battleTrappersBanned, false, null); + } + } + } else { + // Entirely random + for (EncounterSet area : currentEncounters) { + for (Encounter enc : area.encounters) { + enc.pokemon = noLegendaries ? randomNonLegendaryPokemon() + : randomPokemon(); + while (banned.contains(enc.pokemon) + || (area.battleTrappersBanned && hasBattleTrappingAbility(enc.pokemon))) { + enc.pokemon = noLegendaries ? randomNonLegendaryPokemon() + : randomPokemon(); + } + } + } + } + + setEncounters(useTimeOfDay, currentEncounters); + } + + @Override + public void area1to1Encounters(boolean useTimeOfDay, boolean catchEmAll, + boolean typeThemed, boolean usePowerLevels, boolean noLegendaries) { + checkPokemonRestrictions(); + List currentEncounters = this.getEncounters(useTimeOfDay); + List banned = this.bannedForWildEncounters(); + // Assume EITHER catch em all OR type themed for now + if (catchEmAll) { + List allPokes = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList(mainPokemonList); + allPokes.removeAll(banned); + for (EncounterSet area : currentEncounters) { + // Poke-set + Set inArea = pokemonInArea(area); + // Build area map using catch em all + Map areaMap = new TreeMap(); + for (Pokemon areaPk : inArea) { + int picked = RandomSource.nextInt(allPokes.size()); + Pokemon pickedMN = allPokes.get(picked); + if (area.battleTrappersBanned + && hasBattleTrappingAbility(pickedMN)) { + // Skip past this Pokemon for now and just pick a random + // one + List pickable = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList( + mainPokemonList); + pickable.removeAll(banned); + if (pickable.size() == 0) { + JOptionPane.showMessageDialog(null, + "ERROR: Couldn't replace a Pokemon!"); + return; + } + while (hasBattleTrappingAbility(pickedMN)) { + picked = RandomSource.nextInt(pickable.size()); + pickedMN = pickable.get(picked); + } + areaMap.put(areaPk, pickedMN); + } else { + areaMap.put(areaPk, pickedMN); + allPokes.remove(picked); + if (allPokes.size() == 0) { + // Start again + allPokes.addAll(noLegendaries ? noLegendaryList + : mainPokemonList); + allPokes.removeAll(banned); + } + } + } + for (Encounter enc : area.encounters) { + // Apply the map + enc.pokemon = areaMap.get(enc.pokemon); + } + } + } else if (typeThemed) { + Map> cachedPokeLists = new TreeMap>(); + for (EncounterSet area : currentEncounters) { + Type areaTheme = randomType(); + if (!cachedPokeLists.containsKey(areaTheme)) { + cachedPokeLists.put(areaTheme, + pokemonOfType(areaTheme, noLegendaries)); + } + List possiblePokemon = new ArrayList( + cachedPokeLists.get(areaTheme)); + possiblePokemon.removeAll(banned); + // Poke-set + Set inArea = pokemonInArea(area); + // Build area map using type theme, reset the list if needed + Map areaMap = new TreeMap(); + for (Pokemon areaPk : inArea) { + int picked = RandomSource.nextInt(possiblePokemon.size()); + Pokemon pickedMN = possiblePokemon.get(picked); + if (area.battleTrappersBanned + && hasBattleTrappingAbility(pickedMN)) { + // Skip past this Pokemon for now and just pick a random + // one + List pickable = new ArrayList( + cachedPokeLists.get(areaTheme)); + pickable.removeAll(banned); + if (pickable.size() == 0) { + // Try all Pokemon instead + pickable = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList( + mainPokemonList); + pickable.removeAll(banned); + } + if (pickable.size() == 0) { + JOptionPane.showMessageDialog(null, + "ERROR: Couldn't replace a Pokemon!"); + return; + } + while (hasBattleTrappingAbility(pickedMN)) { + picked = RandomSource.nextInt(pickable.size()); + pickedMN = pickable.get(picked); + } + areaMap.put(areaPk, pickedMN); + } else { + areaMap.put(areaPk, pickedMN); + possiblePokemon.remove(picked); + if (possiblePokemon.size() == 0) { + // Start again + possiblePokemon.addAll(cachedPokeLists + .get(areaTheme)); + possiblePokemon.removeAll(banned); + } + } + } + for (Encounter enc : area.encounters) { + // Apply the map + enc.pokemon = areaMap.get(enc.pokemon); + } + } + } else if (usePowerLevels) { + List allowedPokes = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList(mainPokemonList); + allowedPokes.removeAll(banned); + for (EncounterSet area : currentEncounters) { + // Poke-set + Set inArea = pokemonInArea(area); + // Build area map using randoms + Map areaMap = new TreeMap(); + List usedPks = new ArrayList(); + for (Pokemon areaPk : inArea) { + Pokemon picked = pickWildPowerLvlReplacement(allowedPokes, + areaPk, area.battleTrappersBanned, false, usedPks); + areaMap.put(areaPk, picked); + usedPks.add(picked); + } + for (Encounter enc : area.encounters) { + // Apply the map + enc.pokemon = areaMap.get(enc.pokemon); + } + } + } else { + // Entirely random + for (EncounterSet area : currentEncounters) { + // Poke-set + Set inArea = pokemonInArea(area); + // Build area map using randoms + Map areaMap = new TreeMap(); + for (Pokemon areaPk : inArea) { + Pokemon picked = noLegendaries ? randomNonLegendaryPokemon() + : randomPokemon(); + while (areaMap.containsValue(picked) + || banned.contains(picked) + || (area.battleTrappersBanned && hasBattleTrappingAbility(picked))) { + picked = noLegendaries ? randomNonLegendaryPokemon() + : randomPokemon(); + } + areaMap.put(areaPk, picked); + } + for (Encounter enc : area.encounters) { + // Apply the map + enc.pokemon = areaMap.get(enc.pokemon); + } + } + } + + setEncounters(useTimeOfDay, currentEncounters); + + } + + @Override + public void game1to1Encounters(boolean useTimeOfDay, + boolean usePowerLevels, boolean noLegendaries) { + checkPokemonRestrictions(); + // Build the full 1-to-1 map + Map translateMap = new TreeMap(); + List remainingLeft = allPokemonWithoutNull(); + List remainingRight = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList(mainPokemonList); + List banned = this.bannedForWildEncounters(); + // Banned pokemon should be mapped to themselves + for (Pokemon bannedPK : banned) { + translateMap.put(bannedPK, bannedPK); + remainingLeft.remove(bannedPK); + remainingRight.remove(bannedPK); + } + while (remainingLeft.isEmpty() == false) { + if (usePowerLevels) { + int pickedLeft = RandomSource.nextInt(remainingLeft.size()); + Pokemon pickedLeftP = remainingLeft.remove(pickedLeft); + Pokemon pickedRightP = null; + if (remainingRight.size() == 1) { + // pick this (it may or may not be the same poke) + pickedRightP = remainingRight.get(0); + } else { + // pick on power level with the current one blocked + pickedRightP = pickWildPowerLvlReplacement(remainingRight, + pickedLeftP, false, true, null); + } + remainingRight.remove(pickedRightP); + translateMap.put(pickedLeftP, pickedRightP); + } else { + int pickedLeft = RandomSource.nextInt(remainingLeft.size()); + int pickedRight = RandomSource.nextInt(remainingRight.size()); + Pokemon pickedLeftP = remainingLeft.remove(pickedLeft); + Pokemon pickedRightP = remainingRight.get(pickedRight); + while (pickedLeftP.number == pickedRightP.number + && remainingRight.size() != 1) { + // Reroll for a different pokemon if at all possible + pickedRight = RandomSource.nextInt(remainingRight.size()); + pickedRightP = remainingRight.get(pickedRight); + } + remainingRight.remove(pickedRight); + translateMap.put(pickedLeftP, pickedRightP); + } + if (remainingRight.size() == 0) { + // restart + remainingRight.addAll(noLegendaries ? noLegendaryList + : mainPokemonList); + remainingRight.removeAll(banned); + } + } + + // Map remaining to themselves just in case + List allPokes = allPokemonWithoutNull(); + for (Pokemon poke : allPokes) { + if (!translateMap.containsKey(poke)) { + translateMap.put(poke, poke); + } + } + + List currentEncounters = this.getEncounters(useTimeOfDay); + + for (EncounterSet area : currentEncounters) { + for (Encounter enc : area.encounters) { + // Apply the map + enc.pokemon = translateMap.get(enc.pokemon); + if (area.battleTrappersBanned + && hasBattleTrappingAbility(enc.pokemon)) { + // Ignore the map and put a random non-trapping Poke + List pickable = noLegendaries ? new ArrayList( + noLegendaryList) : new ArrayList( + mainPokemonList); + pickable.removeAll(banned); + if (pickable.size() == 0) { + JOptionPane.showMessageDialog(null, + "ERROR: Couldn't replace a Pokemon!"); + return; + } + if (usePowerLevels) { + enc.pokemon = pickWildPowerLvlReplacement(pickable, + enc.pokemon, true, false, null); + } else { + while (hasBattleTrappingAbility(enc.pokemon)) { + int picked = RandomSource.nextInt(pickable.size()); + enc.pokemon = pickable.get(picked); + } + } + } + } + } + + setEncounters(useTimeOfDay, currentEncounters); + + } + + @Override + public void randomizeTrainerPokes(boolean rivalCarriesStarter, + boolean usePowerLevels, boolean noLegendaries, + boolean noEarlyWonderGuard) { + checkPokemonRestrictions(); + List currentTrainers = this.getTrainers(); + cachedReplacementLists = new TreeMap>(); + cachedAllList = noLegendaries ? new ArrayList(noLegendaryList) + : new ArrayList(mainPokemonList); + + // Fully random is easy enough - randomize then worry about rival + // carrying starter at the end + for (Trainer t : currentTrainers) { + if (t.tag != null && t.tag.equals("IRIVAL")) { + continue; // skip + } + for (TrainerPokemon tp : t.pokemon) { + boolean wgAllowed = (!noEarlyWonderGuard) || tp.level >= 20; + tp.pokemon = pickReplacement(tp.pokemon, usePowerLevels, null, + noLegendaries, wgAllowed); + } + } + + // Rival carries starter? + if (rivalCarriesStarter) { + rivalCarriesStarterUpdate(currentTrainers, "RIVAL", 1); + rivalCarriesStarterUpdate(currentTrainers, "FRIEND", 2); + } + + // Save it all up + this.setTrainers(currentTrainers); + } + + @Override + public void typeThemeTrainerPokes(boolean rivalCarriesStarter, + boolean usePowerLevels, boolean weightByFrequency, + boolean noLegendaries, boolean noEarlyWonderGuard) { + checkPokemonRestrictions(); + List currentTrainers = this.getTrainers(); + cachedReplacementLists = new TreeMap>(); + cachedAllList = noLegendaries ? new ArrayList(noLegendaryList) + : new ArrayList(mainPokemonList); + typeWeightings = new TreeMap(); + totalTypeWeighting = 0; + + // Construct groupings for types + // Anything starting with GYM or ELITE or CHAMPION is a group + Set assignedTrainers = new TreeSet(); + Map> groups = new TreeMap>(); + for (Trainer t : currentTrainers) { + if (t.tag != null && t.tag.equals("IRIVAL")) { + continue; // skip + } + String group = t.tag == null ? "" : t.tag; + if (group.contains("-")) { + group = group.substring(0, group.indexOf('-')); + } + if (group.startsWith("GYM") || group.startsWith("ELITE") + || group.startsWith("CHAMPION") + || group.startsWith("THEMED")) { + // Yep this is a group + if (!groups.containsKey(group)) { + groups.put(group, new ArrayList()); + } + groups.get(group).add(t); + assignedTrainers.add(t); + } else if (group.startsWith("GIO")) { + // Giovanni has same grouping as his gym, gym 8 + if (!groups.containsKey("GYM8")) { + groups.put("GYM8", new ArrayList()); + } + groups.get("GYM8").add(t); + assignedTrainers.add(t); + } + } + + // Give a type to each group + // Gym & elite types have to be unique + // So do uber types, including the type we pick for champion + Set usedGymTypes = new TreeSet(); + Set usedEliteTypes = new TreeSet(); + Set usedUberTypes = new TreeSet(); + for (String group : groups.keySet()) { + List trainersInGroup = groups.get(group); + Type typeForGroup = pickType(weightByFrequency, noLegendaries); + if (group.startsWith("GYM")) { + while (usedGymTypes.contains(typeForGroup)) { + typeForGroup = pickType(weightByFrequency, noLegendaries); + } + usedGymTypes.add(typeForGroup); + } + if (group.startsWith("ELITE")) { + while (usedEliteTypes.contains(typeForGroup)) { + typeForGroup = pickType(weightByFrequency, noLegendaries); + } + usedEliteTypes.add(typeForGroup); + } + if (group.equals("CHAMPION")) { + usedUberTypes.add(typeForGroup); + } + // Themed groups just have a theme, no special criteria + for (Trainer t : trainersInGroup) { + for (TrainerPokemon tp : t.pokemon) { + boolean wgAllowed = (!noEarlyWonderGuard) || tp.level >= 20; + tp.pokemon = pickReplacement(tp.pokemon, usePowerLevels, + typeForGroup, noLegendaries, wgAllowed); + } + } + } + + // Give a type to each unassigned trainer + for (Trainer t : currentTrainers) { + if (t.tag != null && t.tag.equals("IRIVAL")) { + continue; // skip + } + + if (!assignedTrainers.contains(t)) { + Type typeForTrainer = pickType(weightByFrequency, noLegendaries); + // Ubers: can't have the same type as each other + if (t.tag != null && t.tag.equals("UBER")) { + while (usedUberTypes.contains(typeForTrainer)) { + typeForTrainer = pickType(weightByFrequency, + noLegendaries); + } + usedUberTypes.add(typeForTrainer); + } + for (TrainerPokemon tp : t.pokemon) { + boolean shedAllowed = (!noEarlyWonderGuard) + || tp.level >= 20; + tp.pokemon = pickReplacement(tp.pokemon, usePowerLevels, + typeForTrainer, noLegendaries, shedAllowed); + } + } + } + + // Rival carries starter? + if (rivalCarriesStarter) { + rivalCarriesStarterUpdate(currentTrainers, "RIVAL", 1); + rivalCarriesStarterUpdate(currentTrainers, "FRIEND", 2); + } + + // Save it all up + this.setTrainers(currentTrainers); + } + + @Override + public void randomizeMovesLearnt(boolean typeThemed, boolean noBroken, + boolean forceFourStartingMoves) { + // Get current sets + Map> movesets = this.getMovesLearnt(); + List hms = this.getHMMoves(); + @SuppressWarnings("unchecked") + List banned = noBroken ? this.getGameBreakingMoves() + : Collections.EMPTY_LIST; + for (Pokemon pkmn : movesets.keySet()) { + Set learnt = new TreeSet(); + List moves = movesets.get(pkmn); + // 4 starting moves? + if (forceFourStartingMoves) { + int lv1count = 0; + for (MoveLearnt ml : moves) { + if (ml.level == 1) { + lv1count++; + } + } + if (lv1count < 4) { + for (int i = 0; i < 4 - lv1count; i++) { + MoveLearnt fakeLv1 = new MoveLearnt(); + fakeLv1.level = 1; + fakeLv1.move = 0; + moves.add(0, fakeLv1); + } + } + } + // Last level 1 move should be replaced with a damaging one + int damagingMove = pickMove(pkmn, typeThemed, true, hms); + // Find last lv1 move + // lv1index ends up as the index of the first non-lv1 move + int lv1index = 0; + while (lv1index < moves.size() && moves.get(lv1index).level == 1) { + lv1index++; + } + // last lv1 move is 1 before lv1index + if (lv1index == 0) { + lv1index++; + } + moves.get(lv1index - 1).move = damagingMove; + moves.get(lv1index - 1).level = 1; // just in case + learnt.add(damagingMove); + // Rest replace with randoms + for (int i = 0; i < moves.size(); i++) { + if (i == (lv1index - 1)) { + continue; + } + int picked = pickMove(pkmn, typeThemed, false, hms); + while (learnt.contains(picked) || banned.contains(picked)) { + picked = pickMove(pkmn, typeThemed, false, hms); + } + moves.get(i).move = picked; + learnt.add(picked); + } + } + // Done, save + this.setMovesLearnt(movesets); + + } + + private static final int METRONOME_MOVE = 118; + + @Override + public void metronomeOnlyMode() { + // TODO fix static pokemon with set movesets + + // movesets + Map> movesets = this.getMovesLearnt(); + + MoveLearnt metronomeML = new MoveLearnt(); + metronomeML.level = 1; + metronomeML.move = METRONOME_MOVE; + + for (List ms : movesets.values()) { + if (ms != null && ms.size() > 0) { + ms.clear(); + ms.add(metronomeML); + } + } + + this.setMovesLearnt(movesets); + + // trainers + // run this to remove all custom non-Metronome moves + this.setTrainers(this.getTrainers()); + + // tms + List tmMoves = this.getTMMoves(); + + for (int i = 0; i < tmMoves.size(); i++) { + tmMoves.set(i, METRONOME_MOVE); + } + + this.setTMMoves(tmMoves); + + // movetutors + if (this.hasMoveTutors()) { + List mtMoves = this.getMoveTutorMoves(); + + for (int i = 0; i < mtMoves.size(); i++) { + mtMoves.set(i, METRONOME_MOVE); + } + + this.setMoveTutorMoves(mtMoves); + } + + // move tweaks + List moveData = this.getMoves(); + + Move metronome = moveData.get(METRONOME_MOVE); + + metronome.pp = 40; + + List hms = this.getHMMoves(); + + for (int hm : hms) { + Move thisHM = moveData.get(hm); + thisHM.pp = 0; + } + } + + @Override + public void randomizeStaticPokemon(boolean legendForLegend) { + // Load + checkPokemonRestrictions(); + List currentStaticPokemon = this.getStaticPokemon(); + List replacements = new ArrayList(); + List banned = this.bannedForStaticPokemon(); + + if (legendForLegend) { + List legendariesLeft = new ArrayList( + onlyLegendaryList); + List nonlegsLeft = new ArrayList(noLegendaryList); + legendariesLeft.removeAll(banned); + nonlegsLeft.removeAll(banned); + for (int i = 0; i < currentStaticPokemon.size(); i++) { + Pokemon old = currentStaticPokemon.get(i); + Pokemon newPK; + if (old.isLegendary()) { + newPK = legendariesLeft.remove(RandomSource + .nextInt(legendariesLeft.size())); + if (legendariesLeft.size() == 0) { + legendariesLeft.addAll(onlyLegendaryList); + legendariesLeft.removeAll(banned); + } + } else { + newPK = nonlegsLeft.remove(RandomSource.nextInt(nonlegsLeft + .size())); + if (nonlegsLeft.size() == 0) { + nonlegsLeft.addAll(onlyLegendaryList); + nonlegsLeft.removeAll(banned); + } + } + replacements.add(newPK); + } + } else { + List pokemonLeft = new ArrayList(mainPokemonList); + pokemonLeft.removeAll(banned); + for (int i = 0; i < currentStaticPokemon.size(); i++) { + Pokemon newPK = pokemonLeft.remove(RandomSource + .nextInt(pokemonLeft.size())); + if (pokemonLeft.size() == 0) { + pokemonLeft.addAll(mainPokemonList); + pokemonLeft.removeAll(banned); + } + replacements.add(newPK); + } + } + + // Save + this.setStaticPokemon(replacements); + } + + @Override + public void randomizeTMMoves(boolean noBroken) { + // Pick some random TM moves. + int tmCount = this.getTMCount(); + List allMoves = this.getMoves(); + List newTMs = new ArrayList(); + List hms = this.getHMMoves(); + @SuppressWarnings("unchecked") + List banned = noBroken ? this.getGameBreakingMoves() + : Collections.EMPTY_LIST; + for (int i = 0; i < tmCount; i++) { + int chosenMove = RandomSource.nextInt(allMoves.size() - 1) + 1; + while (newTMs.contains(chosenMove) + || RomFunctions.bannedRandomMoves[chosenMove] + || hms.contains(chosenMove) || banned.contains(chosenMove)) { + chosenMove = RandomSource.nextInt(allMoves.size() - 1) + 1; + } + newTMs.add(chosenMove); + } + this.setTMMoves(newTMs); + } + + @Override + public void randomizeTMHMCompatibility(boolean preferSameType) { + // Get current compatibility + Map compat = this.getTMHMCompatibility(); + List tmHMs = new ArrayList(this.getTMMoves()); + tmHMs.addAll(this.getHMMoves()); + List moveData = this.getMoves(); + for (Map.Entry compatEntry : compat.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + for (int i = 1; i <= tmHMs.size(); i++) { + int move = tmHMs.get(i - 1); + Move mv = moveData.get(move); + double probability = 0.5; + if (preferSameType) { + if (pkmn.primaryType.equals(mv.type) + || (pkmn.secondaryType != null && pkmn.secondaryType + .equals(mv.type))) { + probability = 0.9; + } else if (mv.type != null && mv.type.equals(Type.NORMAL)) { + probability = 0.5; + } else { + probability = 0.25; + } + } + flags[i] = (RandomSource.random() < probability); + } + } + + // Set the new compatibility + this.setTMHMCompatibility(compat); + } + + @Override + public void randomizeMoveTutorMoves(boolean noBroken) { + if (!this.hasMoveTutors()) { + return; + } + // Pick some random Move Tutor moves, excluding TMs. + int mtCount = this.getMoveTutorMoves().size(); + List allMoves = this.getMoves(); + List tms = this.getTMMoves(); + List newMTs = new ArrayList(); + List hms = this.getHMMoves(); + @SuppressWarnings("unchecked") + List banned = noBroken ? this.getGameBreakingMoves() + : Collections.EMPTY_LIST; + for (int i = 0; i < mtCount; i++) { + int chosenMove = RandomSource.nextInt(allMoves.size() - 1) + 1; + while (newMTs.contains(chosenMove) || tms.contains(chosenMove) + || RomFunctions.bannedRandomMoves[chosenMove] + || hms.contains(chosenMove) || banned.contains(chosenMove)) { + chosenMove = RandomSource.nextInt(allMoves.size() - 1) + 1; + } + newMTs.add(chosenMove); + } + this.setMoveTutorMoves(newMTs); + } + + @Override + public void randomizeMoveTutorCompatibility(boolean preferSameType) { + if (!this.hasMoveTutors()) { + return; + } + // Get current compatibility + Map compat = this.getMoveTutorCompatibility(); + List mts = this.getMoveTutorMoves(); + List moveData = this.getMoves(); + for (Map.Entry compatEntry : compat.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + for (int i = 1; i <= mts.size(); i++) { + int move = mts.get(i - 1); + Move mv = moveData.get(move); + double probability = 0.5; + if (preferSameType) { + if (pkmn.primaryType.equals(mv.type) + || (pkmn.secondaryType != null && pkmn.secondaryType + .equals(mv.type))) { + probability = 0.9; + } else if (mv.type != null && mv.type.equals(Type.NORMAL)) { + probability = 0.5; + } else { + probability = 0.25; + } + } + flags[i] = (RandomSource.random() < probability); + } + } + + // Set the new compatibility + this.setMoveTutorCompatibility(compat); + + } + + @SuppressWarnings("unchecked") + @Override + public void randomizeTrainerNames(byte[] presetNames) { + List[] allTrainerNames = new List[] { new ArrayList(), + new ArrayList() }; + Map> trainerNamesByLength[] = new Map[] { + new TreeMap>(), + new TreeMap>() }; + // Check for the file + if (FileFunctions.configExists(tnamesFile)) { + try { + Scanner sc = null; + if (presetNames == null) { + sc = new Scanner(FileFunctions.openConfig(tnamesFile), + "UTF-8"); + } else { + sc = new Scanner(new ByteArrayInputStream(presetNames), + "UTF-8"); + } + while (sc.hasNextLine()) { + String trainername = sc.nextLine().trim(); + if (trainername.isEmpty()) { + continue; + } + if (trainername.startsWith("\uFEFF")) { + trainername = trainername.substring(1); + } + int idx = trainername.contains("&") ? 1 : 0; + int len = this.internalStringLength(trainername); + if (len <= 10) { + allTrainerNames[idx].add(trainername); + if (trainerNamesByLength[idx].containsKey(len)) { + trainerNamesByLength[idx].get(len).add(trainername); + } else { + List namesOfThisLength = new ArrayList(); + namesOfThisLength.add(trainername); + trainerNamesByLength[idx].put(len, + namesOfThisLength); + } + } + } + sc.close(); + } catch (FileNotFoundException e) { + // Can't read, just don't load anything + } + } + + // Get the current trainer names data + List currentTrainerNames = this.getTrainerNames(); + if (currentTrainerNames.size() == 0) { + // RBY have no trainer names + return; + } + TrainerNameMode mode = this.trainerNameMode(); + int maxLength = this.maxTrainerNameLength(); + + // Init the translation map and new list + Map translation = new HashMap(); + List newTrainerNames = new ArrayList(); + List tcNameLengths = this.getTCNameLengthsByTrainer(); + + // Start choosing + int tnIndex = -1; + for (String trainerName : currentTrainerNames) { + tnIndex++; + if (translation.containsKey(trainerName) + && trainerName.equalsIgnoreCase("GRUNT") == false + && trainerName.equalsIgnoreCase("EXECUTIVE") == false) { + // use an already picked translation + newTrainerNames.add(translation.get(trainerName)); + } else { + int idx = trainerName.contains("&") ? 1 : 0; + List pickFrom = allTrainerNames[idx]; + int intStrLen = this.internalStringLength(trainerName); + if (mode == TrainerNameMode.SAME_LENGTH) { + pickFrom = trainerNamesByLength[idx].get(intStrLen); + } + String changeTo = trainerName; + if (pickFrom != null && pickFrom.size() > 0 && intStrLen > 1) { + int tries = 0; + changeTo = pickFrom.get(RandomSource.nextInt(pickFrom + .size())); + int ctl = this.internalStringLength(changeTo); + while ((mode == TrainerNameMode.MAX_LENGTH && ctl > maxLength) + || (mode == TrainerNameMode.MAX_LENGTH_WITH_CLASS && ctl + + tcNameLengths.get(tnIndex) > maxLength)) { + tries++; + if (tries == 50) { + changeTo = trainerName; + break; + } + changeTo = pickFrom.get(RandomSource.nextInt(pickFrom + .size())); + ctl = this.internalStringLength(changeTo); + } + } + translation.put(trainerName, changeTo); + newTrainerNames.add(changeTo); + } + } + + // Done choosing, save + this.setTrainerNames(newTrainerNames); + } + + @Override + public int maxTrainerNameLength() { + // default: no real limit + return Integer.MAX_VALUE; + } + + @SuppressWarnings("unchecked") + @Override + public void randomizeTrainerClassNames(byte[] presetNames) { + List allTrainerClasses[] = new List[] { + new ArrayList(), new ArrayList() }; + Map> trainerClassesByLength[] = new Map[] { + new HashMap>(), + new HashMap>() }; + // Check for the file + if (FileFunctions.configExists(tclassesFile)) { + try { + Scanner sc = null; + if (presetNames == null) { + sc = new Scanner(FileFunctions.openConfig(tclassesFile), + "UTF-8"); + } else { + sc = new Scanner(new ByteArrayInputStream(presetNames), + "UTF-8"); + } + while (sc.hasNextLine()) { + String trainerClassName = sc.nextLine().trim(); + if (trainerClassName.isEmpty()) { + continue; + } + if (trainerClassName.startsWith("\uFEFF")) { + trainerClassName = trainerClassName.substring(1); + } + String checkName = trainerClassName.toLowerCase(); + int idx = (checkName.endsWith("couple") + || checkName.contains(" and ") + || checkName.endsWith("kin") + || checkName.endsWith("team") + || checkName.contains("&") || (checkName + .endsWith("s") && !checkName.endsWith("ss"))) ? 1 + : 0; + allTrainerClasses[idx].add(trainerClassName); + int len = this.internalStringLength(trainerClassName); + if (trainerClassesByLength[idx].containsKey(len)) { + trainerClassesByLength[idx].get(len).add( + trainerClassName); + } else { + List namesOfThisLength = new ArrayList(); + namesOfThisLength.add(trainerClassName); + trainerClassesByLength[idx].put(len, namesOfThisLength); + } + } + sc.close(); + } catch (FileNotFoundException e) { + // Can't read, just don't load anything + } + } + + // Get the current trainer names data + List currentClassNames = this.getTrainerClassNames(); + boolean mustBeSameLength = this.fixedTrainerClassNamesLength(); + int maxLength = this.maxTrainerClassNameLength(); + + // Init the translation map and new list + Map translation = new HashMap(); + List newClassNames = new ArrayList(); + + // Start choosing + for (String trainerClassName : currentClassNames) { + if (translation.containsKey(trainerClassName)) { + // use an already picked translation + newClassNames.add(translation.get(trainerClassName)); + } else { + String checkName = trainerClassName.toLowerCase(); + int idx = (checkName.endsWith("couple") + || checkName.contains(" and ") + || checkName.endsWith("kin") + || checkName.endsWith("team") + || checkName.contains(" & ") || (checkName + .endsWith("s") && !checkName.endsWith("ss"))) ? 1 : 0; + List pickFrom = allTrainerClasses[idx]; + int intStrLen = this.internalStringLength(trainerClassName); + if (mustBeSameLength) { + pickFrom = trainerClassesByLength[idx].get(intStrLen); + } + String changeTo = trainerClassName; + if (pickFrom != null && pickFrom.size() > 0) { + changeTo = pickFrom.get(RandomSource.nextInt(pickFrom + .size())); + while (changeTo.length() > maxLength) { + changeTo = pickFrom.get(RandomSource.nextInt(pickFrom + .size())); + } + } + translation.put(trainerClassName, changeTo); + newClassNames.add(changeTo); + } + } + + // Done choosing, save + this.setTrainerClassNames(newClassNames); + } + + @Override + public int maxTrainerClassNameLength() { + // default: no real limit + return Integer.MAX_VALUE; + } + + @Override + public void randomizeWildHeldItems() { + List pokemon = allPokemonWithoutNull(); + ItemList possibleItems = this.getAllowedItems(); + for (Pokemon pk : pokemon) { + if (pk.guaranteedHeldItem == -1 && pk.commonHeldItem == -1 + && pk.rareHeldItem == -1 && pk.darkGrassHeldItem == -1) { + // No held items at all, abort + return; + } + boolean canHaveDarkGrass = pk.darkGrassHeldItem != -1; + if (pk.guaranteedHeldItem != -1) { + // Guaranteed held items are supported. + if (pk.guaranteedHeldItem > 0) { + // Currently have a guaranteed item + double decision = RandomSource.nextDouble(); + if (decision < 0.9) { + // Stay as guaranteed + canHaveDarkGrass = false; + pk.guaranteedHeldItem = possibleItems.randomItem(); + } else { + // Change to 25% or 55% chance + pk.guaranteedHeldItem = 0; + pk.commonHeldItem = possibleItems.randomItem(); + pk.rareHeldItem = possibleItems.randomItem(); + while (pk.rareHeldItem == pk.commonHeldItem) { + pk.rareHeldItem = possibleItems.randomItem(); + } + } + } else { + // No guaranteed item atm + double decision = RandomSource.nextDouble(); + if (decision < 0.5) { + // No held item at all + pk.commonHeldItem = 0; + pk.rareHeldItem = 0; + } else if (decision < 0.65) { + // Just a rare item + pk.commonHeldItem = 0; + pk.rareHeldItem = possibleItems.randomItem(); + } else if (decision < 0.8) { + // Just a common item + pk.commonHeldItem = possibleItems.randomItem(); + pk.rareHeldItem = 0; + } else if (decision < 0.95) { + // Both a common and rare item + pk.commonHeldItem = possibleItems.randomItem(); + pk.rareHeldItem = possibleItems.randomItem(); + while (pk.rareHeldItem == pk.commonHeldItem) { + pk.rareHeldItem = possibleItems.randomItem(); + } + } else { + // Guaranteed item + canHaveDarkGrass = false; + pk.guaranteedHeldItem = possibleItems.randomItem(); + pk.commonHeldItem = 0; + pk.rareHeldItem = 0; + } + } + } else { + // Code for no guaranteed items + double decision = RandomSource.nextDouble(); + if (decision < 0.5) { + // No held item at all + pk.commonHeldItem = 0; + pk.rareHeldItem = 0; + } else if (decision < 0.65) { + // Just a rare item + pk.commonHeldItem = 0; + pk.rareHeldItem = possibleItems.randomItem(); + } else if (decision < 0.8) { + // Just a common item + pk.commonHeldItem = possibleItems.randomItem(); + pk.rareHeldItem = 0; + } else { + // Both a common and rare item + pk.commonHeldItem = possibleItems.randomItem(); + pk.rareHeldItem = possibleItems.randomItem(); + while (pk.rareHeldItem == pk.commonHeldItem) { + pk.rareHeldItem = possibleItems.randomItem(); + } + } + } + + if (canHaveDarkGrass) { + double dgDecision = RandomSource.nextDouble(); + if (dgDecision < 0.5) { + // Yes, dark grass item + pk.darkGrassHeldItem = possibleItems.randomItem(); + } else { + pk.darkGrassHeldItem = 0; + } + } else if (pk.darkGrassHeldItem != -1) { + pk.darkGrassHeldItem = 0; + } + } + + } + + @Override + public void randomizeStarterHeldItems() { + List oldHeldItems = this.getStarterHeldItems(); + List newHeldItems = new ArrayList(); + ItemList possibleItems = this.getAllowedItems(); + for (int i = 0; i < oldHeldItems.size(); i++) { + newHeldItems.add(possibleItems.randomItem()); + } + this.setStarterHeldItems(newHeldItems); + } + + @Override + public void shuffleFieldItems() { + List currentItems = this.getRegularFieldItems(); + List currentTMs = this.getCurrentFieldTMs(); + + Collections.shuffle(currentItems, RandomSource.instance()); + Collections.shuffle(currentTMs, RandomSource.instance()); + + this.setRegularFieldItems(currentItems); + this.setFieldTMs(currentTMs); + } + + @Override + public void randomizeFieldItems() { + ItemList possibleItems = this.getAllowedItems(); + List currentItems = this.getRegularFieldItems(); + List currentTMs = this.getCurrentFieldTMs(); + List requiredTMs = this.getRequiredFieldTMs(); + + int fieldItemCount = currentItems.size(); + int fieldTMCount = currentTMs.size(); + int reqTMCount = requiredTMs.size(); + int totalTMCount = this.getTMCount(); + + List newItems = new ArrayList(); + List newTMs = new ArrayList(); + + for (int i = 0; i < fieldItemCount; i++) { + newItems.add(possibleItems.randomNonTM()); + } + + newTMs.addAll(requiredTMs); + + for (int i = reqTMCount; i < fieldTMCount; i++) { + while (true) { + int tm = RandomSource.nextInt(totalTMCount) + 1; + if (!newTMs.contains(tm)) { + newTMs.add(tm); + break; + } + } + } + + Collections.shuffle(newItems, RandomSource.instance()); + Collections.shuffle(newTMs, RandomSource.instance()); + + this.setRegularFieldItems(newItems); + this.setFieldTMs(newTMs); + } + + @Override + public void randomizeIngameTrades(boolean randomizeRequest, + byte[] presetNicknames, boolean randomNickname, + byte[] presetTrainerNames, boolean randomOT, boolean randomStats, + boolean randomItem) { + checkPokemonRestrictions(); + // Process trainer names + List singleTrainerNames = new ArrayList(); + // Check for the file + if (FileFunctions.configExists(tnamesFile) && randomOT) { + int maxOT = this.maxTradeOTNameLength(); + try { + Scanner sc = null; + if (presetTrainerNames == null) { + sc = new Scanner(FileFunctions.openConfig(tnamesFile), + "UTF-8"); + } else { + sc = new Scanner(new ByteArrayInputStream( + presetTrainerNames), "UTF-8"); + } + while (sc.hasNextLine()) { + String trainername = sc.nextLine().trim(); + if (trainername.isEmpty()) { + continue; + } + if (trainername.startsWith("\uFEFF")) { + trainername = trainername.substring(1); + } + int idx = trainername.contains("&") ? 1 : 0; + int len = this.internalStringLength(trainername); + if (len <= maxOT && idx == 0 + && !singleTrainerNames.contains(trainername)) { + singleTrainerNames.add(trainername); + } + } + sc.close(); + } catch (FileNotFoundException e) { + // Can't read, just don't load anything + } + } + + // Process nicknames + List nicknames = new ArrayList(); + // Check for the file + if (FileFunctions.configExists(nnamesFile) && randomNickname) { + int maxNN = this.maxTradeNicknameLength(); + try { + Scanner sc = null; + if (presetNicknames == null) { + sc = new Scanner(FileFunctions.openConfig(nnamesFile), + "UTF-8"); + } else { + sc = new Scanner(new ByteArrayInputStream(presetNicknames), + "UTF-8"); + } + while (sc.hasNextLine()) { + String nickname = sc.nextLine().trim(); + if (nickname.isEmpty()) { + continue; + } + if (nickname.startsWith("\uFEFF")) { + nickname = nickname.substring(1); + } + int len = this.internalStringLength(nickname); + if (len <= maxNN && !nicknames.contains(nickname)) { + nicknames.add(nickname); + } + } + sc.close(); + } catch (FileNotFoundException e) { + // Can't read, just don't load anything + } + } + + // get old trades + List trades = this.getIngameTrades(); + List usedRequests = new ArrayList(); + List usedGivens = new ArrayList(); + List usedOTs = new ArrayList(); + List usedNicknames = new ArrayList(); + ItemList possibleItems = this.getAllowedItems(); + + int nickCount = nicknames.size(); + int trnameCount = singleTrainerNames.size(); + + for (IngameTrade trade : trades) { + // pick new given pokemon + Pokemon oldgiven = trade.givenPokemon; + Pokemon given = this.randomPokemon(); + while (usedGivens.contains(given)) { + given = this.randomPokemon(); + } + usedGivens.add(given); + trade.givenPokemon = given; + + // requested pokemon? + if (randomizeRequest) { + Pokemon request = this.randomPokemon(); + while (usedRequests.contains(request) || request == given) { + request = this.randomPokemon(); + } + usedRequests.add(request); + trade.requestedPokemon = request; + } + + // nickname? + if (randomNickname && nickCount > usedNicknames.size()) { + String nickname = nicknames + .get(RandomSource.nextInt(nickCount)); + while (usedNicknames.contains(nickname)) { + nickname = nicknames.get(RandomSource.nextInt(nickCount)); + } + usedNicknames.add(nickname); + trade.nickname = nickname; + } else if (trade.nickname.equalsIgnoreCase(oldgiven.name)) { + // change the name for sanity + trade.nickname = trade.givenPokemon.name; + } + + if (randomOT && trnameCount > usedOTs.size()) { + String ot = singleTrainerNames.get(RandomSource + .nextInt(trnameCount)); + while (usedOTs.contains(ot)) { + ot = singleTrainerNames.get(RandomSource + .nextInt(trnameCount)); + } + usedOTs.add(ot); + trade.otName = ot; + trade.otId = RandomSource.nextInt(65536); + } + + if (randomStats) { + int maxIV = this.hasDVs() ? 16 : 32; + for (int i = 0; i < trade.ivs.length; i++) { + trade.ivs[i] = RandomSource.nextInt(maxIV); + } + } + + if (randomItem) { + trade.item = possibleItems.randomItem(); + } + } + + // things that the game doesn't support should just be ignored + this.setIngameTrades(trades); + } + + @Override + public int maxTradeNicknameLength() { + return 10; + } + + @Override + public int maxTradeOTNameLength() { + return 7; + } + + private Map moveUpdates; + + @Override + public void initMoveUpdates() { + moveUpdates = new TreeMap(); + } + + @Override + public void printMoveUpdates() { + log("--Move Updates--"); + List moves = this.getMoves(); + for (int moveID : moveUpdates.keySet()) { + boolean[] changes = moveUpdates.get(moveID); + Move mv = moves.get(moveID); + List nonTypeChanges = new ArrayList(); + if (changes[0]) { + nonTypeChanges.add(String.format("%d power", mv.power)); + } + if (changes[1]) { + nonTypeChanges.add(String.format("%d PP", mv.pp)); + } + if (changes[2]) { + nonTypeChanges.add(String.format("%.00f%% accuracy", + mv.hitratio)); + } + String logStr = "Made " + mv.name; + // type or not? + if (changes[3]) { + logStr += " be " + mv.type + "-type"; + if (nonTypeChanges.size() > 0) { + logStr += " and"; + } + } + if (nonTypeChanges.size() > 0) { + logStr += " have "; + if (nonTypeChanges.size() == 3) { + logStr += nonTypeChanges.get(0) + ", " + + nonTypeChanges.get(1) + " and " + + nonTypeChanges.get(2); + } else if (nonTypeChanges.size() == 2) { + logStr += nonTypeChanges.get(0) + " and " + + nonTypeChanges.get(1); + } else { + logStr += nonTypeChanges.get(0); + } + } + log(logStr); + } + logBlankLine(); + } + + @Override + public void updateMovesToGen5() { + List moves = this.getMoves(); + + // gen1 + // Karate Chop => FIGHTING (gen1) + updateMoveType(moves, 2, Type.FIGHTING); + // Razor Wind => 100% accuracy (gen1/2) + updateMoveAccuracy(moves, 13, 100); + // Gust => FLYING (gen1) + updateMoveType(moves, 16, Type.FLYING); + // Wing Attack => 60 power (gen1) + updateMovePower(moves, 17, 60); + // Whirlwind => 100 accuracy + updateMoveAccuracy(moves, 18, 100); + // Fly => 90 power (gen1/2/3) + updateMovePower(moves, 19, 90); + // Bind => 85% accuracy (gen1-4) + updateMoveAccuracy(moves, 20, 85); + // Vine Whip => 15 pp (gen1/2/3) + updateMovePP(moves, 22, 15); + // Jump Kick => 10 pp, 100 power (gen1-4) + updateMovePP(moves, 26, 10); + updateMovePower(moves, 26, 100); + // Sand Attack => GROUND (gen1) + updateMoveType(moves, 28, Type.GROUND); + // Tackle => 50 power, 100% accuracy , gen1-4 + updateMovePower(moves, 33, 50); + updateMoveAccuracy(moves, 33, 100); + // Wrap => 90% accuracy (gen1-4) + updateMoveAccuracy(moves, 35, 90); + // Thrash => 120 power, 10pp (gen1-4) + updateMovePP(moves, 37, 10); + updateMovePower(moves, 37, 120); + // Double-Edge => 120 power (gen1) + updateMovePower(moves, 38, 120); + // Move 44, Bite, becomes dark (but doesn't exist anyway) + // Disable => 100% accuracy (gen1-4) + updateMoveAccuracy(moves, 50, 100); + // Blizzard => 70% accuracy (gen1) + updateMoveAccuracy(moves, 59, 70); + // Move 67, Low Kick, has weight-based power in gen3+ + // Low Kick => 100% accuracy (gen1) + updateMoveAccuracy(moves, 67, 100); + // Absorb => 25pp (gen1/2/3) + updateMovePP(moves, 71, 25); + // Mega Drain => 15pp (gen1/2/3) + updateMovePP(moves, 72, 15); + // Petal Dance => 120power, 10pp (gen1-4) + updateMovePP(moves, 80, 10); + updateMovePower(moves, 80, 120); + // Fire Spin => 35 power, 85% acc (gen1-4) + updateMoveAccuracy(moves, 83, 85); + updateMovePower(moves, 83, 35); + // Rock Throw => 90% accuracy (gen1) + updateMoveAccuracy(moves, 88, 90); + // Dig => 80 power (gen1/2/3) + updateMovePower(moves, 91, 80); + // Toxic => 90% accuracy (gen1-4) + updateMoveAccuracy(moves, 92, 90); + // Hypnosis => 60% accuracy + updateMoveAccuracy(moves, 95, 60); + // Recover => 10pp (gen1/2/3) + updateMovePP(moves, 105, 10); + // SelfDestruct => 200power (gen1) + updateMovePower(moves, 120, 200); + // Clamp => 85% acc (gen1-4) + updateMoveAccuracy(moves, 128, 85); + updateMovePP(moves, 128, 15); + // HJKick => 130 power, 10pp (gen1-4) + updateMovePP(moves, 136, 10); + updateMovePower(moves, 136, 130); + // Glare => 90% acc (gen1-4) + updateMoveAccuracy(moves, 137, 90); + // Poison Gas => 80% acc (gen1-4) + updateMoveAccuracy(moves, 139, 80); + // Flash => 100% acc (gen1/2/3) + updateMoveAccuracy(moves, 148, 100); + // Crabhammer => 90% acc (gen1-4) + updateMoveAccuracy(moves, 152, 90); + // Explosion => 250 power (gen1) + updateMovePower(moves, 153, 250); + // GEN2+ moves only from here + if (moves.size() >= 251) { + // Curse => GHOST (gen2-4) + updateMoveType(moves, 174, Type.GHOST); + // Cotton Spore => 100% acc (gen2-4) + updateMoveAccuracy(moves, 178, 100); + // Scary Face => 100% acc (gen2-4) + updateMoveAccuracy(moves, 184, 100); + // Zap Cannon => 120 power (gen2-3) + updateMovePower(moves, 192, 120); + // Bone Rush => 90% acc (gen2-4) + updateMoveAccuracy(moves, 198, 90); + // Outrage => 120 power (gen2-3) + updateMovePower(moves, 200, 120); + updateMovePP(moves, 200, 10); + // Giga Drain => 10pp (gen2-3), 75 power (gen2-4) + updateMovePP(moves, 202, 10); + updateMovePower(moves, 202, 75); + // Fury Cutter => 20 power (gen2-4) + updateMovePower(moves, 210, 20); + // Future Sight => 10 pp, 100 power, 100% acc (gen2-4) + updateMovePP(moves, 248, 10); + updateMovePower(moves, 248, 100); + updateMoveAccuracy(moves, 248, 100); + // Rock Smash => 40 power (gen2-3) + updateMovePower(moves, 249, 40); + // Whirlpool => 35 pow, 85% acc (gen2-4) + updateMovePower(moves, 250, 35); + updateMoveAccuracy(moves, 250, 85); + } + // GEN3+ only moves from here + if (moves.size() >= 354) { + // Uproar => 90 power (gen3-4) + updateMovePower(moves, 253, 90); + updateMovePP(moves, 254, 20); + updateMovePower(moves, 291, 80); + // Sand Tomb => 35 pow, 85% acc (gen3-4) + updateMovePower(moves, 328, 35); + updateMoveAccuracy(moves, 328, 85); + // Bullet Seed => 25 power (gen3-4) + updateMovePower(moves, 331, 25); + // Icicle Spear => 25 power (gen3-4) + updateMovePower(moves, 333, 25); + // Covet => 60 power (gen3-4) + updateMovePower(moves, 343, 60); + updateMovePower(moves, 348, 90); + // Rock Blast => 90% acc (gen3-4) + updateMoveAccuracy(moves, 350, 90); + // Doom Desire => 140 pow, 100% acc, gen3-4 + updateMovePower(moves, 353, 140); + updateMoveAccuracy(moves, 353, 100); + } + // GEN4+ only moves from here + if (moves.size() >= 467) { + // Feint => 30 pow + updateMovePower(moves, 364, 30); + // Last Resort => 140 pow + updateMovePower(moves, 387, 140); + // Drain Punch => 10 pp, 75 pow + updateMovePP(moves, 409, 10); + updateMovePower(moves, 409, 75); + // Magma Storm => 75% acc + updateMoveAccuracy(moves, 463, 75); + } + } + + @Override + public void updateMovesToGen6() { + List moves = this.getMoves(); + + // gen 1 + // Swords Dance 20 PP + updateMovePP(moves, 14, 20); + + // Vine Whip 25 PP, 45 Power + updateMovePP(moves, 22, 25); + updateMovePower(moves, 22, 45); + + // Pin Missile 25 Power, 95% Accuracy + updateMovePower(moves, 42, 25); + updateMoveAccuracy(moves, 42, 95); + + // Flamethrower 90 Power + updateMovePower(moves, 53, 90); + + // Hydro Pump 110 Power + updateMovePower(moves, 56, 110); + + // Surf 90 Power + updateMovePower(moves, 57, 90); + + // Ice Beam 90 Power + updateMovePower(moves, 58, 90); + + // Blizzard 110 Power + updateMovePower(moves, 59, 110); + + // Growth 20 PP + updateMovePP(moves, 74, 20); + + // Thunderbolt 90 Power + updateMovePower(moves, 85, 90); + + // Thunder 110 Power + updateMovePower(moves, 87, 110); + + // Minimize 10 PP + updateMovePP(moves, 107, 10); + + // Barrier 20 PP + updateMovePP(moves, 112, 20); + + // Lick 30 Power + updateMovePower(moves, 122, 30); + + // Smog 30 Power + updateMovePower(moves, 123, 30); + + // Fire Blast 110 Power + updateMovePower(moves, 126, 110); + + // Skull Bash 10 PP, 130 Power + updateMovePP(moves, 130, 10); + updateMovePower(moves, 130, 130); + + // Glare 100% Accuracy + updateMoveAccuracy(moves, 137, 100); + + // Poison Gas 90% Accuracy + updateMoveAccuracy(moves, 139, 90); + + // Bubble 40 Power + updateMovePower(moves, 145, 40); + + // Psywave 100% Accuracy + updateMoveAccuracy(moves, 149, 100); + + // Acid Armor 20 PP + updateMovePP(moves, 151, 20); + + // Crabhammer 100 Power + updateMovePower(moves, 152, 100); + + // Gen2+ only + if (moves.size() >= 251) { + // Thief 25 PP, 60 Power + updateMovePP(moves, 168, 25); + updateMovePower(moves, 168, 60); + + // Snore 50 Power + updateMovePower(moves, 173, 50); + + // Fury Cutter 40 Power + updateMovePower(moves, 210, 40); + + // Future Sight 120 Power + updateMovePower(moves, 248, 120); + } + + // Gen3+ only + if (moves.size() >= 354) { + // Heat Wave 95 Power + updateMovePower(moves, 257, 95); + + // Will-o-Wisp 85% Accuracy + updateMoveAccuracy(moves, 261, 85); + + // Smellingsalt 70 Power + updateMovePower(moves, 265, 70); + + // Knock off 65 Power + updateMovePower(moves, 282, 65); + + // Meteor Mash 90 Power, 90% Accuracy + updateMovePower(moves, 309, 90); + updateMoveAccuracy(moves, 309, 90); + + // Air Cutter 60 Power + updateMovePower(moves, 314, 60); + + // Overheat 130 Power + updateMovePower(moves, 315, 130); + + // Rock Tomb 15 PP, 60 Power, 95% Accuracy + updateMovePP(moves, 317, 15); + updateMovePower(moves, 317, 60); + updateMoveAccuracy(moves, 317, 95); + + // Extrasensory 20 PP + updateMovePP(moves, 326, 20); + + // Muddy Water 90 Power + updateMovePower(moves, 330, 90); + + // Covet 25 PP + updateMovePP(moves, 343, 25); + } + + // Gen4+ only + if (moves.size() >= 467) { + // Wake-Up Slap 70 Power + updateMovePower(moves, 358, 70); + + // Tailwind 15 PP + updateMovePP(moves, 366, 15); + + // Assurance 60 Power + updateMovePower(moves, 372, 60); + + // Psycho Shift 100% Accuracy + updateMoveAccuracy(moves, 375, 100); + + // Aura Sphere 80 Power + updateMovePower(moves, 396, 80); + + // Air Slash 15 PP + updateMovePP(moves, 403, 15); + + // Dragon Pulse 85 Power + updateMovePower(moves, 406, 85); + + // Power Gem 80 Power + updateMovePower(moves, 408, 80); + + // Energy Ball 90 Power + updateMovePower(moves, 412, 90); + + // Draco Meteor 130 Power + updateMovePower(moves, 434, 130); + + // Leaf Storm 130 Power + updateMovePower(moves, 437, 130); + + // Gunk Shot 80% Accuracy + updateMoveAccuracy(moves, 441, 80); + + // Chatter 65 Power + updateMovePower(moves, 448, 65); + + // Magma Storm 100 Power + updateMovePower(moves, 463, 100); + } + + // Gen5+ only + if (moves.size() >= 559) { + // Storm Throw 60 Power + updateMovePower(moves, 480, 60); + + // Synchronoise 120 Power + updateMovePower(moves, 485, 120); + + // Low Sweep 65 Power + updateMovePower(moves, 490, 65); + + // Hex 65 Power + updateMovePower(moves, 506, 65); + + // Incinerate 60 Power + updateMovePower(moves, 510, 60); + + // Pledges 80 Power + updateMovePower(moves, 518, 80); + updateMovePower(moves, 519, 80); + updateMovePower(moves, 520, 80); + + // Struggle Bug 50 Power + updateMovePower(moves, 522, 50); + + // Frost Breath 45 Power + // crits are 2x in these games + updateMovePower(moves, 524, 45); + + // Sacred Sword 15 PP + updateMovePP(moves, 533, 15); + + // Hurricane 110 Power + updateMovePower(moves, 542, 110); + + // Techno Blast 120 Power + updateMovePower(moves, 546, 120); + } + } + + private void updateMovePower(List moves, int moveNum, int power) { + Move mv = moves.get(moveNum); + if (mv.power != power) { + mv.power = power; + addMoveUpdate(moveNum, 0); + } + } + + private void updateMovePP(List moves, int moveNum, int pp) { + Move mv = moves.get(moveNum); + if (mv.pp != pp) { + mv.pp = pp; + addMoveUpdate(moveNum, 1); + } + } + + private void updateMoveAccuracy(List moves, int moveNum, int accuracy) { + Move mv = moves.get(moveNum); + if (Math.abs(mv.hitratio - accuracy) >= 1) { + mv.setAccuracy(accuracy); + addMoveUpdate(moveNum, 2); + } + } + + private void updateMoveType(List moves, int moveNum, Type type) { + Move mv = moves.get(moveNum); + if (mv.type != type) { + mv.type = type; + addMoveUpdate(moveNum, 3); + } + } + + private void addMoveUpdate(int moveNum, int updateType) { + if (!moveUpdates.containsKey(moveNum)) { + boolean[] updateField = new boolean[4]; + updateField[updateType] = true; + moveUpdates.put(moveNum, updateField); + } else { + moveUpdates.get(moveNum)[updateType] = true; + } + } + + private int pickMove(Pokemon pkmn, boolean typeThemed, boolean damaging, + List hms) { + // If damaging, we want a move with at least 80% accuracy and 2 power + List allMoves = this.getMoves(); + Type typeOfMove = null; + double picked = RandomSource.random(); + // Type? + if (typeThemed) { + if (pkmn.primaryType == Type.NORMAL + || pkmn.secondaryType == Type.NORMAL) { + if (pkmn.secondaryType == null) { + // Pure NORMAL: 75% normal, 25% random + if (picked < 0.75) { + typeOfMove = Type.NORMAL; + } + // else random + } else { + // Find the other type + // Normal/OTHER: 30% normal, 55% other, 15% random + Type otherType = pkmn.primaryType; + if (otherType == Type.NORMAL) { + otherType = pkmn.secondaryType; + } + if (picked < 0.3) { + typeOfMove = Type.NORMAL; + } else if (picked < 0.85) { + typeOfMove = otherType; + } + // else random + } + } else if (pkmn.secondaryType != null) { + // Primary/Secondary: 50% primary, 30% secondary, 5% normal, 15% + // random + if (picked < 0.5) { + typeOfMove = pkmn.primaryType; + } else if (picked < 0.8) { + typeOfMove = pkmn.secondaryType; + } else if (picked < 0.85) { + typeOfMove = Type.NORMAL; + } + // else random + } else { + // Primary/None: 60% primary, 20% normal, 20% random + if (picked < 0.6) { + typeOfMove = pkmn.primaryType; + } else if (picked < 0.8) { + typeOfMove = Type.NORMAL; + } + // else random + } + } + // Filter by type, and if necessary, by damage + List canPick = new ArrayList(); + for (Move mv : allMoves) { + if (mv != null && !RomFunctions.bannedRandomMoves[mv.number] + && !hms.contains(mv.number) + && (mv.type == typeOfMove || typeOfMove == null)) { + if (!damaging + || (mv.power > 1 && mv.hitratio > 79 && !RomFunctions.bannedForDamagingMove[mv.number])) { + canPick.add(mv); + } + } + } + // If we ended up with no results, reroll + if (canPick.size() == 0) { + return pickMove(pkmn, typeThemed, damaging, hms); + } else { + // pick a random one + return canPick.get(RandomSource.nextInt(canPick.size())).number; + } + } + + private List pokemonOfType(Type type, boolean noLegendaries) { + List typedPokes = new ArrayList(); + for (Pokemon pk : mainPokemonList) { + if (pk != null && (!noLegendaries || !pk.isLegendary())) { + if (pk.primaryType == type || pk.secondaryType == type) { + typedPokes.add(pk); + } + } + } + return typedPokes; + } + + private List allPokemonWithoutNull() { + List allPokes = new ArrayList(this.getPokemon()); + allPokes.remove(0); + return allPokes; + } + + private Set pokemonInArea(EncounterSet area) { + Set inArea = new TreeSet(); + for (Encounter enc : area.encounters) { + inArea.add(enc.pokemon); + } + return inArea; + } + + private Map typeWeightings; + private int totalTypeWeighting; + + private Type pickType(boolean weightByFrequency, boolean noLegendaries) { + if (totalTypeWeighting == 0) { + // Determine weightings + for (Type t : Type.values()) { + if (typeInGame(t)) { + int pkWithTyping = pokemonOfType(t, noLegendaries).size(); + typeWeightings.put(t, pkWithTyping); + totalTypeWeighting += pkWithTyping; + } + } + } + + if (weightByFrequency) { + int typePick = RandomSource.nextInt(totalTypeWeighting); + int typePos = 0; + for (Type t : typeWeightings.keySet()) { + int weight = typeWeightings.get(t); + if (typePos + weight > typePick) { + return t; + } + typePos += weight; + } + return null; + } else { + return randomType(); + } + } + + private void rivalCarriesStarterUpdate(List currentTrainers, + String prefix, int pokemonOffset) { + // Find the highest rival battle # + int highestRivalNum = 0; + for (Trainer t : currentTrainers) { + if (t.tag != null && t.tag.startsWith(prefix)) { + highestRivalNum = Math.max( + highestRivalNum, + Integer.parseInt(t.tag.substring(prefix.length(), + t.tag.indexOf('-')))); + } + } + + if (highestRivalNum == 0) { + // This rival type not used in this game + return; + } + + // Get the starters + // us 0 1 2 => them 0+n 1+n 2+n + List starters = this.getStarters(); + + // Yellow needs its own case, unfortunately. + if (isYellow()) { + // The rival's starter is index 1 + Pokemon rivalStarter = starters.get(1); + int timesEvolves = timesEvolves(rivalStarter); + // Apply evolutions as appropriate + if (timesEvolves == 0) { + for (int j = 1; j <= 3; j++) { + changeStarterWithTag(currentTrainers, prefix + j + "-0", + rivalStarter); + } + for (int j = 4; j <= 7; j++) { + for (int i = 0; i < 3; i++) { + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, rivalStarter); + } + } + } else if (timesEvolves == 1) { + for (int j = 1; j <= 3; j++) { + changeStarterWithTag(currentTrainers, prefix + j + "-0", + rivalStarter); + } + rivalStarter = firstEvolution(rivalStarter); + for (int j = 4; j <= 7; j++) { + for (int i = 0; i < 3; i++) { + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, rivalStarter); + } + } + } else if (timesEvolves == 2) { + for (int j = 1; j <= 2; j++) { + changeStarterWithTag(currentTrainers, prefix + j + "-" + 0, + rivalStarter); + } + rivalStarter = firstEvolution(rivalStarter); + changeStarterWithTag(currentTrainers, prefix + "3-0", + rivalStarter); + for (int i = 0; i < 3; i++) { + changeStarterWithTag(currentTrainers, prefix + "4-" + i, + rivalStarter); + } + rivalStarter = firstEvolution(rivalStarter); + for (int j = 5; j <= 7; j++) { + for (int i = 0; i < 3; i++) { + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, rivalStarter); + } + } + } + } else { + // Replace each starter as appropriate + // Use level to determine when to evolve, not number anymore + for (int i = 0; i < 3; i++) { + // Rival's starters are pokemonOffset over from each of ours + int starterToUse = (i + pokemonOffset) % 3; + Pokemon thisStarter = starters.get(starterToUse); + int timesEvolves = timesEvolves(thisStarter); + // If a fully evolved pokemon, use throughout + // Otherwise split by evolutions as appropriate + if (timesEvolves == 0) { + for (int j = 1; j <= highestRivalNum; j++) { + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, thisStarter); + } + } else if (timesEvolves == 1) { + int j = 1; + for (; j <= highestRivalNum / 2; j++) { + if (getLevelOfStarter(currentTrainers, prefix + j + "-" + + i) >= 30) { + break; + } + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, thisStarter); + } + thisStarter = firstEvolution(thisStarter); + for (; j <= highestRivalNum; j++) { + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, thisStarter); + } + } else if (timesEvolves == 2) { + int j = 1; + for (; j <= highestRivalNum; j++) { + if (getLevelOfStarter(currentTrainers, prefix + j + "-" + + i) >= 16) { + break; + } + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, thisStarter); + } + thisStarter = firstEvolution(thisStarter); + for (; j <= highestRivalNum; j++) { + if (getLevelOfStarter(currentTrainers, prefix + j + "-" + + i) >= 36) { + break; + } + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, thisStarter); + } + thisStarter = firstEvolution(thisStarter); + for (; j <= highestRivalNum; j++) { + changeStarterWithTag(currentTrainers, prefix + j + "-" + + i, thisStarter); + } + } + } + } + + } + + private int getLevelOfStarter(List currentTrainers, String tag) { + for (Trainer t : currentTrainers) { + if (t.tag != null && t.tag.equals(tag)) { + // Bingo, get highest level + // last pokemon is given priority +2 but equal priority + // = first pokemon wins, so its effectively +1 + // If it's tagged the same we can assume it's the same team + // just the opposite gender or something like that... + // So no need to check other trainers with same tag. + int highestLevel = t.pokemon.get(0).level; + int trainerPkmnCount = t.pokemon.size(); + for (int i = 1; i < trainerPkmnCount; i++) { + int levelBonus = (i == trainerPkmnCount - 1) ? 2 : 0; + if (t.pokemon.get(i).level + levelBonus > highestLevel) { + highestLevel = t.pokemon.get(i).level; + } + } + return highestLevel; + } + } + return 0; + } + + private void changeStarterWithTag(List currentTrainers, + String tag, Pokemon starter) { + for (Trainer t : currentTrainers) { + if (t.tag != null && t.tag.equals(tag)) { + // Bingo + // Change the highest level pokemon, not the last. + // BUT: last gets +2 lvl priority (effectively +1) + // same as above, equal priority = earlier wins + TrainerPokemon bestPoke = t.pokemon.get(0); + int trainerPkmnCount = t.pokemon.size(); + for (int i = 1; i < trainerPkmnCount; i++) { + int levelBonus = (i == trainerPkmnCount - 1) ? 2 : 0; + if (t.pokemon.get(i).level + levelBonus > bestPoke.level) { + bestPoke = t.pokemon.get(i); + } + } + bestPoke.pokemon = starter; + } + } + + } + + private int timesEvolves(Pokemon pk) { + // This method works ASSUMING a pokemon has no weird split evolutions + // with different levels on each side + // Which is true for every pokemon so far. + List evos = this.getEvolutions(); + List pokes = this.getPokemon(); + for (Evolution e : evos) { + if (e.from == pk.number) { + return timesEvolves(pokes.get(e.to)) + 1; + } + } + return 0; + } + + private Pokemon firstEvolution(Pokemon pk) { + List evos = this.getEvolutions(); + List pokes = this.getPokemon(); + for (Evolution e : evos) { + if (e.from == pk.number) { + return pokes.get(e.to); + } + } + return null; + } + + private Map> cachedReplacementLists; + private List cachedAllList; + + private Pokemon pickReplacement(Pokemon current, boolean usePowerLevels, + Type type, boolean noLegendaries, boolean wonderGuardAllowed) { + List pickFrom = cachedAllList; + if (type != null) { + if (!cachedReplacementLists.containsKey(type)) { + cachedReplacementLists.put(type, + pokemonOfType(type, noLegendaries)); + } + pickFrom = cachedReplacementLists.get(type); + } + + if (usePowerLevels) { + // start with within 10% and add 5% either direction till we find + // something + int currentBST = current.bstForPowerLevels(); + int minTarget = currentBST - currentBST / 10; + int maxTarget = currentBST + currentBST / 10; + List canPick = new ArrayList(); + while (canPick.isEmpty()) { + for (Pokemon pk : pickFrom) { + if (pk.bstForPowerLevels() >= minTarget + && pk.bstForPowerLevels() <= maxTarget + && (wonderGuardAllowed || (pk.ability1 != WONDER_GUARD_INDEX + && pk.ability2 != WONDER_GUARD_INDEX && pk.ability3 != WONDER_GUARD_INDEX))) { + canPick.add(pk); + } + } + minTarget -= currentBST / 20; + maxTarget += currentBST / 20; + } + return canPick.get(RandomSource.nextInt(canPick.size())); + } else { + if (wonderGuardAllowed) { + return pickFrom.get(RandomSource.nextInt(pickFrom.size())); + } else { + Pokemon pk = pickFrom + .get(RandomSource.nextInt(pickFrom.size())); + while (pk.ability1 == WONDER_GUARD_INDEX + || pk.ability2 == WONDER_GUARD_INDEX + || pk.ability3 == WONDER_GUARD_INDEX) { + pk = pickFrom.get(RandomSource.nextInt(pickFrom.size())); + } + return pk; + } + } + } + + private Pokemon pickWildPowerLvlReplacement(List pokemonPool, + Pokemon current, boolean banBattleTrappers, boolean banSamePokemon, + List usedUp) { + // start with within 10% and add 5% either direction till we find + // something + int currentBST = current.bstForPowerLevels(); + int minTarget = currentBST - currentBST / 10; + int maxTarget = currentBST + currentBST / 10; + List canPick = new ArrayList(); + while (canPick.isEmpty()) { + for (Pokemon pk : pokemonPool) { + if (pk.bstForPowerLevels() >= minTarget + && pk.bstForPowerLevels() <= maxTarget + && (!banBattleTrappers || !hasBattleTrappingAbility(pk)) + && (!banSamePokemon || pk != current) + && (usedUp == null || !usedUp.contains(pk))) { + canPick.add(pk); + } + } + minTarget -= currentBST / 20; + maxTarget += currentBST / 20; + } + return canPick.get(RandomSource.nextInt(canPick.size())); + } + + private static final List battleTrappingAbilities = Arrays.asList( + 23, 42, 71); + + private boolean hasBattleTrappingAbility(Pokemon pokemon) { + return battleTrappingAbilities.contains(pokemon.ability1) + || battleTrappingAbilities.contains(pokemon.ability2) + || battleTrappingAbilities.contains(pokemon.ability3); + } + + /* Helper methods used by subclasses */ + + protected void log(String log) { + RandomizerGUI.verboseLog.println(log); + } + + protected void logBlankLine() { + RandomizerGUI.verboseLog.println(); + } + + protected void logEvoChangeLevel(String pkFrom, String pkTo, int level) { + RandomizerGUI.verboseLog.printf("Made %s evolve into %s at level %d", + pkFrom, pkTo, level); + RandomizerGUI.verboseLog.println(); + } + + protected void logEvoChangeLevelWithItem(String pkFrom, String pkTo, + String itemName) { + RandomizerGUI.verboseLog.printf( + "Made %s evolve into %s by leveling up holding %s", pkFrom, + pkTo, itemName); + RandomizerGUI.verboseLog.println(); + } + + protected void logEvoChangeStone(String pkFrom, String pkTo, String itemName) { + RandomizerGUI.verboseLog.printf("Made %s evolve into %s using a %s", + pkFrom, pkTo, itemName); + RandomizerGUI.verboseLog.println(); + } + + protected void logEvoChangeLevelWithPkmn(String pkFrom, String pkTo, + String otherRequired) { + RandomizerGUI.verboseLog.printf( + "Made %s evolve into %s by leveling up with %s in the party", + pkFrom, pkTo, otherRequired); + RandomizerGUI.verboseLog.println(); + } + + /* Default Implementations */ + /* Used when a subclass doesn't override */ + + @Override + public void fixTypeEffectiveness() { + // DEFAULT: do nothing + + } + + @Override + public boolean hasTimeBasedEncounters() { + // DEFAULT: no + return false; + } + + @Override + public boolean typeInGame(Type type) { + return type.isHackOnly == false; + } + + @Override + public String abilityName(int number) { + return ""; + } + + @Override + public Type randomType() { + Type t = Type.randomType(); + while (!typeInGame(t)) { + t = Type.randomType(); + } + return t; + } + + @Override + public boolean isYellow() { + return false; + } + + @Override + public void patchForNationalDex() { + // Default: Do Nothing. + + } + + @Override + public boolean canChangeStarters() { + return true; + } + + @SuppressWarnings("unchecked") + @Override + public List bannedForWildEncounters() { + return (List) Collections.EMPTY_LIST; + } + + @SuppressWarnings("unchecked") + @Override + public List bannedForStaticPokemon() { + return (List) Collections.EMPTY_LIST; + } + + @Override + public int codeTweaksAvailable() { + // default: none + return 0; + } + + @Override + public void applyBWEXPPatch() { + // default: do nothing + + } + + @Override + public void applyXAccNerfPatch() { + // default: do nothing + + } + + @Override + public void applyCritRatePatch() { + // default: do nothing + + } + + @Override + public boolean hasHiddenHollowPokemon() { + // default: no + return false; + } + + @Override + public void randomizeHiddenHollowPokemon() { + // default: do nothing + + } + + @Override + public List getGameBreakingMoves() { + // Sonicboom & drage + return Arrays.asList(49, 82); + } + + @Override + public boolean isROMHack() { + // override until detection implemented + return false; + } + +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/Gen1RomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/Gen1RomHandler.java new file mode 100755 index 000000000..8d2835549 --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/Gen1RomHandler.java @@ -0,0 +1,2543 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- Gen1RomHandler.java - randomizer handler for R/B/Y. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.TreeMap; + +import com.dabomstew.pkrandom.CodeTweaks; +import com.dabomstew.pkrandom.FileFunctions; +import com.dabomstew.pkrandom.RomFunctions; +import com.dabomstew.pkrandom.pokemon.Encounter; +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.EvolutionType; +import com.dabomstew.pkrandom.pokemon.ExpCurve; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.ItemList; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.TrainerPokemon; +import com.dabomstew.pkrandom.pokemon.Type; + +public class Gen1RomHandler extends AbstractGBRomHandler { + + // Important RBY Data Structures + + private int[] pokeNumToRBYTable; + private int[] pokeRBYToNumTable; + private int[] moveNumToRomTable; + private int[] moveRomToNumTable; + private int pokedexCount; + + private static final Type[] typeTable = constructTypeTable(); + + private static Type[] constructTypeTable() { + Type[] table = new Type[256]; + table[0x00] = Type.NORMAL; + table[0x01] = Type.FIGHTING; + table[0x02] = Type.FLYING; + table[0x03] = Type.POISON; + table[0x04] = Type.GROUND; + table[0x05] = Type.ROCK; + table[0x07] = Type.BUG; + table[0x08] = Type.GHOST; + table[0x14] = Type.FIRE; + table[0x15] = Type.WATER; + table[0x16] = Type.GRASS; + table[0x17] = Type.ELECTRIC; + table[0x18] = Type.PSYCHIC; + table[0x19] = Type.ICE; + table[0x1A] = Type.DRAGON; + return table; + } + + private Type idToType(int value) { + if (typeTable[value] != null) { + return typeTable[value]; + } + if (romEntry.extraTypeLookup.containsKey(value)) { + return romEntry.extraTypeLookup.get(value); + } + return null; + } + + private byte typeToByte(Type type) { + if (type == null) { + return 0x00; // revert to normal + } + if (romEntry.extraTypeReverse.containsKey(type)) { + return romEntry.extraTypeReverse.get(type).byteValue(); + } + switch (type) { + case NORMAL: + return 0x00; + case FIGHTING: + return 0x01; + case FLYING: + return 0x02; + case POISON: + return 0x03; + case GROUND: + return 0x04; + case ROCK: + return 0x05; + case BUG: + return 0x07; + case GHOST: + return 0x08; + case FIRE: + return 0x14; + case WATER: + return 0x15; + case GRASS: + return 0x16; + case ELECTRIC: + return 0x17; + case PSYCHIC: + return 0x18; + case ICE: + return 0x19; + case DRAGON: + return 0x1A; + case STEEL: + return 0x17; // turn steel into electric, to account for + // magnemite/magneton + case DARK: + return 0x08; // turn dark into ghost + default: + return 0; // normal by default + } + } + + private static class RomEntry { + private String name; + private String romName; + private int version, nonJapanese; + private String extraTableFile; + private boolean isYellow; + private int crcInHeader = -1; + private Map codeTweaks = new HashMap(); + private List tmTexts = new ArrayList(); + private Map entries = new HashMap(); + private Map arrayEntries = new HashMap(); + private List staticPokemonSingle = new ArrayList(); + private List staticPokemonGameCorner = new ArrayList(); + private Map extraTypeLookup = new HashMap(); + private Map extraTypeReverse = new HashMap(); + + private int getValue(String key) { + if (!entries.containsKey(key)) { + entries.put(key, 0); + } + return entries.get(key); + } + } + + private static List roms; + private static ItemList allowedItems; + + static { + loadROMInfo(); + setupAllowedItems(); + } + + private static class GameCornerPokemon { + private int[] offsets; + + public String toString() { + return Arrays.toString(offsets); + } + } + + private static class TMTextEntry { + private int number; + private int offset; + private String template; + } + + private static void loadROMInfo() { + roms = new ArrayList(); + RomEntry current = null; + try { + Scanner sc = new Scanner( + FileFunctions.openConfig("gen1_offsets.ini"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine().trim(); + if (q.contains("//")) { + q = q.substring(0, q.indexOf("//")).trim(); + } + if (!q.isEmpty()) { + if (q.startsWith("[") && q.endsWith("]")) { + // New rom + current = new RomEntry(); + current.name = q.substring(1, q.length() - 1); + roms.add(current); + } else { + String[] r = q.split("=", 2); + if (r.length == 1) { + System.err.println("invalid entry " + q); + continue; + } + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + r[1] = r[1].trim(); + r[0] = r[0].trim(); + // Static Pokemon? + if (r[0].equals("StaticPokemonGameCorner[]")) { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + GameCornerPokemon gc = new GameCornerPokemon(); + gc.offsets = offs; + current.staticPokemonGameCorner.add(gc); + } else { + int offs = parseRIInt(r[1]); + GameCornerPokemon gc = new GameCornerPokemon(); + gc.offsets = new int[] { offs }; + current.staticPokemonGameCorner.add(gc); + } + } else if (r[0].equals("TMText[]")) { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] parts = r[1].substring(1, + r[1].length() - 1).split(",", 3); + TMTextEntry tte = new TMTextEntry(); + tte.number = parseRIInt(parts[0]); + tte.offset = parseRIInt(parts[1]); + tte.template = parts[2]; + current.tmTexts.add(tte); + } + } else if (r[0].equals("Game")) { + current.romName = r[1]; + } else if (r[0].equals("Version")) { + current.version = parseRIInt(r[1]); + } else if (r[0].equals("NonJapanese")) { + current.nonJapanese = parseRIInt(r[1]); + } else if (r[0].equals("Type")) { + if (r[1].equalsIgnoreCase("Yellow")) { + current.isYellow = true; + } else { + current.isYellow = false; + } + } else if (r[0].equals("ExtraTableFile")) { + current.extraTableFile = r[1]; + } else if (r[0].equals("CRCInHeader")) { + current.crcInHeader = parseRIInt(r[1]); + } else if (r[0].endsWith("Tweak")) { + current.codeTweaks.put(r[0], r[1]); + } else if (r[0].equals("ExtraTypes")) { + // remove the containers + r[1] = r[1].substring(1, r[1].length() - 1); + String[] parts = r[1].split(","); + for (String part : parts) { + String[] iParts = part.split("="); + int typeId = Integer.parseInt(iParts[0], 16); + String typeName = iParts[1].trim(); + Type theType = Type.valueOf(typeName); + current.extraTypeLookup.put(typeId, theType); + current.extraTypeReverse.put(theType, typeId); + } + } else if (r[0].equals("CopyFrom")) { + for (RomEntry otherEntry : roms) { + if (r[1].equalsIgnoreCase(otherEntry.name)) { + // copy from here + boolean cSP = (current + .getValue("CopyStaticPokemon") == 1); + boolean cTT = (current + .getValue("CopyTMText") == 1); + current.arrayEntries + .putAll(otherEntry.arrayEntries); + current.entries.putAll(otherEntry.entries); + if (cSP) { + current.staticPokemonSingle + .addAll(otherEntry.staticPokemonSingle); + current.staticPokemonGameCorner + .addAll(otherEntry.staticPokemonGameCorner); + current.entries.put( + "StaticPokemonSupport", 1); + } else { + current.entries.put( + "StaticPokemonSupport", 0); + } + if (cTT) { + current.tmTexts + .addAll(otherEntry.tmTexts); + } + current.extraTableFile = otherEntry.extraTableFile; + } + } + } else { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + if (offsets.length == 1 + && offsets[0].trim().isEmpty()) { + current.arrayEntries.put(r[0], new int[0]); + } else { + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + if (r[0].startsWith("StaticPokemon")) { + for (int off : offs) { + current.staticPokemonSingle + .add(off); + } + } else { + current.arrayEntries.put(r[0], offs); + } + } + + } else { + int offs = parseRIInt(r[1]); + current.entries.put(r[0], offs); + } + } + } + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + private static void setupAllowedItems() { + allowedItems = new ItemList(250); // 251-255 are junk TMs + // Assorted key items & junk + // 23/01/2014: ban fake PP Up + allowedItems.banSingles(5, 6, 7, 8, 9, 31, 48, 50, 59, 63, 64); + allowedItems.banRange(21, 8); + allowedItems.banRange(41, 5); + allowedItems.banRange(69, 10); + // Unused + allowedItems.banRange(84, 112); + // HMs + allowedItems.banRange(196, 5); + // Real TMs + allowedItems.tmRange(201, 50); + } + + private static int parseRIInt(String off) { + int radix = 10; + off = off.trim().toLowerCase(); + if (off.startsWith("0x") || off.startsWith("&h")) { + radix = 16; + off = off.substring(2); + } + try { + return Integer.parseInt(off, radix); + } catch (NumberFormatException ex) { + System.err.println("invalid base " + radix + "number " + off); + return 0; + } + } + + // This ROM's data + private Pokemon[] pokes; + private List pokemonList; + private RomEntry romEntry; + private Move[] moves; + private String[] tb; + private Map d; + private int longestTableToken; + private String[] itemNames; + private String[] mapNames; + private SubMap[] maps; + private boolean xAccNerfed; + + @Override + public boolean detectRom(byte[] rom) { + if (rom.length < 524288 || rom.length > 2097152) { + return false; // size check + } + return checkRomEntry(rom) != null; // so it's OK if it's a valid ROM + } + + @Override + public void loadedRom() { + romEntry = checkRomEntry(this.rom); + pokeNumToRBYTable = new int[256]; + pokeRBYToNumTable = new int[256]; + moveNumToRomTable = new int[256]; + moveRomToNumTable = new int[256]; + tb = new String[256]; + d = new HashMap(); + maps = new SubMap[256]; + xAccNerfed = false; + clearTextTables(); + readTextTable("gameboy_jap"); + if (romEntry.extraTableFile != null + && romEntry.extraTableFile.equalsIgnoreCase("none") == false) { + readTextTable(romEntry.extraTableFile); + } + loadPokedexOrder(); + loadPokemonStats(); + pokemonList = Arrays.asList(pokes); + loadMoves(); + preloadMaps(); + loadItemNames(); + loadMapNames(); + } + + private void loadPokedexOrder() { + int pkmnCount = romEntry.getValue("InternalPokemonCount"); + int orderOffset = romEntry.getValue("PokedexOrder"); + pokedexCount = 0; + for (int i = 1; i <= pkmnCount; i++) { + int pokedexNum = rom[orderOffset + i - 1] & 0xFF; + pokeRBYToNumTable[i] = pokedexNum; + if (pokedexNum != 0 && pokeNumToRBYTable[pokedexNum] == 0) { + pokeNumToRBYTable[pokedexNum] = i; + } + pokedexCount = Math.max(pokedexCount, pokedexNum); + } + } + + private RomEntry checkRomEntry(byte[] rom) { + int version = rom[0x14C] & 0xFF; + int nonjap = rom[0x14A] & 0xFF; + // Check for specific CRC first + int crcInHeader = ((rom[0x14E] & 0xFF) << 8) | (rom[0x14F] & 0xFF); + for (RomEntry re : roms) { + if (romSig(rom, re.romName) && re.version == version + && re.nonJapanese == nonjap + && re.crcInHeader == crcInHeader) { + return re; + } + } + // Now check for non-specific-CRC entries + for (RomEntry re : roms) { + if (romSig(rom, re.romName) && re.version == version + && re.nonJapanese == nonjap && re.crcInHeader == -1) { + return re; + } + } + // Not found + return null; + } + + private void clearTextTables() { + tb = new String[256]; + d.clear(); + longestTableToken = 0; + } + + private void readTextTable(String name) { + try { + Scanner sc = new Scanner(FileFunctions.openConfig(name + ".tbl"), + "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine(); + if (!q.trim().isEmpty()) { + String[] r = q.split("=", 2); + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + int hexcode = Integer.parseInt(r[0], 16); + if (tb[hexcode] != null) { + String oldMatch = tb[hexcode]; + tb[hexcode] = null; + if (d.get(oldMatch) == hexcode) { + d.remove(oldMatch); + } + } + tb[hexcode] = r[1]; + longestTableToken = Math.max(longestTableToken, + r[1].length()); + d.put(r[1], (byte) hexcode); + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + @Override + public void savingRom() { + savePokemonStats(); + saveMoves(); + } + + private String[] readMoveNames() { + int moveCount = romEntry.getValue("MoveCount"); + int offset = romEntry.getValue("MoveNamesOffset"); + String[] moveNames = new String[moveCount + 1]; + for (int i = 1; i <= moveCount; i++) { + moveNames[i] = readVariableLengthString(offset); + offset += lengthOfStringAt(offset) + 1; + } + return moveNames; + } + + private void loadMoves() { + String[] moveNames = readMoveNames(); + int moveCount = romEntry.getValue("MoveCount"); + int movesOffset = romEntry.getValue("MoveDataOffset"); + // check real move count + int trueMoveCount = 0; + for (int i = 1; i <= moveCount; i++) { + // temp hack for Brown + if (rom[movesOffset + (i - 1) * 6] != 0 + && moveNames[i].equals("Nothing") == false) { + trueMoveCount++; + } + } + moves = new Move[trueMoveCount + 1]; + int trueMoveIndex = 0; + + for (int i = 1; i <= moveCount; i++) { + int anim = rom[movesOffset + (i - 1) * 6] & 0xFF; + // another temp hack for brown + if (anim > 0 && moveNames[i].equals("Nothing") == false) { + trueMoveIndex++; + moveNumToRomTable[trueMoveIndex] = i; + moveRomToNumTable[i] = trueMoveIndex; + moves[trueMoveIndex] = new Move(); + moves[trueMoveIndex].name = moveNames[i]; + moves[trueMoveIndex].internalId = i; + moves[trueMoveIndex].number = trueMoveIndex; + moves[trueMoveIndex].effectIndex = rom[movesOffset + (i - 1) + * 6 + 1] & 0xFF; + moves[trueMoveIndex].hitratio = ((rom[movesOffset + (i - 1) * 6 + + 4] & 0xFF) + 0) / 255.0 * 100; + moves[trueMoveIndex].power = rom[movesOffset + (i - 1) * 6 + 2] & 0xFF; + moves[trueMoveIndex].pp = rom[movesOffset + (i - 1) * 6 + 5] & 0xFF; + moves[trueMoveIndex].type = idToType(rom[movesOffset + (i - 1) + * 6 + 3] & 0xFF); + } + } + + } + + private void saveMoves() { + int movesOffset = romEntry.getValue("MoveDataOffset"); + for (Move m : moves) { + if (m != null) { + int i = m.internalId; + rom[movesOffset + (i - 1) * 6 + 1] = (byte) m.effectIndex; + rom[movesOffset + (i - 1) * 6 + 2] = (byte) m.power; + rom[movesOffset + (i - 1) * 6 + 3] = typeToByte(m.type); + int hitratio = (int) Math.round(m.hitratio * 2.55); + if (hitratio < 0) { + hitratio = 0; + } + if (hitratio > 255) { + hitratio = 255; + } + rom[movesOffset + (i - 1) * 6 + 4] = (byte) hitratio; + rom[movesOffset + (i - 1) * 6 + 5] = (byte) m.pp; + } + } + } + + public List getMoves() { + return Arrays.asList(moves); + } + + private void loadPokemonStats() { + pokes = new Pokemon[pokedexCount + 1]; + // Fetch our names + String[] pokeNames = readPokemonNames(); + // Get base stats + int pokeStatsOffset = romEntry.getValue("PokemonStatsOffset"); + for (int i = 1; i <= pokedexCount; i++) { + pokes[i] = new Pokemon(); + pokes[i].number = i; + loadBasicPokeStats(pokes[i], pokeStatsOffset + (i - 1) * 0x1C); + // Name? + pokes[i].name = pokeNames[pokeNumToRBYTable[i]]; + } + + // Mew override for R/B + if (!romEntry.isYellow) { + loadBasicPokeStats(pokes[151], romEntry.getValue("MewStatsOffset")); + } + + } + + private void savePokemonStats() { + // Write pokemon names + int offs = romEntry.getValue("PokemonNamesOffset"); + int nameLength = romEntry.getValue("PokemonNamesLength"); + for (int i = 1; i <= pokedexCount; i++) { + int rbynum = pokeNumToRBYTable[i]; + int stringOffset = offs + (rbynum - 1) * nameLength; + writeFixedLengthString(pokes[i].name, stringOffset, nameLength); + } + // Write pokemon stats + int pokeStatsOffset = romEntry.getValue("PokemonStatsOffset"); + for (int i = 1; i <= pokedexCount; i++) { + if (i == 151) { + continue; + } + saveBasicPokeStats(pokes[i], pokeStatsOffset + (i - 1) * 0x1C); + } + // Write MEW + int mewOffset = romEntry.isYellow ? pokeStatsOffset + 150 * 0x1C + : romEntry.getValue("MewStatsOffset"); + saveBasicPokeStats(pokes[151], mewOffset); + } + + private void loadBasicPokeStats(Pokemon pkmn, int offset) { + pkmn.hp = rom[offset + 1] & 0xFF; + pkmn.attack = rom[offset + 2] & 0xFF; + pkmn.defense = rom[offset + 3] & 0xFF; + pkmn.speed = rom[offset + 4] & 0xFF; + pkmn.special = rom[offset + 5] & 0xFF; + pkmn.spatk = pkmn.special; + pkmn.spdef = pkmn.special; + // Type + pkmn.primaryType = idToType(rom[offset + 6] & 0xFF); + pkmn.secondaryType = idToType(rom[offset + 7] & 0xFF); + // Only one type? + if (pkmn.secondaryType == pkmn.primaryType) { + pkmn.secondaryType = null; + } + + pkmn.catchRate = rom[offset + 8] & 0xFF; + pkmn.growthCurve = ExpCurve.fromByte(rom[offset + 19]); + + pkmn.guaranteedHeldItem = -1; + pkmn.commonHeldItem = -1; + pkmn.rareHeldItem = -1; + pkmn.darkGrassHeldItem = -1; + } + + private void saveBasicPokeStats(Pokemon pkmn, int offset) { + rom[offset + 1] = (byte) pkmn.hp; + rom[offset + 2] = (byte) pkmn.attack; + rom[offset + 3] = (byte) pkmn.defense; + rom[offset + 4] = (byte) pkmn.speed; + rom[offset + 5] = (byte) pkmn.special; + rom[offset + 6] = typeToByte(pkmn.primaryType); + if (pkmn.secondaryType == null) { + rom[offset + 7] = rom[offset + 6]; + } else { + rom[offset + 7] = typeToByte(pkmn.secondaryType); + } + rom[offset + 8] = (byte) pkmn.catchRate; + rom[offset + 19] = pkmn.growthCurve.toByte(); + } + + private String[] readPokemonNames() { + int offs = romEntry.getValue("PokemonNamesOffset"); + int nameLength = romEntry.getValue("PokemonNamesLength"); + int pkmnCount = romEntry.getValue("InternalPokemonCount"); + String[] names = new String[pkmnCount + 1]; + for (int i = 1; i <= pkmnCount; i++) { + names[i] = readFixedLengthString(offs + (i - 1) * nameLength, + nameLength); + } + return names; + } + + private String readString(int offset, int maxLength) { + StringBuilder string = new StringBuilder(); + for (int c = 0; c < maxLength; c++) { + int currChar = rom[offset + c] & 0xFF; + if (tb[currChar] != null) { + string.append(tb[currChar]); + } else { + if (currChar == 0x50 || currChar == 0x00) { + break; + } else { + string.append("\\x" + String.format("%02X", currChar)); + } + } + } + return string.toString(); + } + + public byte[] translateString(String text) { + List data = new ArrayList(); + while (text.length() != 0) { + int i = Math.max(0, longestTableToken - text.length()); + if (text.charAt(0) == '\\' && text.charAt(1) == 'x') { + data.add((byte) Integer.parseInt(text.substring(2, 4), 16)); + text = text.substring(4); + } else { + while (!(d + .containsKey(text.substring(0, longestTableToken - i)) || (i == longestTableToken))) { + i++; + } + if (i == longestTableToken) { + text = text.substring(1); + } else { + data.add(d.get(text.substring(0, longestTableToken - i))); + text = text.substring(longestTableToken - i); + } + } + } + byte[] ret = new byte[data.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = data.get(i); + } + return ret; + } + + private String readFixedLengthString(int offset, int length) { + return readString(offset, length); + } + + private void writeFixedLengthString(String str, int offset, int length) { + byte[] translated = translateString(str); + int len = Math.min(translated.length, length); + System.arraycopy(translated, 0, rom, offset, len); + while (len < length) { + rom[offset + len] = 0x50; + len++; + } + } + + private int makeGBPointer(int offset) { + if (offset < 0x4000) { + return offset; + } else { + return (offset % 0x4000) + 0x4000; + } + } + + private int bankOf(int offset) { + return (offset / 0x4000); + } + + private int calculateOffset(int bank, int pointer) { + if (pointer < 0x4000) { + return pointer; + } else { + return (pointer % 0x4000) + bank * 0x4000; + } + } + + public String readVariableLengthString(int offset) { + return readString(offset, Integer.MAX_VALUE); + } + + public byte[] traduire(String str) { + return translateString(str); + } + + public String readVariableLengthScriptString(int offset) { + return readString(offset, Integer.MAX_VALUE); + } + + private void writeFixedLengthScriptString(String str, int offset, int length) { + byte[] translated = translateString(str); + int len = Math.min(translated.length, length); + System.arraycopy(translated, 0, rom, offset, len); + while (len < length) { + rom[offset + len] = 0x00; + len++; + } + } + + private int lengthOfStringAt(int offset) { + int len = 0; + while (rom[offset + len] != 0x50 && rom[offset + len] != 0x00) { + len++; + } + return len; + } + + private boolean romSig(byte[] rom, String sig) { + try { + int sigOffset = 0x134; + byte[] sigBytes = sig.getBytes("US-ASCII"); + for (int i = 0; i < sigBytes.length; i++) { + if (rom[sigOffset + i] != sigBytes[i]) { + return false; + } + } + return true; + } catch (UnsupportedEncodingException ex) { + return false; + } + + } + + @Override + public boolean isInGame(Pokemon pkmn) { + return (pkmn.number >= 1 && pkmn.number <= pokedexCount); + } + + @Override + public boolean isInGame(int pokemonNumber) { + return (pokemonNumber >= 1 && pokemonNumber <= pokedexCount); + } + + @Override + public List getStarters() { + // Get the starters + List starters = new ArrayList(); + starters.add(pokes[pokeRBYToNumTable[rom[romEntry.arrayEntries + .get("StarterOffsets1")[0]] & 0xFF]]); + starters.add(pokes[pokeRBYToNumTable[rom[romEntry.arrayEntries + .get("StarterOffsets2")[0]] & 0xFF]]); + if (!romEntry.isYellow) { + starters.add(pokes[pokeRBYToNumTable[rom[romEntry.arrayEntries + .get("StarterOffsets3")[0]] & 0xFF]]); + } + return starters; + } + + @Override + public boolean setStarters(List newStarters) { + // Amount? + int starterAmount = 2; + if (!romEntry.isYellow) { + starterAmount = 3; + } + + // Basic checks + if (newStarters.size() != starterAmount) { + return false; + } + + for (Pokemon pkmn : newStarters) { + if (!isInGame(pkmn)) { + return false; + } + } + + // Patch starter bytes + for (int i = 0; i < starterAmount; i++) { + byte starter = (byte) pokeNumToRBYTable[newStarters.get(i).number]; + int[] offsets = romEntry.arrayEntries.get("StarterOffsets" + + (i + 1)); + for (int offset : offsets) { + rom[offset] = starter; + } + } + + // Special stuff for non-Yellow only + + if (!romEntry.isYellow) { + + // Starter text + if (romEntry.getValue("CanChangeStarterText") > 0) { + List starterTextOffsets = RomFunctions.search(rom, + traduire("So! You want the")); + for (int i = 0; i < 3 && i < starterTextOffsets.size(); i++) { + writeFixedLengthScriptString("So! You want\\n" + + newStarters.get(i).name + "?\\e", + starterTextOffsets.get(i), + lengthOfStringAt(starterTextOffsets.get(i)) + 1); + } + } + + // Patch starter pokedex routine? + // Can only do in 1M roms because of size concerns + if (romEntry.getValue("PatchPokedex") > 0) { + + // Starter pokedex required RAM values + // RAM offset => value + // Allows for multiple starters in the same RAM byte + Map onValues = new TreeMap(); + for (int i = 0; i < 3; i++) { + int pkDexNum = newStarters.get(i).number; + int ramOffset = (pkDexNum - 1) / 8 + + romEntry.getValue("PokedexRamOffset"); + int bitShift = (pkDexNum - 1) % 8; + int writeValue = 1 << bitShift; + if (onValues.containsKey(ramOffset)) { + onValues.put(ramOffset, onValues.get(ramOffset) + | writeValue); + } else { + onValues.put(ramOffset, writeValue); + } + } + + // Starter pokedex offset/pointer calculations + + int pkDexOnOffset = romEntry.getValue("StarterPokedexOnOffset"); + int pkDexOffOffset = romEntry + .getValue("StarterPokedexOffOffset"); + + int sizeForOnRoutine = 5 * onValues.size() + 3; + int writeOnRoutineTo = romEntry + .getValue("StarterPokedexBranchOffset"); + int writeOffRoutineTo = writeOnRoutineTo + sizeForOnRoutine; + int offsetForOnRoutine = makeGBPointer(writeOnRoutineTo); + int offsetForOffRoutine = makeGBPointer(writeOffRoutineTo); + int retOnOffset = makeGBPointer(pkDexOnOffset + 5); + int retOffOffset = makeGBPointer(pkDexOffOffset + 4); + + // Starter pokedex + // Branch to our new routine(s) + + // Turn bytes on + rom[pkDexOnOffset] = (byte) 0xC3; + writeWord(pkDexOnOffset + 1, offsetForOnRoutine); + rom[pkDexOnOffset + 3] = 0x00; + rom[pkDexOnOffset + 4] = 0x00; + + // Turn bytes off + rom[pkDexOffOffset] = (byte) 0xC3; + writeWord(pkDexOffOffset + 1, offsetForOffRoutine); + rom[pkDexOffOffset + 3] = 0x00; + + // Put together the two scripts + rom[writeOffRoutineTo] = (byte) 0xAF; + int turnOnOffset = writeOnRoutineTo; + int turnOffOffset = writeOffRoutineTo + 1; + for (int ramOffset : onValues.keySet()) { + int onValue = onValues.get(ramOffset); + // Turn on code + rom[turnOnOffset++] = 0x3E; + rom[turnOnOffset++] = (byte) onValue; + // Turn on code for ram writing + rom[turnOnOffset++] = (byte) 0xEA; + rom[turnOnOffset++] = (byte) (ramOffset % 0x100); + rom[turnOnOffset++] = (byte) (ramOffset / 0x100); + // Turn off code for ram writing + rom[turnOffOffset++] = (byte) 0xEA; + rom[turnOffOffset++] = (byte) (ramOffset % 0x100); + rom[turnOffOffset++] = (byte) (ramOffset / 0x100); + } + // Jump back + rom[turnOnOffset++] = (byte) 0xC3; + writeWord(turnOnOffset, retOnOffset); + + rom[turnOffOffset++] = (byte) 0xC3; + writeWord(turnOffOffset, retOffOffset); + } + + } + + return true; + + } + + @Override + public List getStarterHeldItems() { + // do nothing + return new ArrayList(); + } + + @Override + public void setStarterHeldItems(List items) { + // do nothing + } + + @Override + public void shufflePokemonStats() { + for (int i = 1; i <= pokedexCount; i++) { + pokes[i].shuffleStats(); + } + } + + @Override + public List getEncounters(boolean useTimeOfDay) { + List encounters = new ArrayList(); + + // grass & water + List usedOffsets = new ArrayList(); + int tableOffset = romEntry.getValue("WildPokemonTableOffset"); + int tableBank = bankOf(tableOffset); + int mapID = -1; + + while (readWord(tableOffset) != 0xFFFF) { + mapID++; + int offset = calculateOffset(tableBank, readWord(tableOffset)); + int rootOffset = offset; + if (!usedOffsets.contains(offset)) { + usedOffsets.add(offset); + // grass and water are exactly the same + for (int a = 0; a < 2; a++) { + int rate = rom[offset++] & 0xFF; + if (rate > 0) { + // there is data here + EncounterSet thisSet = new EncounterSet(); + thisSet.rate = rate; + thisSet.offset = rootOffset; + thisSet.displayName = (a == 1 ? "Surfing" + : "Grass/Cave") + " on " + mapNames[mapID]; + for (int slot = 0; slot < 10; slot++) { + Encounter enc = new Encounter(); + enc.level = rom[offset] & 0xFF; + enc.pokemon = pokes[pokeRBYToNumTable[rom[offset + 1] & 0xFF]]; + thisSet.encounters.add(enc); + offset += 2; + } + encounters.add(thisSet); + } + } + } else { + for (EncounterSet es : encounters) { + if (es.offset == offset) { + es.displayName += ", " + mapNames[mapID]; + } + } + } + tableOffset += 2; + } + + // old rod + int oldRodOffset = romEntry.getValue("OldRodOffset"); + EncounterSet oldRodSet = new EncounterSet(); + oldRodSet.displayName = "Old Rod Fishing"; + Encounter oldRodEnc = new Encounter(); + oldRodEnc.level = rom[oldRodOffset + 2] & 0xFF; + oldRodEnc.pokemon = pokes[pokeRBYToNumTable[rom[oldRodOffset + 1] & 0xFF]]; + oldRodSet.encounters.add(oldRodEnc); + encounters.add(oldRodSet); + + // good rod + int goodRodOffset = romEntry.getValue("GoodRodOffset"); + EncounterSet goodRodSet = new EncounterSet(); + goodRodSet.displayName = "Good Rod Fishing"; + for (int grSlot = 0; grSlot < 2; grSlot++) { + Encounter enc = new Encounter(); + enc.level = rom[goodRodOffset + grSlot * 2] & 0xFF; + enc.pokemon = pokes[pokeRBYToNumTable[rom[goodRodOffset + grSlot + * 2 + 1] & 0xFF]]; + goodRodSet.encounters.add(enc); + } + encounters.add(goodRodSet); + + // super rod + if (romEntry.isYellow) { + int superRodOffset = romEntry.getValue("SuperRodTableOffset"); + while ((rom[superRodOffset] & 0xFF) != 0xFF) { + int map = rom[superRodOffset++] & 0xFF; + EncounterSet thisSet = new EncounterSet(); + thisSet.displayName = "Super Rod Fishing on " + mapNames[map]; + for (int encN = 0; encN < 4; encN++) { + Encounter enc = new Encounter(); + enc.level = rom[superRodOffset + 1] & 0xFF; + enc.pokemon = pokes[pokeRBYToNumTable[rom[superRodOffset] & 0xFF]]; + thisSet.encounters.add(enc); + superRodOffset += 2; + } + encounters.add(thisSet); + } + } else { + // red/blue + int superRodOffset = romEntry.getValue("SuperRodTableOffset"); + int superRodBank = bankOf(superRodOffset); + List usedSROffsets = new ArrayList(); + while ((rom[superRodOffset] & 0xFF) != 0xFF) { + int map = rom[superRodOffset++] & 0xFF; + int setOffset = calculateOffset(superRodBank, + readWord(superRodOffset)); + superRodOffset += 2; + if (!usedSROffsets.contains(setOffset)) { + usedSROffsets.add(setOffset); + EncounterSet thisSet = new EncounterSet(); + thisSet.displayName = "Super Rod Fishing on " + + mapNames[map]; + thisSet.offset = setOffset; + int pokesInSet = rom[setOffset++] & 0xFF; + for (int encN = 0; encN < pokesInSet; encN++) { + Encounter enc = new Encounter(); + enc.level = rom[setOffset] & 0xFF; + enc.pokemon = pokes[pokeRBYToNumTable[rom[setOffset + 1] & 0xFF]]; + thisSet.encounters.add(enc); + setOffset += 2; + } + encounters.add(thisSet); + } else { + for (EncounterSet es : encounters) { + if (es.offset == setOffset) { + es.displayName += ", " + mapNames[map]; + } + } + } + } + } + + return encounters; + } + + @Override + public void setEncounters(boolean useTimeOfDay, + List encounters) { + Iterator encsetit = encounters.iterator(); + + // grass & water + List usedOffsets = new ArrayList(); + int tableOffset = romEntry.getValue("WildPokemonTableOffset"); + int tableBank = bankOf(tableOffset); + + while (readWord(tableOffset) != 0xFFFF) { + int offset = calculateOffset(tableBank, readWord(tableOffset)); + if (!usedOffsets.contains(offset)) { + usedOffsets.add(offset); + // grass and water are exactly the same + for (int a = 0; a < 2; a++) { + int rate = rom[offset++] & 0xFF; + if (rate > 0) { + // there is data here + EncounterSet thisSet = encsetit.next(); + for (int slot = 0; slot < 10; slot++) { + Encounter enc = thisSet.encounters.get(slot); + rom[offset] = (byte) enc.level; + rom[offset + 1] = (byte) pokeNumToRBYTable[enc.pokemon.number]; + offset += 2; + } + } + } + } + tableOffset += 2; + } + + // old rod + int oldRodOffset = romEntry.getValue("OldRodOffset"); + EncounterSet oldRodSet = encsetit.next(); + Encounter oldRodEnc = oldRodSet.encounters.get(0); + rom[oldRodOffset + 2] = (byte) oldRodEnc.level; + rom[oldRodOffset + 1] = (byte) pokeNumToRBYTable[oldRodEnc.pokemon.number]; + + // good rod + int goodRodOffset = romEntry.getValue("GoodRodOffset"); + EncounterSet goodRodSet = encsetit.next(); + for (int grSlot = 0; grSlot < 2; grSlot++) { + Encounter enc = goodRodSet.encounters.get(grSlot); + rom[goodRodOffset + grSlot * 2] = (byte) enc.level; + rom[goodRodOffset + grSlot * 2 + 1] = (byte) pokeNumToRBYTable[enc.pokemon.number]; + } + + // super rod + if (romEntry.isYellow) { + int superRodOffset = romEntry.getValue("SuperRodTableOffset"); + while ((rom[superRodOffset] & 0xFF) != 0xFF) { + superRodOffset++; + EncounterSet thisSet = encsetit.next(); + for (int encN = 0; encN < 4; encN++) { + Encounter enc = thisSet.encounters.get(encN); + rom[superRodOffset + 1] = (byte) enc.level; + rom[superRodOffset] = (byte) pokeNumToRBYTable[enc.pokemon.number]; + superRodOffset += 2; + } + } + } else { + // red/blue + int superRodOffset = romEntry.getValue("SuperRodTableOffset"); + int superRodBank = bankOf(superRodOffset); + List usedSROffsets = new ArrayList(); + while ((rom[superRodOffset] & 0xFF) != 0xFF) { + superRodOffset++; + int setOffset = calculateOffset(superRodBank, + readWord(superRodOffset)); + superRodOffset += 2; + if (!usedSROffsets.contains(setOffset)) { + usedSROffsets.add(setOffset); + int pokesInSet = rom[setOffset++] & 0xFF; + EncounterSet thisSet = encsetit.next(); + for (int encN = 0; encN < pokesInSet; encN++) { + Encounter enc = thisSet.encounters.get(encN); + rom[setOffset] = (byte) enc.level; + rom[setOffset + 1] = (byte) pokeNumToRBYTable[enc.pokemon.number]; + setOffset += 2; + } + } + } + } + } + + @Override + public List getPokemon() { + return pokemonList; + } + + public List getTrainers() { + int traineroffset = romEntry.getValue("TrainerDataTableOffset"); + int traineramount = 47; + int[] trainerclasslimits = romEntry.arrayEntries + .get("TrainerDataClassCounts"); + + int[] pointers = new int[traineramount + 1]; + for (int i = 1; i <= traineramount; i++) { + int tPointer = readWord(traineroffset + (i - 1) * 2); + pointers[i] = calculateOffset(bankOf(traineroffset), tPointer); + } + + List tcnames = getTrainerClassesForText(); + + List allTrainers = new ArrayList(); + for (int i = 1; i <= traineramount; i++) { + int offs = pointers[i]; + int limit = trainerclasslimits[i]; + String tcname = tcnames.get(i - 1); + for (int trnum = 0; trnum < limit; trnum++) { + Trainer tr = new Trainer(); + tr.offset = offs; + tr.trainerclass = i; + tr.fullDisplayName = tcname; + int dataType = rom[offs] & 0xFF; + if (dataType == 0xFF) { + // "Special" trainer + tr.poketype = 1; + offs++; + while (rom[offs] != 0x0) { + TrainerPokemon tpk = new TrainerPokemon(); + tpk.level = rom[offs] & 0xFF; + tpk.pokemon = pokes[pokeRBYToNumTable[rom[offs + 1] & 0xFF]]; + tr.pokemon.add(tpk); + offs += 2; + } + } else { + tr.poketype = 0; + int fixedLevel = dataType; + offs++; + while (rom[offs] != 0x0) { + TrainerPokemon tpk = new TrainerPokemon(); + tpk.level = fixedLevel; + tpk.pokemon = pokes[pokeRBYToNumTable[rom[offs] & 0xFF]]; + tr.pokemon.add(tpk); + offs++; + } + } + offs++; + allTrainers.add(tr); + } + } + tagTrainersUniversal(allTrainers); + if (romEntry.isYellow) { + tagTrainersYellow(allTrainers); + } else { + tagTrainersRB(allTrainers); + } + return allTrainers; + } + + private void tagTrainersUniversal(List trs) { + // Gym Leaders + tbc(trs, 34, 0, "GYM1"); + tbc(trs, 35, 0, "GYM2"); + tbc(trs, 36, 0, "GYM3"); + tbc(trs, 37, 0, "GYM4"); + tbc(trs, 38, 0, "GYM5"); + tbc(trs, 40, 0, "GYM6"); + tbc(trs, 39, 0, "GYM7"); + tbc(trs, 29, 2, "GYM8"); + + // Other giovanni teams + tbc(trs, 29, 0, "GIO1"); + tbc(trs, 29, 1, "GIO2"); + + // Elite 4 + tbc(trs, 44, 0, "ELITE1"); + tbc(trs, 33, 0, "ELITE2"); + tbc(trs, 46, 0, "ELITE3"); + tbc(trs, 47, 0, "ELITE4"); + } + + private void tagTrainersRB(List trs) { + // Gary Battles + tbc(trs, 25, 0, "RIVAL1-0"); + tbc(trs, 25, 1, "RIVAL1-1"); + tbc(trs, 25, 2, "RIVAL1-2"); + + tbc(trs, 25, 3, "RIVAL2-0"); + tbc(trs, 25, 4, "RIVAL2-1"); + tbc(trs, 25, 5, "RIVAL2-2"); + + tbc(trs, 25, 6, "RIVAL3-0"); + tbc(trs, 25, 7, "RIVAL3-1"); + tbc(trs, 25, 8, "RIVAL3-2"); + + tbc(trs, 42, 0, "RIVAL4-0"); + tbc(trs, 42, 1, "RIVAL4-1"); + tbc(trs, 42, 2, "RIVAL4-2"); + + tbc(trs, 42, 3, "RIVAL5-0"); + tbc(trs, 42, 4, "RIVAL5-1"); + tbc(trs, 42, 5, "RIVAL5-2"); + + tbc(trs, 42, 6, "RIVAL6-0"); + tbc(trs, 42, 7, "RIVAL6-1"); + tbc(trs, 42, 8, "RIVAL6-2"); + + tbc(trs, 42, 9, "RIVAL7-0"); + tbc(trs, 42, 10, "RIVAL7-1"); + tbc(trs, 42, 11, "RIVAL7-2"); + + tbc(trs, 43, 0, "RIVAL8-0"); + tbc(trs, 43, 1, "RIVAL8-1"); + tbc(trs, 43, 2, "RIVAL8-2"); + + // Gym Trainers + tbc(trs, 5, 0, "GYM1"); + + tbc(trs, 15, 0, "GYM2"); + tbc(trs, 6, 0, "GYM2"); + + tbc(trs, 4, 7, "GYM3"); + tbc(trs, 20, 0, "GYM3"); + tbc(trs, 41, 2, "GYM3"); + + tbc(trs, 3, 16, "GYM4"); + tbc(trs, 3, 17, "GYM4"); + tbc(trs, 6, 10, "GYM4"); + tbc(trs, 18, 0, "GYM4"); + tbc(trs, 18, 1, "GYM4"); + tbc(trs, 18, 2, "GYM4"); + tbc(trs, 32, 0, "GYM4"); + + tbc(trs, 21, 2, "GYM5"); + tbc(trs, 21, 3, "GYM5"); + tbc(trs, 21, 6, "GYM5"); + tbc(trs, 21, 7, "GYM5"); + tbc(trs, 22, 0, "GYM5"); + tbc(trs, 22, 1, "GYM5"); + + tbc(trs, 19, 0, "GYM6"); + tbc(trs, 19, 1, "GYM6"); + tbc(trs, 19, 2, "GYM6"); + tbc(trs, 19, 3, "GYM6"); + tbc(trs, 45, 21, "GYM6"); + tbc(trs, 45, 22, "GYM6"); + tbc(trs, 45, 23, "GYM6"); + + tbc(trs, 8, 8, "GYM7"); + tbc(trs, 8, 9, "GYM7"); + tbc(trs, 8, 10, "GYM7"); + tbc(trs, 8, 11, "GYM7"); + tbc(trs, 11, 3, "GYM7"); + tbc(trs, 11, 4, "GYM7"); + tbc(trs, 11, 5, "GYM7"); + + tbc(trs, 22, 2, "GYM8"); + tbc(trs, 22, 3, "GYM8"); + tbc(trs, 24, 5, "GYM8"); + tbc(trs, 24, 6, "GYM8"); + tbc(trs, 24, 7, "GYM8"); + tbc(trs, 31, 0, "GYM8"); + tbc(trs, 31, 8, "GYM8"); + tbc(trs, 31, 9, "GYM8"); + } + + private void tagTrainersYellow(List trs) { + // Rival Battles + tbc(trs, 25, 0, "IRIVAL"); + + tbc(trs, 25, 1, "RIVAL1-0"); + + tbc(trs, 25, 2, "RIVAL2-0"); + + tbc(trs, 42, 0, "RIVAL3-0"); + + tbc(trs, 42, 1, "RIVAL4-0"); + tbc(trs, 42, 2, "RIVAL4-1"); + tbc(trs, 42, 3, "RIVAL4-2"); + + tbc(trs, 42, 4, "RIVAL5-0"); + tbc(trs, 42, 5, "RIVAL5-1"); + tbc(trs, 42, 6, "RIVAL5-2"); + + tbc(trs, 42, 7, "RIVAL6-0"); + tbc(trs, 42, 8, "RIVAL6-1"); + tbc(trs, 42, 9, "RIVAL6-2"); + + tbc(trs, 43, 0, "RIVAL7-0"); + tbc(trs, 43, 1, "RIVAL7-1"); + tbc(trs, 43, 2, "RIVAL7-2"); + + // Rocket Jessie & James + tbc(trs, 30, 41, "THEMED:JESSIE&JAMES"); + tbc(trs, 30, 42, "THEMED:JESSIE&JAMES"); + tbc(trs, 30, 43, "THEMED:JESSIE&JAMES"); + tbc(trs, 30, 44, "THEMED:JESSIE&JAMES"); + + // Gym Trainers + tbc(trs, 5, 0, "GYM1"); + + tbc(trs, 6, 0, "GYM2"); + tbc(trs, 15, 0, "GYM2"); + + tbc(trs, 4, 7, "GYM3"); + tbc(trs, 20, 0, "GYM3"); + tbc(trs, 41, 2, "GYM3"); + + tbc(trs, 3, 16, "GYM4"); + tbc(trs, 3, 17, "GYM4"); + tbc(trs, 6, 10, "GYM4"); + tbc(trs, 18, 0, "GYM4"); + tbc(trs, 18, 1, "GYM4"); + tbc(trs, 18, 2, "GYM4"); + tbc(trs, 32, 0, "GYM4"); + + tbc(trs, 21, 2, "GYM5"); + tbc(trs, 21, 3, "GYM5"); + tbc(trs, 21, 6, "GYM5"); + tbc(trs, 21, 7, "GYM5"); + tbc(trs, 22, 0, "GYM5"); + tbc(trs, 22, 1, "GYM5"); + + tbc(trs, 19, 0, "GYM6"); + tbc(trs, 19, 1, "GYM6"); + tbc(trs, 19, 2, "GYM6"); + tbc(trs, 19, 3, "GYM6"); + tbc(trs, 45, 21, "GYM6"); + tbc(trs, 45, 22, "GYM6"); + tbc(trs, 45, 23, "GYM6"); + + tbc(trs, 8, 8, "GYM7"); + tbc(trs, 8, 9, "GYM7"); + tbc(trs, 8, 10, "GYM7"); + tbc(trs, 8, 11, "GYM7"); + tbc(trs, 11, 3, "GYM7"); + tbc(trs, 11, 4, "GYM7"); + tbc(trs, 11, 5, "GYM7"); + + tbc(trs, 22, 2, "GYM8"); + tbc(trs, 22, 3, "GYM8"); + tbc(trs, 24, 5, "GYM8"); + tbc(trs, 24, 6, "GYM8"); + tbc(trs, 24, 7, "GYM8"); + tbc(trs, 31, 0, "GYM8"); + tbc(trs, 31, 8, "GYM8"); + tbc(trs, 31, 9, "GYM8"); + } + + public void setTrainers(List trainerData) { + int traineroffset = romEntry.getValue("TrainerDataTableOffset"); + int traineramount = 47; + int[] trainerclasslimits = romEntry.arrayEntries + .get("TrainerDataClassCounts"); + + int[] pointers = new int[traineramount + 1]; + for (int i = 1; i <= traineramount; i++) { + int tPointer = readWord(traineroffset + (i - 1) * 2); + pointers[i] = calculateOffset(bankOf(traineroffset), tPointer); + } + + Iterator allTrainers = trainerData.iterator(); + for (int i = 1; i <= traineramount; i++) { + int offs = pointers[i]; + int limit = trainerclasslimits[i]; + for (int trnum = 0; trnum < limit; trnum++) { + Trainer tr = allTrainers.next(); + if (tr.trainerclass != i) { + System.err.println("Trainer mismatch: " + tr.name); + } + Iterator tPokes = tr.pokemon.iterator(); + // Write their pokemon based on poketype + if (tr.poketype == 0) { + // Regular trainer + int fixedLevel = tr.pokemon.get(0).level; + rom[offs] = (byte) fixedLevel; + offs++; + while (tPokes.hasNext()) { + TrainerPokemon tpk = tPokes.next(); + rom[offs] = (byte) pokeNumToRBYTable[tpk.pokemon.number]; + offs++; + } + } else { + // Special trainer + rom[offs] = (byte) 0xFF; + offs++; + while (tPokes.hasNext()) { + TrainerPokemon tpk = tPokes.next(); + rom[offs] = (byte) tpk.level; + rom[offs + 1] = (byte) pokeNumToRBYTable[tpk.pokemon.number]; + offs += 2; + } + } + rom[offs] = 0; + offs++; + } + } + + // Custom Moves AI Table + // Zero it out entirely. + rom[romEntry.getValue("ExtraTrainerMovesTableOffset")] = (byte) 0xFF; + + // Champion Rival overrides in Red/Blue + if (!isYellow()) { + // hacky relative offset (very likely to work but maybe not always) + int champRivalJump = romEntry.getValue("GymLeaderMovesTableOffset") - 0x44; + // nop out this jump + rom[champRivalJump] = 0x00; + rom[champRivalJump + 1] = 0x00; + } + + } + + private void tbc(List allTrainers, int classNum, int number, + String tag) { + int currnum = -1; + for (Trainer t : allTrainers) { + if (t.trainerclass == classNum) { + currnum++; + if (currnum == number) { + t.tag = tag; + return; + } + } + } + } + + @Override + public boolean isYellow() { + return romEntry.isYellow; + } + + @Override + public boolean typeInGame(Type type) { + if (type.isHackOnly == false + && (type != Type.DARK && type != Type.STEEL)) { + return true; + } + if (romEntry.extraTypeReverse.containsKey(type)) { + return true; + } + return false; + } + + @Override + public void fixTypeEffectiveness() { + int base = romEntry.getValue("TypeEffectivenessOffset"); + log("--Fixing Type Effectiveness--"); + // Change Poison SE to bug (should be neutral) + // to Ice NE to Fire (is currently neutral) + log("Replaced: Poison super effective vs Bug => Ice not very effective vs Fire"); + rom[base + 135] = typeToByte(Type.ICE); + rom[base + 136] = typeToByte(Type.FIRE); + rom[base + 137] = 5; // Not very effective + // Change BUG SE to Poison to Bug NE to Poison + log("Changed: Bug super effective vs Poison => Bug not very effective vs Poison"); + rom[base + 203] = 5; // Not very effective + // Change Ghost 0E to Psychic to Ghost SE to Psychic + log("Changed: Psychic immune to Ghost => Ghost super effective vs Psychic"); + rom[base + 227] = 20; // Super effective + logBlankLine(); + } + + @Override + public Map> getMovesLearnt() { + Map> movesets = new TreeMap>(); + int pointersOffset = romEntry.getValue("PokemonMovesetsTableOffset"); + int pokeStatsOffset = romEntry.getValue("PokemonStatsOffset"); + int pkmnCount = romEntry.getValue("InternalPokemonCount"); + for (int i = 1; i <= pkmnCount; i++) { + int pointer = readWord(pointersOffset + (i - 1) * 2); + int realPointer = calculateOffset(bankOf(pointersOffset), pointer); + if (pokeRBYToNumTable[i] != 0) { + Pokemon pkmn = pokes[pokeRBYToNumTable[i]]; + int statsOffset = 0; + if (pokeRBYToNumTable[i] == 151 && !romEntry.isYellow) { + // Mewww + statsOffset = romEntry.getValue("MewStatsOffset"); + } else { + statsOffset = (pokeRBYToNumTable[i] - 1) * 0x1C + + pokeStatsOffset; + } + List ourMoves = new ArrayList(); + for (int delta = 0x0F; delta < 0x13; delta++) { + if (rom[statsOffset + delta] != 0x00) { + MoveLearnt learnt = new MoveLearnt(); + learnt.level = 1; + learnt.move = moveRomToNumTable[rom[statsOffset + delta] & 0xFF]; + ourMoves.add(learnt); + } + } + // Skip over evolution data + while (rom[realPointer] != 0) { + if (rom[realPointer] == 1) { + realPointer += 3; + } else if (rom[realPointer] == 2) { + realPointer += 4; + } else if (rom[realPointer] == 3) { + realPointer += 3; + } + } + realPointer++; + while (rom[realPointer] != 0) { + MoveLearnt learnt = new MoveLearnt(); + learnt.level = rom[realPointer] & 0xFF; + learnt.move = moveRomToNumTable[rom[realPointer + 1] & 0xFF]; + ourMoves.add(learnt); + realPointer += 2; + } + movesets.put(pkmn, ourMoves); + } + } + return movesets; + } + + @Override + public void setMovesLearnt(Map> movesets) { + int pointersOffset = romEntry.getValue("PokemonMovesetsTableOffset"); + int pokeStatsOffset = romEntry.getValue("PokemonStatsOffset"); + int pkmnCount = romEntry.getValue("InternalPokemonCount"); + for (int i = 1; i <= pkmnCount; i++) { + int pointer = readWord(pointersOffset + (i - 1) * 2); + int realPointer = calculateOffset(bankOf(pointersOffset), pointer); + if (pokeRBYToNumTable[i] != 0) { + Pokemon pkmn = pokes[pokeRBYToNumTable[i]]; + List ourMoves = movesets.get(pkmn); + int statsOffset = 0; + if (pokeRBYToNumTable[i] == 151 && !romEntry.isYellow) { + // Mewww + statsOffset = romEntry.getValue("MewStatsOffset"); + } else { + statsOffset = (pokeRBYToNumTable[i] - 1) * 0x1C + + pokeStatsOffset; + } + int movenum = 0; + while (movenum < 4 && ourMoves.size() > movenum + && ourMoves.get(movenum).level == 1) { + rom[statsOffset + 0x0F + movenum] = (byte) moveNumToRomTable[ourMoves + .get(movenum).move]; + movenum++; + } + // Write out the rest of zeroes + for (int mn = movenum; mn < 4; mn++) { + rom[statsOffset + 0x0F + mn] = 0; + } + // Skip over evolution data + while (rom[realPointer] != 0) { + if (rom[realPointer] == 1) { + realPointer += 3; + } else if (rom[realPointer] == 2) { + realPointer += 4; + } else if (rom[realPointer] == 3) { + realPointer += 3; + } + } + realPointer++; + while (rom[realPointer] != 0 && movenum < ourMoves.size()) { + rom[realPointer] = (byte) ourMoves.get(movenum).level; + rom[realPointer + 1] = (byte) moveNumToRomTable[ourMoves + .get(movenum).move]; + realPointer += 2; + movenum++; + } + // Make sure we finish off the moveset + rom[realPointer] = 0; + } + } + } + + @Override + public List getStaticPokemon() { + List statics = new ArrayList(); + if (romEntry.getValue("StaticPokemonSupport") > 0) { + for (int offset : romEntry.staticPokemonSingle) { + statics.add(pokes[pokeRBYToNumTable[rom[offset] & 0xFF]]); + } + for (GameCornerPokemon gcp : romEntry.staticPokemonGameCorner) { + statics.add(pokes[pokeRBYToNumTable[rom[gcp.offsets[0]] & 0xFF]]); + } + } + return statics; + } + + @Override + public boolean setStaticPokemon(List staticPokemon) { + if (romEntry.getValue("StaticPokemonSupport") == 0) { + return false; + } + // Checks + int singleSize = romEntry.staticPokemonSingle.size(); + int gcSize = romEntry.staticPokemonGameCorner.size(); + if (staticPokemon.size() != singleSize + gcSize) { + return false; + } + for (Pokemon pkmn : staticPokemon) { + if (!isInGame(pkmn)) { + return false; + } + } + + // Singular entries + for (int i = 0; i < singleSize; i++) { + rom[romEntry.staticPokemonSingle.get(i)] = (byte) pokeNumToRBYTable[staticPokemon + .get(i).number]; + } + + // Game corner + for (int i = 0; i < gcSize; i++) { + byte pokeNum = (byte) pokeNumToRBYTable[staticPokemon.get(i + + singleSize).number]; + int[] offsets = romEntry.staticPokemonGameCorner.get(i).offsets; + for (int offset : offsets) { + rom[offset] = pokeNum; + } + } + return true; + } + + @Override + public boolean canChangeStaticPokemon() { + return (romEntry.getValue("StaticPokemonSupport") > 0); + } + + @Override + public List getTMMoves() { + List tms = new ArrayList(); + int offset = romEntry.getValue("TMMovesOffset"); + for (int i = 1; i <= 50; i++) { + tms.add(moveRomToNumTable[rom[offset + (i - 1)] & 0xFF]); + } + return tms; + } + + @Override + public List getHMMoves() { + List hms = new ArrayList(); + int offset = romEntry.getValue("TMMovesOffset"); + for (int i = 1; i <= 5; i++) { + hms.add(moveRomToNumTable[rom[offset + 50 + (i - 1)] & 0xFF]); + } + return hms; + } + + @Override + public void setTMMoves(List moveIndexes) { + int offset = romEntry.getValue("TMMovesOffset"); + for (int i = 1; i <= 50; i++) { + rom[offset + (i - 1)] = (byte) moveNumToRomTable[moveIndexes + .get(i - 1)]; + } + + // Gym Leader TM Moves (RB only) + if (!romEntry.isYellow) { + int[] tms = new int[] { 34, 11, 24, 21, 6, 46, 38, 27 }; + int glMovesOffset = romEntry.getValue("GymLeaderMovesTableOffset"); + for (int i = 0; i < tms.length; i++) { + // Set the special move used by gym (i+1) to + // the move we just wrote to TM tms[i] + rom[glMovesOffset + i * 2] = (byte) moveNumToRomTable[moveIndexes + .get(tms[i] - 1)]; + } + } + + // TM Text + String[] moveNames = readMoveNames(); + for (TMTextEntry tte : romEntry.tmTexts) { + String moveName = moveNames[moveNumToRomTable[moveIndexes + .get(tte.number - 1)]]; + String text = tte.template.replace("%m", moveName); + writeFixedLengthScriptString(text, tte.offset, + lengthOfStringAt(tte.offset)); + } + } + + @Override + public int getTMCount() { + return 50; + } + + @Override + public int getHMCount() { + return 5; + } + + @Override + public Map getTMHMCompatibility() { + Map compat = new TreeMap(); + int pokeStatsOffset = romEntry.getValue("PokemonStatsOffset"); + for (int i = 1; i <= pokedexCount; i++) { + int baseStatsOffset = (romEntry.isYellow || i != 151) ? (pokeStatsOffset + (i - 1) * 0x1C) + : romEntry.getValue("MewStatsOffset"); + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[56]; + for (int j = 0; j < 7; j++) { + readByteIntoFlags(flags, j * 8 + 1, baseStatsOffset + 0x14 + j); + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setTMHMCompatibility(Map compatData) { + int pokeStatsOffset = romEntry.getValue("PokemonStatsOffset"); + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + int baseStatsOffset = (romEntry.isYellow || pkmn.number != 151) ? (pokeStatsOffset + (pkmn.number - 1) * 0x1C) + : romEntry.getValue("MewStatsOffset"); + for (int j = 0; j < 7; j++) { + rom[baseStatsOffset + 0x14 + j] = getByteFromFlags(flags, + j * 8 + 1); + } + } + } + + @Override + public boolean hasMoveTutors() { + return false; + } + + @Override + public List getMoveTutorMoves() { + return new ArrayList(); + } + + @Override + public void setMoveTutorMoves(List moves) { + // Do nothing + } + + @Override + public Map getMoveTutorCompatibility() { + return new TreeMap(); + } + + @Override + public void setMoveTutorCompatibility(Map compatData) { + // Do nothing + } + + @Override + public String getROMName() { + return "Pokemon " + romEntry.name; + } + + @Override + public String getROMCode() { + return romEntry.romName + " (" + romEntry.version + "/" + + romEntry.nonJapanese + ")"; + } + + @Override + public String getSupportLevel() { + return (romEntry.getValue("StaticPokemonSupport") > 0) ? "Complete" + : "No Static Pokemon"; + } + + @Override + public List getEvolutions() { + List evos = new ArrayList(); + int pointersOffset = romEntry.getValue("PokemonMovesetsTableOffset"); + List evosForThisPoke = new ArrayList(); + int pkmnCount = romEntry.getValue("InternalPokemonCount"); + for (int i = 1; i <= pkmnCount; i++) { + int pointer = readWord(pointersOffset + (i - 1) * 2); + int realPointer = calculateOffset(bankOf(pointersOffset), pointer); + if (pokeRBYToNumTable[i] != 0) { + evosForThisPoke.clear(); + int thisPoke = pokeRBYToNumTable[i]; + while (rom[realPointer] != 0) { + int method = rom[realPointer]; + EvolutionType type = EvolutionType.fromIndex(1, method); + int otherPoke = pokeRBYToNumTable[rom[realPointer + 2 + + (type == EvolutionType.STONE ? 1 : 0)] & 0xFF]; + int extraInfo = rom[realPointer + 1] & 0xFF; + Evolution evo = new Evolution(thisPoke, otherPoke, true, + type, extraInfo); + if (!evos.contains(evo)) { + evos.add(evo); + evosForThisPoke.add(evo); + } + realPointer += (type == EvolutionType.STONE ? 4 : 3); + } + // split evos don't carry stats + if (evosForThisPoke.size() > 1) { + for (Evolution e : evosForThisPoke) { + e.carryStats = false; + } + } + } + } + return evos; + } + + @Override + public void removeTradeEvolutions(boolean changeMoveEvos) { + // Gen 1: evolution data is right before moveset data + // So use those pointers + // no move evos, so no need to check for those + int pointersOffset = romEntry.getValue("PokemonMovesetsTableOffset"); + int pkmnCount = romEntry.getValue("InternalPokemonCount"); + log("--Removing Trade Evolutions--"); + for (int i = 1; i <= pkmnCount; i++) { + int pointer = readWord(pointersOffset + (i - 1) * 2); + int realPointer = calculateOffset(bankOf(pointersOffset), pointer); + if (pokeRBYToNumTable[i] != 0) { + // Evolution data + // All the 4 trade evos (Abra, Geodude, Gastly, Machop) + // evolve at around 25 + // So make this "3rd stage" to 37 + while (rom[realPointer] != 0) { + if (rom[realPointer] == 1) { + realPointer += 3; + } else if (rom[realPointer] == 2) { + realPointer += 4; + } else if (rom[realPointer] == 3) { + int otherPoke = pokeRBYToNumTable[rom[realPointer + 2] & 0xFF]; + // Trade evo + rom[realPointer] = 1; + rom[realPointer + 1] = 37; + logEvoChangeLevel(pokes[pokeRBYToNumTable[i]].name, + pokes[otherPoke].name, 37); + realPointer += 3; + } + } + } + } + logBlankLine(); + } + + private static final int[] tclassesCounts = new int[] { 21, 47 }; + private static final List singularTrainers = Arrays.asList(28, 32, + 33, 34, 35, 36, 37, 38, 39, 43, 45, 46); + + private List getTrainerClassesForText() { + int[] offsets = romEntry.arrayEntries.get("TrainerClassNamesOffsets"); + List tcNames = new ArrayList(); + int offset = offsets[offsets.length - 1]; + for (int j = 0; j < tclassesCounts[1]; j++) { + String name = readVariableLengthString(offset); + offset += (internalStringLength(name) + 1); + tcNames.add(name); + } + return tcNames; + } + + @Override + public List getTrainerNames() { + int[] offsets = romEntry.arrayEntries.get("TrainerClassNamesOffsets"); + List trainerNames = new ArrayList(); + int offset = offsets[offsets.length - 1]; + for (int j = 0; j < tclassesCounts[1]; j++) { + String name = readVariableLengthString(offset); + offset += (internalStringLength(name) + 1); + if (singularTrainers.contains(j)) { + trainerNames.add(name); + } + } + return trainerNames; + } + + @Override + public void setTrainerNames(List trainerNames) { + if (romEntry.getValue("CanChangeTrainerText") > 0) { + int[] offsets = romEntry.arrayEntries + .get("TrainerClassNamesOffsets"); + Iterator trainerNamesI = trainerNames.iterator(); + int offset = offsets[offsets.length - 1]; + for (int j = 0; j < tclassesCounts[1]; j++) { + String name = readVariableLengthString(offset); + if (singularTrainers.contains(j)) { + String newName = trainerNamesI.next(); + writeFixedLengthString(newName, offset, + internalStringLength(name) + 1); + } + offset += (internalStringLength(name) + 1); + } + } + } + + @Override + public TrainerNameMode trainerNameMode() { + return TrainerNameMode.SAME_LENGTH; + } + + @Override + public List getTCNameLengthsByTrainer() { + // not needed + return new ArrayList(); + } + + @Override + public List getTrainerClassNames() { + int[] offsets = romEntry.arrayEntries.get("TrainerClassNamesOffsets"); + List trainerClassNames = new ArrayList(); + if (offsets.length == 2) { + for (int i = 0; i < offsets.length; i++) { + int offset = offsets[i]; + for (int j = 0; j < tclassesCounts[i]; j++) { + String name = readVariableLengthString(offset); + offset += (internalStringLength(name) + 1); + if (i == 0 || !singularTrainers.contains(j)) { + trainerClassNames.add(name); + } + } + } + } else { + int offset = offsets[0]; + for (int j = 0; j < tclassesCounts[1]; j++) { + String name = readVariableLengthString(offset); + offset += (internalStringLength(name) + 1); + if (!singularTrainers.contains(j)) { + trainerClassNames.add(name); + } + } + } + return trainerClassNames; + } + + @Override + public void setTrainerClassNames(List trainerClassNames) { + if (romEntry.getValue("CanChangeTrainerText") > 0) { + int[] offsets = romEntry.arrayEntries + .get("TrainerClassNamesOffsets"); + Iterator tcNamesIter = trainerClassNames.iterator(); + if (offsets.length == 2) { + for (int i = 0; i < offsets.length; i++) { + int offset = offsets[i]; + for (int j = 0; j < tclassesCounts[i]; j++) { + String name = readVariableLengthString(offset); + if (i == 0 || !singularTrainers.contains(j)) { + String newName = tcNamesIter.next(); + writeFixedLengthString(newName, offset, + internalStringLength(name) + 1); + } + offset += (internalStringLength(name) + 1); + } + } + } else { + int offset = offsets[0]; + for (int j = 0; j < tclassesCounts[1]; j++) { + String name = readVariableLengthString(offset); + if (!singularTrainers.contains(j)) { + String newName = tcNamesIter.next(); + writeFixedLengthString(newName, offset, + internalStringLength(name) + 1); + } + offset += (internalStringLength(name) + 1); + } + } + } + + } + + @Override + public boolean fixedTrainerClassNamesLength() { + return true; + } + + @Override + public String getDefaultExtension() { + if (((rom[0x143] & 0xFF) & 0x80) > 0) { + return "gbc"; + } + return "sgb"; + } + + @Override + public int abilitiesPerPokemon() { + return 0; + } + + @Override + public int highestAbilityIndex() { + return 0; + } + + @Override + public int internalStringLength(String string) { + return translateString(string).length; + } + + @Override + public int codeTweaksAvailable() { + int available = 0; + if (romEntry.codeTweaks.get("BWXPTweak") != null) { + available |= CodeTweaks.BW_EXP_PATCH; + } + if (romEntry.codeTweaks.get("XAccNerfTweak") != null) { + available |= CodeTweaks.NERF_X_ACCURACY; + } + if (romEntry.codeTweaks.get("CritRateTweak") != null) { + available |= CodeTweaks.FIX_CRIT_RATE; + } + return available; + } + + @Override + public void applyBWEXPPatch() { + genericIPSPatch("BWXPTweak"); + } + + @Override + public void applyXAccNerfPatch() { + xAccNerfed = genericIPSPatch("XAccNerfTweak"); + } + + @Override + public void applyCritRatePatch() { + genericIPSPatch("CritRateTweak"); + } + + private boolean genericIPSPatch(String ctName) { + String patchName = romEntry.codeTweaks.get(ctName); + if (patchName == null) { + return false; + } + + try { + FileFunctions.applyPatch(rom, patchName); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public List getGameBreakingMoves() { + // Sonicboom & drage & OHKO moves + // 160 add spore + // also remove OHKO if xacc nerfed + if (xAccNerfed) { + return Arrays.asList(49, 82, 147); + } else { + return Arrays.asList(49, 82, 32, 90, 12, 147); + } + } + + @Override + public void applySignature() { + // First off, intro Pokemon + // 160 add yellow intro random + int introPokemon = pokeNumToRBYTable[this.randomPokemon().number]; + rom[romEntry.getValue("IntroPokemonOffset")] = (byte) introPokemon; + rom[romEntry.getValue("IntroCryOffset")] = (byte) introPokemon; + + } + + @Override + public ItemList getAllowedItems() { + return allowedItems; + } + + private void loadItemNames() { + itemNames = new String[256]; + itemNames[0] = "glitch"; + // trying to emulate pretty much what the game does here + // normal items + int origOffset = romEntry.getValue("ItemNamesOffset"); + int itemNameOffset = origOffset; + for (int index = 1; index <= 0x100; index++) { + if (itemNameOffset / 0x4000 > origOffset / 0x4000) { + // the game would continue making its merry way into VRAM here, + // but we don't have VRAM to simulate. + // just give up. + break; + } + int startOfText = itemNameOffset; + while ((rom[itemNameOffset] & 0xFF) != 0x50) { + itemNameOffset++; + } + itemNameOffset++; + itemNames[index % 256] = readFixedLengthString(startOfText, 20); + } + // hms override + for (int index = 0xC4; index < 0xC9; index++) { + itemNames[index] = String.format("HM%02d", index - 0xC3); + } + // tms override + for (int index = 0xC9; index < 0x100; index++) { + itemNames[index] = String.format("TM%02d", index - 0xC8); + } + } + + @Override + public String[] getItemNames() { + return itemNames; + } + + private static class SubMap { + private int id; + private int addr; + private int bank; + private MapHeader header; + private Connection[] cons; + private int n_cons; + private int obj_addr; + private List itemOffsets; + } + + private static class MapHeader { + private int tileset_id; // u8 + private int map_h, map_w; // u8 + private int map_ptr, text_ptr, script_ptr; // u16 + private int connect_byte; // u8 + // 10 bytes + } + + private static class Connection { + private int index; // u8 + private int connected_map; // u16 + private int current_map; // u16 + private int bigness; // u8 + private int map_width; // u8 + private int y_align; // u8 + private int x_align; // u8 + private int window; // u16 + // 11 bytes + } + + private void preloadMaps() { + int mapBanks = romEntry.getValue("MapBanks"); + int mapAddresses = romEntry.getValue("MapAddresses"); + + preloadMap(mapBanks, mapAddresses, 0); + } + + private void preloadMap(int mapBanks, int mapAddresses, int mapID) { + + if (maps[mapID] != null || mapID == 0xED || mapID == 0xFF) { + return; + } + + SubMap map = new SubMap(); + maps[mapID] = map; + + map.id = mapID; + map.addr = calculateOffset(rom[mapBanks + mapID] & 0xFF, + readWord(mapAddresses + mapID * 2)); + map.bank = bankOf(map.addr); + + map.header = new MapHeader(); + map.header.tileset_id = rom[map.addr] & 0xFF; + map.header.map_h = rom[map.addr + 1] & 0xFF; + map.header.map_w = rom[map.addr + 2] & 0xFF; + map.header.map_ptr = calculateOffset(map.bank, readWord(map.addr + 3)); + map.header.text_ptr = calculateOffset(map.bank, readWord(map.addr + 5)); + map.header.script_ptr = calculateOffset(map.bank, + readWord(map.addr + 7)); + map.header.connect_byte = rom[map.addr + 9] & 0xFF; + + int cb = map.header.connect_byte; + map.n_cons = ((cb & 8) >> 3) + ((cb & 4) >> 2) + ((cb & 2) >> 1) + + (cb & 1); + + int cons_offset = map.addr + 10; + + map.cons = new Connection[map.n_cons]; + for (int i = 0; i < map.n_cons; i++) { + int tcon_offs = cons_offset + i * 11; + Connection con = new Connection(); + con.index = rom[tcon_offs] & 0xFF; + con.connected_map = readWord(tcon_offs + 1); + con.current_map = readWord(tcon_offs + 3); + con.bigness = rom[tcon_offs + 5] & 0xFF; + con.map_width = rom[tcon_offs + 6] & 0xFF; + con.y_align = rom[tcon_offs + 7] & 0xFF; + con.x_align = rom[tcon_offs + 8] & 0xFF; + con.window = readWord(tcon_offs + 9); + map.cons[i] = con; + preloadMap(mapBanks, mapAddresses, con.index); + } + map.obj_addr = calculateOffset(map.bank, readWord(cons_offset + + map.n_cons * 11)); + + // Read objects + // +0 is the border tile (ignore) + // +1 is warp count + + int n_warps = rom[map.obj_addr + 1] & 0xFF; + int offs = map.obj_addr + 2; + for (int i = 0; i < n_warps; i++) { + // track this warp + int to_map = rom[offs + 3] & 0xFF; + preloadMap(mapBanks, mapAddresses, to_map); + offs += 4; + } + + // Now we're pointing to sign count + int n_signs = rom[offs++] & 0xFF; + offs += n_signs * 3; + + // Finally, entities, which contain the items + map.itemOffsets = new ArrayList(); + int n_entities = rom[offs++] & 0xFF; + for (int i = 0; i < n_entities; i++) { + // Read text ID + int tid = rom[offs + 5] & 0xFF; + if ((tid & (1 << 6)) > 0) { + // trainer + offs += 8; + } else if ((tid & (1 << 7)) > 0 && (rom[offs + 6] != 0x00)) { + // item + map.itemOffsets.add(offs + 6); + offs += 7; + } else { + // generic + offs += 6; + } + } + } + + private void loadMapNames() { + mapNames = new String[256]; + int mapNameTableOffset = romEntry.getValue("MapNameTableOffset"); + int mapNameBank = bankOf(mapNameTableOffset); + // external names + List usedExternal = new ArrayList(); + for (int i = 0; i < 0x25; i++) { + int externalOffset = calculateOffset(mapNameBank, + readWord(mapNameTableOffset + 1)); + usedExternal.add(externalOffset); + mapNames[i] = readVariableLengthString(externalOffset); + mapNameTableOffset += 3; + } + + // internal names + int lastMaxMap = 0x25; + Map previousMapCounts = new HashMap(); + while ((rom[mapNameTableOffset] & 0xFF) != 0xFF) { + int maxMap = rom[mapNameTableOffset] & 0xFF; + int nameOffset = calculateOffset(mapNameBank, + readWord(mapNameTableOffset + 2)); + String actualName = readVariableLengthString(nameOffset).trim(); + if (usedExternal.contains(nameOffset)) { + for (int i = lastMaxMap; i < maxMap; i++) { + if (maps[i] != null) { + mapNames[i] = actualName + " (Building)"; + } + } + } else { + int mapCount = 0; + if (previousMapCounts.containsKey(nameOffset)) { + mapCount = previousMapCounts.get(nameOffset); + } + for (int i = lastMaxMap; i < maxMap; i++) { + if (maps[i] != null) { + mapCount++; + mapNames[i] = actualName + " (" + mapCount + ")"; + } + } + previousMapCounts.put(nameOffset, mapCount); + } + lastMaxMap = maxMap; + mapNameTableOffset += 4; + } + } + + private List getItemOffsets() { + + List itemOffs = new ArrayList(); + + for (int i = 0; i < maps.length; i++) { + if (maps[i] != null) { + itemOffs.addAll(maps[i].itemOffsets); + } + } + + int hiRoutine = romEntry.getValue("HiddenItemRoutine"); + int spclTable = romEntry.getValue("SpecialMapPointerTable"); + int spclBank = bankOf(spclTable); + + if (!isYellow()) { + + int spclList = romEntry.getValue("SpecialMapList"); + + int lOffs = spclList; + int idx = 0; + + while ((rom[lOffs] & 0xFF) != 0xFF) { + + int spclOffset = calculateOffset(spclBank, readWord(spclTable + + idx)); + + while ((rom[spclOffset] & 0xFF) != 0xFF) { + if (calculateOffset(rom[spclOffset + 3] & 0xFF, + readWord(spclOffset + 4)) == hiRoutine) { + itemOffs.add(spclOffset + 2); + } + spclOffset += 6; + } + lOffs++; + idx += 2; + } + } else { + + int lOffs = spclTable; + + while ((rom[lOffs] & 0xFF) != 0xFF) { + + int spclOffset = calculateOffset(spclBank, readWord(lOffs + 1)); + + while ((rom[spclOffset] & 0xFF) != 0xFF) { + if (calculateOffset(rom[spclOffset + 3] & 0xFF, + readWord(spclOffset + 4)) == hiRoutine) { + itemOffs.add(spclOffset + 2); + } + spclOffset += 6; + } + lOffs += 3; + } + } + + return itemOffs; + } + + @Override + public List getRequiredFieldTMs() { + return Arrays.asList(new Integer[] { 3, 4, 8, 10, 12, 14, 16, 19, 20, + 22, 25, 26, 30, 40, 43, 44, 45, 47 }); + } + + @Override + public List getCurrentFieldTMs() { + List itemOffsets = getItemOffsets(); + List fieldTMs = new ArrayList(); + + for (int offset : itemOffsets) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isTM(itemHere)) { + fieldTMs.add(itemHere - 200); // TM offset + } + } + return fieldTMs; + } + + @Override + public void setFieldTMs(List fieldTMs) { + List itemOffsets = getItemOffsets(); + Iterator iterTMs = fieldTMs.iterator(); + + for (int offset : itemOffsets) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isTM(itemHere)) { + // Replace this with a TM from the list + rom[offset] = (byte) (iterTMs.next() + 200); + } + } + } + + @Override + public List getRegularFieldItems() { + List itemOffsets = getItemOffsets(); + List fieldItems = new ArrayList(); + + for (int offset : itemOffsets) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isAllowed(itemHere) + && !(allowedItems.isTM(itemHere))) { + fieldItems.add(itemHere); + } + } + return fieldItems; + } + + @Override + public void setRegularFieldItems(List items) { + List itemOffsets = getItemOffsets(); + Iterator iterItems = items.iterator(); + + for (int offset : itemOffsets) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isAllowed(itemHere) + && !(allowedItems.isTM(itemHere))) { + // Replace it + rom[offset] = (byte) (iterItems.next().intValue()); + } + } + + } + + @Override + public List getIngameTrades() { + List trades = new ArrayList(); + + // info + int tableOffset = romEntry.getValue("TradeTableOffset"); + int tableSize = romEntry.getValue("TradeTableSize"); + int nicknameLength = romEntry.getValue("TradeNameLength"); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + int entryLength = nicknameLength + 3; + + for (int entry = 0; entry < tableSize; entry++) { + if (unusedOffset < unused.length && unused[unusedOffset] == entry) { + unusedOffset++; + continue; + } + IngameTrade trade = new IngameTrade(); + int entryOffset = tableOffset + entry * entryLength; + trade.requestedPokemon = pokes[pokeRBYToNumTable[rom[entryOffset] & 0xFF]]; + trade.givenPokemon = pokes[pokeRBYToNumTable[rom[entryOffset + 1] & 0xFF]]; + trade.nickname = readString(entryOffset + 3, nicknameLength); + trades.add(trade); + } + + return trades; + } + + @Override + public void setIngameTrades(List trades) { + + // info + int tableOffset = romEntry.getValue("TradeTableOffset"); + int tableSize = romEntry.getValue("TradeTableSize"); + int nicknameLength = romEntry.getValue("TradeNameLength"); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + int entryLength = nicknameLength + 3; + int tradeOffset = 0; + + for (int entry = 0; entry < tableSize; entry++) { + if (unusedOffset < unused.length && unused[unusedOffset] == entry) { + unusedOffset++; + continue; + } + IngameTrade trade = trades.get(tradeOffset++); + int entryOffset = tableOffset + entry * entryLength; + rom[entryOffset] = (byte) pokeNumToRBYTable[trade.requestedPokemon.number]; + rom[entryOffset + 1] = (byte) pokeNumToRBYTable[trade.givenPokemon.number]; + if (romEntry.getValue("CanChangeTrainerText") > 0) { + writeFixedLengthString(trade.nickname, entryOffset + 3, + nicknameLength); + } + } + } + + @Override + public boolean hasDVs() { + return true; + } + + @Override + public int generationOfPokemon() { + return 1; + } + + @Override + public void removeEvosForPokemonPool() { + // gen1 doesn't have this functionality anyway + } + + @Override + public boolean supportsFourStartingMoves() { + return true; + } +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/Gen2RomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/Gen2RomHandler.java new file mode 100755 index 000000000..153f7fe7b --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/Gen2RomHandler.java @@ -0,0 +1,2517 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- Gen2RomHandler.java - randomizer handler for G/S/C. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.TreeMap; + +import com.dabomstew.pkrandom.CodeTweaks; +import com.dabomstew.pkrandom.FileFunctions; +import com.dabomstew.pkrandom.RandomSource; +import com.dabomstew.pkrandom.RomFunctions; +import com.dabomstew.pkrandom.pokemon.Encounter; +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.EvolutionType; +import com.dabomstew.pkrandom.pokemon.ExpCurve; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.ItemList; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.TrainerPokemon; +import com.dabomstew.pkrandom.pokemon.Type; + +public class Gen2RomHandler extends AbstractGBRomHandler { + + private static final Type[] typeTable = constructTypeTable(); + + private static Type[] constructTypeTable() { + Type[] table = new Type[256]; + table[0x00] = Type.NORMAL; + table[0x01] = Type.FIGHTING; + table[0x02] = Type.FLYING; + table[0x03] = Type.POISON; + table[0x04] = Type.GROUND; + table[0x05] = Type.ROCK; + table[0x07] = Type.BUG; + table[0x08] = Type.GHOST; + table[0x09] = Type.STEEL; + table[0x14] = Type.FIRE; + table[0x15] = Type.WATER; + table[0x16] = Type.GRASS; + table[0x17] = Type.ELECTRIC; + table[0x18] = Type.PSYCHIC; + table[0x19] = Type.ICE; + table[0x1A] = Type.DRAGON; + table[0x1B] = Type.DARK; + return table; + } + + private static byte typeToByte(Type type) { + if (type == null) { + return 0x13; // ???-type + } + switch (type) { + case NORMAL: + return 0x00; + case FIGHTING: + return 0x01; + case FLYING: + return 0x02; + case POISON: + return 0x03; + case GROUND: + return 0x04; + case ROCK: + return 0x05; + case BUG: + return 0x07; + case GHOST: + return 0x08; + case FIRE: + return 0x14; + case WATER: + return 0x15; + case GRASS: + return 0x16; + case ELECTRIC: + return 0x17; + case PSYCHIC: + return 0x18; + case ICE: + return 0x19; + case DRAGON: + return 0x1A; + case STEEL: + return 0x09; + case DARK: + return 0x1B; + default: + return 0; // normal by default + } + } + + private static class RomEntry { + private String name; + private String romCode; + private int version, nonJapanese; + private String extraTableFile; + private boolean isCrystal; + private int crcInHeader = -1; + private Map codeTweaks = new HashMap(); + private List tmTexts = new ArrayList(); + private Map entries = new HashMap(); + private Map arrayEntries = new HashMap(); + private List staticPokemonSingle = new ArrayList(); + private Map staticPokemonGameCorner = new TreeMap(); + private Map staticPokemonCopy = new TreeMap(); + + private int getValue(String key) { + if (!entries.containsKey(key)) { + entries.put(key, 0); + } + return entries.get(key); + } + } + + private static class TMTextEntry { + private int number; + private int offset; + private String template; + } + + private static List roms; + private static ItemList allowedItems; + + static { + loadROMInfo(); + setupAllowedItems(); + } + + private static void loadROMInfo() { + roms = new ArrayList(); + RomEntry current = null; + try { + Scanner sc = new Scanner( + FileFunctions.openConfig("gen2_offsets.ini"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine().trim(); + if (q.contains("//")) { + q = q.substring(0, q.indexOf("//")).trim(); + } + if (!q.isEmpty()) { + if (q.startsWith("[") && q.endsWith("]")) { + // New rom + current = new RomEntry(); + current.name = q.substring(1, q.length() - 1); + roms.add(current); + } else { + String[] r = q.split("=", 2); + if (r.length == 1) { + System.err.println("invalid entry " + q); + continue; + } + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + r[1] = r[1].trim(); + r[0] = r[0].trim(); + // Static Pokemon? + if (r[0].equals("StaticPokemonGameCorner[]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + if (offsets.length != 2) { + continue; + } + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + current.staticPokemonGameCorner.put(offs[0], + offs[1]); + } else if (r[0].equals("StaticPokemonCopy[]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + if (offsets.length != 2) { + continue; + } + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + current.staticPokemonCopy.put(offs[0], offs[1]); + } else if (r[0].equals("TMText[]")) { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] parts = r[1].substring(1, + r[1].length() - 1).split(",", 3); + TMTextEntry tte = new TMTextEntry(); + tte.number = parseRIInt(parts[0]); + tte.offset = parseRIInt(parts[1]); + tte.template = parts[2]; + current.tmTexts.add(tte); + } + } else if (r[0].equals("Game")) { + current.romCode = r[1]; + } else if (r[0].equals("Version")) { + current.version = parseRIInt(r[1]); + } else if (r[0].equals("NonJapanese")) { + current.nonJapanese = parseRIInt(r[1]); + } else if (r[0].equals("Type")) { + if (r[1].equalsIgnoreCase("Crystal")) { + current.isCrystal = true; + } else { + current.isCrystal = false; + } + } else if (r[0].equals("ExtraTableFile")) { + current.extraTableFile = r[1]; + } else if (r[0].equals("CRCInHeader")) { + current.crcInHeader = parseRIInt(r[1]); + } else if (r[0].endsWith("Tweak")) { + current.codeTweaks.put(r[0], r[1]); + } else if (r[0].equals("CopyFrom")) { + for (RomEntry otherEntry : roms) { + if (r[1].equalsIgnoreCase(otherEntry.name)) { + // copy from here + boolean cSP = (current + .getValue("CopyStaticPokemon") == 1); + boolean cTT = (current + .getValue("CopyTMText") == 1); + current.arrayEntries + .putAll(otherEntry.arrayEntries); + current.entries.putAll(otherEntry.entries); + if (cSP) { + current.staticPokemonSingle + .addAll(otherEntry.staticPokemonSingle); + current.staticPokemonGameCorner + .putAll(otherEntry.staticPokemonGameCorner); + current.staticPokemonCopy + .putAll(otherEntry.staticPokemonCopy); + current.entries.put( + "StaticPokemonSupport", 1); + } else { + current.entries.put( + "StaticPokemonSupport", 0); + } + if (cTT) { + current.tmTexts + .addAll(otherEntry.tmTexts); + } + current.extraTableFile = otherEntry.extraTableFile; + } + } + } else { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + if (offsets.length == 1 + && offsets[0].trim().isEmpty()) { + current.arrayEntries.put(r[0], new int[0]); + } else { + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + if (r[0].startsWith("StaticPokemon")) { + for (int off : offs) { + current.staticPokemonSingle + .add(off); + } + } else { + current.arrayEntries.put(r[0], offs); + } + } + } else { + int offs = parseRIInt(r[1]); + current.entries.put(r[0], offs); + } + } + } + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + private static void setupAllowedItems() { + allowedItems = new ItemList(249); // 250-255 are junk and cancel + // Assorted key items + allowedItems.banSingles(6, 54, 55, 58, 59, 61, 115, 116, 133, 134, 175, + 178); + allowedItems.banRange(66, 6); + allowedItems.banRange(127, 4); + // HMs + allowedItems.banRange(243, 7); + // Unused items (Teru-Samas and dummy TMs) + allowedItems.banSingles(7, 25, 45, 50, 56, 90, 100, 120, 135, 136, 137); + allowedItems.banSingles(141, 142, 145, 147, 148, 149, 153, 154, 155, + 162, 171); + allowedItems.banSingles(176, 179, 190, 220, 195); + // Real TMs + allowedItems.tmRange(191, 4); + allowedItems.tmRange(196, 24); + allowedItems.tmRange(221, 22); + } + + private static int parseRIInt(String off) { + int radix = 10; + off = off.trim().toLowerCase(); + if (off.startsWith("0x") || off.startsWith("&h")) { + radix = 16; + off = off.substring(2); + } + try { + return Integer.parseInt(off, radix); + } catch (NumberFormatException ex) { + System.err.println("invalid base " + radix + "number " + off); + return 0; + } + } + + // This ROM's data + private Pokemon[] pokes; + private List pokemonList; + private RomEntry romEntry; + private Move[] moves; + private String[] tb; + private Map d; + private int longestTableToken; + private boolean havePatchedFleeing; + private String[] itemNames; + private List itemOffs; + private String[][] mapNames; + private String[] landmarkNames; + private boolean isVietCrystal; + + @Override + public boolean detectRom(byte[] rom) { + if (rom.length > 2097152) { + return false; // size check + } + return checkRomEntry(rom) != null; // so it's OK if it's a valid ROM + } + + @Override + public void loadedRom() { + romEntry = checkRomEntry(this.rom); + tb = new String[256]; + d = new HashMap(); + clearTextTables(); + readTextTable("gameboy_jap"); + if (romEntry.extraTableFile != null + && romEntry.extraTableFile.equalsIgnoreCase("none") == false) { + readTextTable(romEntry.extraTableFile); + } + // VietCrystal override + if (romEntry.name.equals("Crystal (J)") && (rom[0x63] & 0xFF) == 0xF5) { + readTextTable("vietcrystal"); + isVietCrystal = true; + } else { + isVietCrystal = false; + } + havePatchedFleeing = false; + loadPokemonStats(); + pokemonList = Arrays.asList(pokes); + loadMoves(); + loadLandmarkNames(); + preprocessMaps(); + loadItemNames(); + } + + private RomEntry checkRomEntry(byte[] rom) { + int version = rom[0x14C] & 0xFF; + int nonjap = rom[0x14A] & 0xFF; + // Check for specific CRC first + int crcInHeader = ((rom[0x14E] & 0xFF) << 8) | (rom[0x14F] & 0xFF); + for (RomEntry re : roms) { + if (romSig(rom, re.romCode) && re.version == version + && re.nonJapanese == nonjap + && re.crcInHeader == crcInHeader) { + return re; + } + } + // Now check for non-specific-CRC entries + for (RomEntry re : roms) { + if (romSig(rom, re.romCode) && re.version == version + && re.nonJapanese == nonjap && re.crcInHeader == -1) { + return re; + } + } + // Not found + return null; + } + + private void clearTextTables() { + tb = new String[256]; + d.clear(); + longestTableToken = 0; + } + + private void readTextTable(String name) { + try { + Scanner sc = new Scanner(FileFunctions.openConfig(name + ".tbl"), + "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine(); + if (!q.trim().isEmpty()) { + String[] r = q.split("=", 2); + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + int hexcode = Integer.parseInt(r[0], 16); + if (tb[hexcode] != null) { + String oldMatch = tb[hexcode]; + tb[hexcode] = null; + d.remove(oldMatch); + } + tb[hexcode] = r[1]; + longestTableToken = Math.max(longestTableToken, + r[1].length()); + d.put(r[1], (byte) hexcode); + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + @Override + public void savingRom() { + savePokemonStats(); + saveMoves(); + } + + private void loadPokemonStats() { + pokes = new Pokemon[252]; + // Fetch our names + String[] pokeNames = readPokemonNames(); + int offs = romEntry.getValue("PokemonStatsOffset"); + // Get base stats + for (int i = 1; i <= 251; i++) { + pokes[i] = new Pokemon(); + pokes[i].number = i; + loadBasicPokeStats(pokes[i], offs + (i - 1) * 0x20); + // Name? + pokes[i].name = pokeNames[i]; + } + + } + + private void savePokemonStats() { + // Write pokemon names + int offs = romEntry.getValue("PokemonNamesOffset"); + int len = romEntry.getValue("PokemonNamesLength"); + for (int i = 1; i <= 251; i++) { + int stringOffset = offs + (i - 1) * len; + writeFixedLengthString(pokes[i].name, stringOffset, len); + } + // Write pokemon stats + int offs2 = romEntry.getValue("PokemonStatsOffset"); + for (int i = 1; i <= 251; i++) { + saveBasicPokeStats(pokes[i], offs2 + (i - 1) * 0x20); + } + } + + private String[] readMoveNames() { + int offset = romEntry.getValue("MoveNamesOffset"); + String[] moveNames = new String[252]; + for (int i = 1; i <= 251; i++) { + moveNames[i] = readVariableLengthString(offset); + offset += lengthOfStringAt(offset) + 1; + } + return moveNames; + } + + private void loadMoves() { + moves = new Move[252]; + String[] moveNames = readMoveNames(); + int offs = romEntry.getValue("MoveDataOffset"); + for (int i = 1; i <= 251; i++) { + moves[i] = new Move(); + moves[i].name = moveNames[i]; + moves[i].number = i; + moves[i].effectIndex = rom[offs + (i - 1) * 7] & 0xFF; + moves[i].hitratio = ((rom[offs + (i - 1) * 7 + 3] & 0xFF) + 0) / 255.0 * 100; + moves[i].power = rom[offs + (i - 1) * 7 + 1] & 0xFF; + moves[i].pp = rom[offs + (i - 1) * 7 + 4] & 0xFF; + moves[i].type = typeTable[rom[offs + (i - 1) * 7 + 2]]; + } + + } + + private void saveMoves() { + int offs = romEntry.getValue("MoveDataOffset"); + for (int i = 1; i <= 251; i++) { + rom[offs + (i - 1) * 7] = (byte) moves[i].effectIndex; + rom[offs + (i - 1) * 7 + 1] = (byte) moves[i].power; + rom[offs + (i - 1) * 7 + 2] = typeToByte(moves[i].type); + int hitratio = (int) Math.round(moves[i].hitratio * 2.55); + if (hitratio < 0) { + hitratio = 0; + } + if (hitratio > 255) { + hitratio = 255; + } + rom[offs + (i - 1) * 7 + 3] = (byte) hitratio; + rom[offs + (i - 1) * 7 + 4] = (byte) moves[i].pp; + } + } + + public List getMoves() { + return Arrays.asList(moves); + } + + private void loadBasicPokeStats(Pokemon pkmn, int offset) { + pkmn.hp = rom[offset + 1] & 0xFF; + pkmn.attack = rom[offset + 2] & 0xFF; + pkmn.defense = rom[offset + 3] & 0xFF; + pkmn.speed = rom[offset + 4] & 0xFF; + pkmn.spatk = rom[offset + 5] & 0xFF; + pkmn.spdef = rom[offset + 6] & 0xFF; + // Type + pkmn.primaryType = typeTable[rom[offset + 7] & 0xFF]; + pkmn.secondaryType = typeTable[rom[offset + 8] & 0xFF]; + // Only one type? + if (pkmn.secondaryType == pkmn.primaryType) { + pkmn.secondaryType = null; + } + pkmn.catchRate = rom[offset + 9] & 0xFF; + pkmn.guaranteedHeldItem = -1; + pkmn.commonHeldItem = rom[offset + 11] & 0xFF; + pkmn.rareHeldItem = rom[offset + 12] & 0xFF; + pkmn.darkGrassHeldItem = -1; + pkmn.growthCurve = ExpCurve.fromByte(rom[offset + 22]); + + } + + private void saveBasicPokeStats(Pokemon pkmn, int offset) { + rom[offset + 1] = (byte) pkmn.hp; + rom[offset + 2] = (byte) pkmn.attack; + rom[offset + 3] = (byte) pkmn.defense; + rom[offset + 4] = (byte) pkmn.speed; + rom[offset + 5] = (byte) pkmn.spatk; + rom[offset + 6] = (byte) pkmn.spdef; + rom[offset + 7] = typeToByte(pkmn.primaryType); + if (pkmn.secondaryType == null) { + rom[offset + 8] = rom[offset + 7]; + } else { + rom[offset + 8] = typeToByte(pkmn.secondaryType); + } + rom[offset + 9] = (byte) pkmn.catchRate; + + rom[offset + 11] = (byte) pkmn.commonHeldItem; + rom[offset + 12] = (byte) pkmn.rareHeldItem; + rom[offset + 22] = pkmn.growthCurve.toByte(); + } + + private String[] readPokemonNames() { + int offs = romEntry.getValue("PokemonNamesOffset"); + int len = romEntry.getValue("PokemonNamesLength"); + String[] names = new String[252]; + for (int i = 1; i <= 251; i++) { + names[i] = readFixedLengthString(offs + (i - 1) * len, len); + } + return names; + } + + private String readString(int offset, int maxLength) { + StringBuilder string = new StringBuilder(); + for (int c = 0; c < maxLength; c++) { + int currChar = rom[offset + c] & 0xFF; + if (tb[currChar] != null) { + string.append(tb[currChar]); + } else { + if (currChar == 0x50 || currChar == 0x00) { + break; + } else { + string.append("\\x" + String.format("%02X", currChar)); + } + } + } + return string.toString(); + } + + public byte[] translateString(String text) { + List data = new ArrayList(); + while (text.length() != 0) { + int i = Math.max(0, longestTableToken - text.length()); + if (text.charAt(0) == '\\' && text.charAt(1) == 'x') { + data.add((byte) Integer.parseInt(text.substring(2, 4), 16)); + text = text.substring(4); + } else { + while (!(d + .containsKey(text.substring(0, longestTableToken - i)) || (i == longestTableToken))) { + i++; + } + if (i == longestTableToken) { + text = text.substring(1); + } else { + data.add(d.get(text.substring(0, longestTableToken - i))); + text = text.substring(longestTableToken - i); + } + } + } + byte[] ret = new byte[data.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = data.get(i); + } + return ret; + } + + private String readFixedLengthString(int offset, int length) { + return readString(offset, length); + } + + public String readVariableLengthString(int offset) { + return readString(offset, Integer.MAX_VALUE); + } + + public String readVariableLengthScriptString(int offset) { + return readString(offset, Integer.MAX_VALUE); + } + + public byte[] traduire(String str) { + return translateString(str); + } + + private void writeFixedLengthString(String str, int offset, int length) { + byte[] translated = translateString(str); + int len = Math.min(translated.length, length); + System.arraycopy(translated, 0, rom, offset, len); + while (len < length) { + rom[offset + len] = 0x50; + len++; + } + } + + private void writeFixedLengthScriptString(String str, int offset, int length) { + byte[] translated = translateString(str); + int len = Math.min(translated.length, length); + System.arraycopy(translated, 0, rom, offset, len); + while (len < length) { + rom[offset + len] = 0x00; + len++; + } + } + + private int lengthOfStringAt(int offset) { + int len = 0; + while (rom[offset + len] != 0x50 && rom[offset + len] != 0x00) { + len++; + } + return len; + } + + private int makeGBPointer(int offset) { + if (offset < 0x4000) { + return offset; + } else { + return (offset % 0x4000) + 0x4000; + } + } + + private int bankOf(int offset) { + return (offset / 0x4000); + } + + private int calculateOffset(int bank, int pointer) { + if (pointer < 0x4000) { + return pointer; + } else { + return (pointer % 0x4000) + bank * 0x4000; + } + } + + private boolean romSig(byte[] rom, String sig) { + try { + int sigOffset = 0x13F; + byte[] sigBytes = sig.getBytes("US-ASCII"); + for (int i = 0; i < sigBytes.length; i++) { + if (rom[sigOffset + i] != sigBytes[i]) { + return false; + } + } + return true; + } catch (UnsupportedEncodingException ex) { + return false; + } + + } + + @Override + public boolean isInGame(Pokemon pkmn) { + return (pkmn.number >= 1 && pkmn.number <= 251); + } + + @Override + public boolean isInGame(int pokemonNumber) { + return (pokemonNumber >= 1 && pokemonNumber <= 251); + } + + @Override + public List getStarters() { + // Get the starters + List starters = new ArrayList(); + starters.add(pokes[rom[romEntry.arrayEntries.get("StarterOffsets1")[0]] & 0xFF]); + starters.add(pokes[rom[romEntry.arrayEntries.get("StarterOffsets2")[0]] & 0xFF]); + starters.add(pokes[rom[romEntry.arrayEntries.get("StarterOffsets3")[0]] & 0xFF]); + return starters; + } + + @Override + public boolean setStarters(List newStarters) { + if (newStarters.size() != 3) { + return false; + } + for (Pokemon pkmn : newStarters) { + if (!isInGame(pkmn)) { + return false; + } + } + + // Actually write + + for (int i = 0; i < 3; i++) { + byte starter = (byte) newStarters.get(i).number; + int[] offsets = romEntry.arrayEntries.get("StarterOffsets" + + (i + 1)); + for (int offset : offsets) { + rom[offset] = starter; + } + } + + // Attempt to replace text + if (romEntry.getValue("CanChangeStarterText") > 0) { + List cyndaTexts = RomFunctions.search(rom, + traduire("CYNDAQUIL")); + int offset = cyndaTexts.get(romEntry.isCrystal ? 1 : 0); + String pokeName = newStarters.get(0).name; + writeFixedLengthScriptString(pokeName + "?\\e", offset, + lengthOfStringAt(offset) + 1); + + List totoTexts = RomFunctions.search(rom, + traduire("TOTODILE")); + offset = totoTexts.get(romEntry.isCrystal ? 1 : 0); + pokeName = newStarters.get(1).name; + writeFixedLengthScriptString(pokeName + "?\\e", offset, + lengthOfStringAt(offset) + 1); + + List chikoTexts = RomFunctions.search(rom, + traduire("CHIKORITA")); + offset = chikoTexts.get(romEntry.isCrystal ? 1 : 0); + pokeName = newStarters.get(2).name; + writeFixedLengthScriptString(pokeName + "?\\e", offset, + lengthOfStringAt(offset) + 1); + } + return true; + } + + @Override + public List getStarterHeldItems() { + List sHeldItems = new ArrayList(); + int[] shiOffsets = romEntry.arrayEntries.get("StarterHeldItems"); + for (int offset : shiOffsets) { + sHeldItems.add(rom[offset] & 0xFF); + } + return sHeldItems; + } + + @Override + public void setStarterHeldItems(List items) { + int[] shiOffsets = romEntry.arrayEntries.get("StarterHeldItems"); + if (items.size() != shiOffsets.length) { + return; + } + Iterator sHeldItems = items.iterator(); + for (int offset : shiOffsets) { + rom[offset] = sHeldItems.next().byteValue(); + } + } + + @Override + public void shufflePokemonStats() { + for (int i = 1; i <= 251; i++) { + pokes[i].shuffleStats(); + } + } + + @Override + public List getEncounters(boolean useTimeOfDay) { + int offset = romEntry.getValue("WildPokemonOffset"); + List areas = new ArrayList(); + offset = readLandEncounters(offset, areas, useTimeOfDay); // Johto + offset = readSeaEncounters(offset, areas); // Johto + offset = readLandEncounters(offset, areas, useTimeOfDay); // Kanto + offset = readSeaEncounters(offset, areas); // Kanto + offset = readLandEncounters(offset, areas, useTimeOfDay); // Specials + offset = readSeaEncounters(offset, areas); // Specials + + // Fishing Data + offset = romEntry.getValue("FishingWildsOffset"); + int rootOffset = offset; + for (int k = 0; k < 12; k++) { + EncounterSet es = new EncounterSet(); + es.displayName = "Fishing Group " + (k + 1); + for (int i = 0; i < 11; i++) { + offset++; + int pokeNum = rom[offset++] & 0xFF; + int level = rom[offset++] & 0xFF; + if (pokeNum == 0) { + if (!useTimeOfDay) { + // read the encounter they put here for DAY + int specialOffset = rootOffset + 33 * 12 + level * 4 + + 2; + Encounter enc = new Encounter(); + enc.pokemon = pokes[rom[specialOffset] & 0xFF]; + enc.level = rom[specialOffset + 1] & 0xFF; + es.encounters.add(enc); + } + // else will be handled by code below + } else { + Encounter enc = new Encounter(); + enc.pokemon = pokes[pokeNum]; + enc.level = level; + es.encounters.add(enc); + } + } + areas.add(es); + } + if (useTimeOfDay) { + for (int k = 0; k < 11; k++) { + EncounterSet es = new EncounterSet(); + es.displayName = "Time-Specific Fishing " + (k + 1); + for (int i = 0; i < 4; i++) { + int pokeNum = rom[offset++] & 0xFF; + int level = rom[offset++] & 0xFF; + Encounter enc = new Encounter(); + enc.pokemon = pokes[pokeNum]; + enc.level = level; + es.encounters.add(enc); + } + areas.add(es); + } + } + + // Headbutt Data + offset = romEntry.getValue("HeadbuttWildsOffset"); + int limit = romEntry.getValue("HeadbuttTableSize"); + for (int i = 0; i < limit; i++) { + EncounterSet es = new EncounterSet(); + es.displayName = "Headbutt Trees Set " + (i + 1); + while ((rom[offset] & 0xFF) != 0xFF) { + offset++; + int pokeNum = rom[offset++] & 0xFF; + int level = rom[offset++] & 0xFF; + Encounter enc = new Encounter(); + enc.pokemon = pokes[pokeNum]; + enc.level = level; + es.encounters.add(enc); + } + offset++; + areas.add(es); + } + + // Bug Catching Contest Data + offset = romEntry.getValue("BCCWildsOffset"); + EncounterSet bccES = new EncounterSet(); + bccES.displayName = "Bug Catching Contest"; + while ((rom[offset] & 0xFF) != 0xFF) { + offset++; + Encounter enc = new Encounter(); + enc.pokemon = pokes[rom[offset++] & 0xFF]; + enc.level = rom[offset++] & 0xFF; + enc.maxLevel = rom[offset++] & 0xFF; + bccES.encounters.add(enc); + } + areas.add(bccES); + + return areas; + } + + private int readLandEncounters(int offset, List areas, + boolean useTimeOfDay) { + String[] todNames = new String[] { "Morning", "Day", "Night" }; + while ((rom[offset] & 0xFF) != 0xFF) { + int mapBank = rom[offset] & 0xFF; + int mapNumber = rom[offset + 1] & 0xFF; + String mapName = mapNames[mapBank][mapNumber]; + if (useTimeOfDay) { + for (int i = 0; i < 3; i++) { + EncounterSet encset = new EncounterSet(); + encset.rate = rom[offset + 2 + i] & 0xFF; + encset.displayName = mapName + " Grass/Cave (" + + todNames[i] + ")"; + for (int j = 0; j < 7; j++) { + Encounter enc = new Encounter(); + enc.level = rom[offset + 5 + (i * 14) + (j * 2)] & 0xFF; + enc.maxLevel = 0; + enc.pokemon = pokes[rom[offset + 5 + (i * 14) + (j * 2) + + 1] & 0xFF]; + encset.encounters.add(enc); + } + areas.add(encset); + } + } else { + // Use Day only + EncounterSet encset = new EncounterSet(); + encset.rate = rom[offset + 3] & 0xFF; + encset.displayName = mapName + " Grass/Cave"; + for (int j = 0; j < 7; j++) { + Encounter enc = new Encounter(); + enc.level = rom[offset + 5 + 14 + (j * 2)] & 0xFF; + enc.maxLevel = 0; + enc.pokemon = pokes[rom[offset + 5 + 14 + (j * 2) + 1] & 0xFF]; + encset.encounters.add(enc); + } + areas.add(encset); + } + offset += 47; + } + return offset + 1; + } + + private int readSeaEncounters(int offset, List areas) { + while ((rom[offset] & 0xFF) != 0xFF) { + int mapBank = rom[offset] & 0xFF; + int mapNumber = rom[offset + 1] & 0xFF; + String mapName = mapNames[mapBank][mapNumber]; + EncounterSet encset = new EncounterSet(); + encset.rate = rom[offset + 2] & 0xFF; + encset.displayName = mapName + " Surfing"; + for (int j = 0; j < 3; j++) { + Encounter enc = new Encounter(); + enc.level = rom[offset + 3 + (j * 2)] & 0xFF; + enc.maxLevel = 0; + enc.pokemon = pokes[rom[offset + 3 + (j * 2) + 1] & 0xFF]; + encset.encounters.add(enc); + } + areas.add(encset); + offset += 9; + } + return offset + 1; + } + + @Override + public void setEncounters(boolean useTimeOfDay, + List encounters) { + if (!havePatchedFleeing) { + patchFleeing(); + } + int offset = romEntry.getValue("WildPokemonOffset"); + Iterator areas = encounters.iterator(); + offset = writeLandEncounters(offset, areas, useTimeOfDay); // Johto + offset = writeSeaEncounters(offset, areas); // Johto + offset = writeLandEncounters(offset, areas, useTimeOfDay); // Kanto + offset = writeSeaEncounters(offset, areas); // Kanto + offset = writeLandEncounters(offset, areas, useTimeOfDay); // Specials + offset = writeSeaEncounters(offset, areas); // Specials + + // Fishing Data + offset = romEntry.getValue("FishingWildsOffset"); + for (int k = 0; k < 12; k++) { + EncounterSet es = areas.next(); + Iterator encs = es.encounters.iterator(); + for (int i = 0; i < 11; i++) { + offset++; + if (rom[offset] == 0) { + if (!useTimeOfDay) { + // overwrite with a static encounter + Encounter enc = encs.next(); + rom[offset++] = (byte) enc.pokemon.number; + rom[offset++] = (byte) enc.level; + } else { + // else handle below + offset += 2; + } + } else { + Encounter enc = encs.next(); + rom[offset++] = (byte) enc.pokemon.number; + rom[offset++] = (byte) enc.level; + } + } + } + if (useTimeOfDay) { + for (int k = 0; k < 11; k++) { + EncounterSet es = areas.next(); + Iterator encs = es.encounters.iterator(); + for (int i = 0; i < 4; i++) { + Encounter enc = encs.next(); + rom[offset++] = (byte) enc.pokemon.number; + rom[offset++] = (byte) enc.level; + } + } + } + + // Headbutt Data + offset = romEntry.getValue("HeadbuttWildsOffset"); + int limit = romEntry.getValue("HeadbuttTableSize"); + for (int i = 0; i < limit; i++) { + EncounterSet es = areas.next(); + Iterator encs = es.encounters.iterator(); + while ((rom[offset] & 0xFF) != 0xFF) { + Encounter enc = encs.next(); + offset++; + rom[offset++] = (byte) enc.pokemon.number; + rom[offset++] = (byte) enc.level; + } + offset++; + } + + // Bug Catching Contest Data + offset = romEntry.getValue("BCCWildsOffset"); + EncounterSet bccES = areas.next(); + Iterator bccEncs = bccES.encounters.iterator(); + while ((rom[offset] & 0xFF) != 0xFF) { + offset++; + Encounter enc = bccEncs.next(); + rom[offset++] = (byte) enc.pokemon.number; + rom[offset++] = (byte) enc.level; + rom[offset++] = (byte) enc.maxLevel; + } + + } + + private int writeLandEncounters(int offset, Iterator areas, + boolean useTimeOfDay) { + while ((rom[offset] & 0xFF) != 0xFF) { + if (useTimeOfDay) { + for (int i = 0; i < 3; i++) { + EncounterSet encset = areas.next(); + Iterator encountersHere = encset.encounters + .iterator(); + for (int j = 0; j < 7; j++) { + rom[offset + 5 + (i * 14) + (j * 2) + 1] = (byte) encountersHere + .next().pokemon.number; + } + } + } else { + // Write the set to all 3 equally + EncounterSet encset = areas.next(); + for (int i = 0; i < 3; i++) { + Iterator encountersHere = encset.encounters + .iterator(); + for (int j = 0; j < 7; j++) { + rom[offset + 5 + (i * 14) + (j * 2) + 1] = (byte) encountersHere + .next().pokemon.number; + } + } + } + offset += 47; + } + return offset + 1; + } + + private int writeSeaEncounters(int offset, Iterator areas) { + while ((rom[offset] & 0xFF) != 0xFF) { + EncounterSet encset = areas.next(); + Iterator encountersHere = encset.encounters.iterator(); + for (int j = 0; j < 3; j++) { + rom[offset + 3 + (j * 2) + 1] = (byte) encountersHere.next().pokemon.number; + } + offset += 9; + } + return offset + 1; + } + + @Override + public List getTrainers() { + int traineroffset = romEntry.getValue("TrainerDataTableOffset"); + int traineramount = romEntry.getValue("TrainerClassAmount"); + int[] trainerclasslimits = romEntry.arrayEntries + .get("TrainerDataClassCounts"); + + int[] pointers = new int[traineramount + 1]; + for (int i = 1; i <= traineramount; i++) { + int pointer = readWord(traineroffset + (i - 1) * 2); + pointers[i] = calculateOffset(bankOf(traineroffset), pointer); + } + + List tcnames = this.getTrainerClassNames(); + + List allTrainers = new ArrayList(); + for (int i = 1; i <= traineramount; i++) { + int offs = pointers[i]; + int limit = trainerclasslimits[i]; + for (int trnum = 0; trnum < limit; trnum++) { + Trainer tr = new Trainer(); + tr.offset = offs; + tr.trainerclass = i; + String name = readVariableLengthString(offs); + tr.name = name; + tr.fullDisplayName = tcnames.get(i - 1) + " " + name; + int len = lengthOfStringAt(offs); + offs += len + 1; + int dataType = rom[offs] & 0xFF; + tr.poketype = dataType; + offs++; + while ((rom[offs] & 0xFF) != 0xFF) { + TrainerPokemon tp = new TrainerPokemon(); + tp.level = rom[offs] & 0xFF; + tp.pokemon = pokes[rom[offs + 1] & 0xFF]; + offs += 2; + if (dataType == 2 || dataType == 3) { + tp.heldItem = rom[offs] & 0xFF; + offs++; + } + if (dataType % 2 == 1) { + tp.move1 = rom[offs] & 0xFF; + tp.move2 = rom[offs + 1] & 0xFF; + tp.move3 = rom[offs + 2] & 0xFF; + tp.move4 = rom[offs + 3] & 0xFF; + offs += 4; + } + tr.pokemon.add(tp); + } + allTrainers.add(tr); + offs++; + } + } + universalTrainerTags(allTrainers); + if (romEntry.isCrystal) { + crystalTags(allTrainers); + } else { + goldSilverTags(allTrainers); + } + return allTrainers; + } + + @Override + public void setTrainers(List trainerData) { + int traineroffset = romEntry.getValue("TrainerDataTableOffset"); + int traineramount = romEntry.getValue("TrainerClassAmount"); + int[] trainerclasslimits = romEntry.arrayEntries + .get("TrainerDataClassCounts"); + + int[] pointers = new int[traineramount + 1]; + for (int i = 1; i <= traineramount; i++) { + int pointer = readWord(traineroffset + (i - 1) * 2); + pointers[i] = calculateOffset(bankOf(traineroffset), pointer); + } + + Iterator allTrainers = trainerData.iterator(); + for (int i = 1; i <= traineramount; i++) { + int offs = pointers[i]; + int limit = trainerclasslimits[i]; + for (int trnum = 0; trnum < limit; trnum++) { + Trainer tr = allTrainers.next(); + if (tr.trainerclass != i) { + System.err.println("Trainer mismatch: " + tr.name); + } + // Write their name + int trnamelen = internalStringLength(tr.name); + writeFixedLengthString(tr.name, offs, trnamelen + 1); + offs += trnamelen + 1; + // Poketype + tr.poketype = 0; // remove held items and moves + rom[offs++] = (byte) tr.poketype; + Iterator tPokes = tr.pokemon.iterator(); + for (int tpnum = 0; tpnum < tr.pokemon.size(); tpnum++) { + TrainerPokemon tp = tPokes.next(); + rom[offs] = (byte) tp.level; + rom[offs + 1] = (byte) tp.pokemon.number; + offs += 2; + if (tr.poketype == 2 || tr.poketype == 3) { + rom[offs] = (byte) tp.heldItem; + offs++; + } + if (tr.poketype % 2 == 1) { + rom[offs] = (byte) tp.move1; + rom[offs + 1] = (byte) tp.move2; + rom[offs + 2] = (byte) tp.move3; + rom[offs + 3] = (byte) tp.move4; + offs += 4; + } + } + rom[offs] = (byte) 0xFF; + offs++; + } + } + + } + + private void universalTrainerTags(List allTrainers) { + // Gym Leaders + tbc(allTrainers, 1, 0, "GYM1"); + tbc(allTrainers, 3, 0, "GYM2"); + tbc(allTrainers, 2, 0, "GYM3"); + tbc(allTrainers, 4, 0, "GYM4"); + tbc(allTrainers, 7, 0, "GYM5"); + tbc(allTrainers, 6, 0, "GYM6"); + tbc(allTrainers, 5, 0, "GYM7"); + tbc(allTrainers, 8, 0, "GYM8"); + tbc(allTrainers, 17, 0, "GYM9"); + tbc(allTrainers, 18, 0, "GYM10"); + tbc(allTrainers, 19, 0, "GYM11"); + tbc(allTrainers, 21, 0, "GYM12"); + tbc(allTrainers, 26, 0, "GYM13"); + tbc(allTrainers, 35, 0, "GYM14"); + tbc(allTrainers, 46, 0, "GYM15"); + tbc(allTrainers, 64, 0, "GYM16"); + + // Elite 4 & Red + tbc(allTrainers, 11, 0, "ELITE1"); + tbc(allTrainers, 15, 0, "ELITE2"); + tbc(allTrainers, 13, 0, "ELITE3"); + tbc(allTrainers, 14, 0, "ELITE4"); + tbc(allTrainers, 16, 0, "CHAMPION"); + tbc(allTrainers, 63, 0, "UBER"); + + // Silver + // Order in rom is BAYLEEF, QUILAVA, CROCONAW teams + // Starters go CYNDA, TOTO, CHIKO + // So we want 0=CROCONAW/FERALI, 1=BAYLEEF/MEGAN, 2=QUILAVA/TYPHLO + tbc(allTrainers, 9, 0, "RIVAL1-1"); + tbc(allTrainers, 9, 1, "RIVAL1-2"); + tbc(allTrainers, 9, 2, "RIVAL1-0"); + + tbc(allTrainers, 9, 3, "RIVAL2-1"); + tbc(allTrainers, 9, 4, "RIVAL2-2"); + tbc(allTrainers, 9, 5, "RIVAL2-0"); + + tbc(allTrainers, 9, 6, "RIVAL3-1"); + tbc(allTrainers, 9, 7, "RIVAL3-2"); + tbc(allTrainers, 9, 8, "RIVAL3-0"); + + tbc(allTrainers, 9, 9, "RIVAL4-1"); + tbc(allTrainers, 9, 10, "RIVAL4-2"); + tbc(allTrainers, 9, 11, "RIVAL4-0"); + + tbc(allTrainers, 9, 12, "RIVAL5-1"); + tbc(allTrainers, 9, 13, "RIVAL5-2"); + tbc(allTrainers, 9, 14, "RIVAL5-0"); + + tbc(allTrainers, 42, 0, "RIVAL6-1"); + tbc(allTrainers, 42, 1, "RIVAL6-2"); + tbc(allTrainers, 42, 2, "RIVAL6-0"); + + tbc(allTrainers, 42, 3, "RIVAL7-1"); + tbc(allTrainers, 42, 4, "RIVAL7-2"); + tbc(allTrainers, 42, 5, "RIVAL7-0"); + + // Female Rocket Executive (Ariana) + tbc(allTrainers, 55, 0, "THEMED:ARIANA"); + tbc(allTrainers, 55, 1, "THEMED:ARIANA"); + + // others (unlabeled in this game, using HGSS names) + tbc(allTrainers, 51, 2, "THEMED:PETREL"); + tbc(allTrainers, 51, 3, "THEMED:PETREL"); + + tbc(allTrainers, 51, 1, "THEMED:PROTON"); + tbc(allTrainers, 31, 0, "THEMED:PROTON"); + + // Sprout Tower + tbc(allTrainers, 56, 0, "THEMED:SPROUTTOWER"); + tbc(allTrainers, 56, 1, "THEMED:SPROUTTOWER"); + tbc(allTrainers, 56, 2, "THEMED:SPROUTTOWER"); + tbc(allTrainers, 56, 3, "THEMED:SPROUTTOWER"); + tbc(allTrainers, 56, 6, "THEMED:SPROUTTOWER"); + tbc(allTrainers, 56, 7, "THEMED:SPROUTTOWER"); + tbc(allTrainers, 56, 8, "THEMED:SPROUTTOWER"); + } + + private void goldSilverTags(List allTrainers) { + tbc(allTrainers, 24, 0, "GYM1"); + tbc(allTrainers, 24, 1, "GYM1"); + tbc(allTrainers, 36, 4, "GYM2"); + tbc(allTrainers, 36, 5, "GYM2"); + tbc(allTrainers, 36, 6, "GYM2"); + tbc(allTrainers, 61, 0, "GYM2"); + tbc(allTrainers, 61, 3, "GYM2"); + tbc(allTrainers, 25, 0, "GYM3"); + tbc(allTrainers, 25, 1, "GYM3"); + tbc(allTrainers, 29, 0, "GYM3"); + tbc(allTrainers, 29, 1, "GYM3"); + tbc(allTrainers, 56, 4, "GYM4"); + tbc(allTrainers, 56, 5, "GYM4"); + tbc(allTrainers, 57, 0, "GYM4"); + tbc(allTrainers, 57, 1, "GYM4"); + tbc(allTrainers, 50, 1, "GYM5"); + tbc(allTrainers, 50, 3, "GYM5"); + tbc(allTrainers, 50, 4, "GYM5"); + tbc(allTrainers, 50, 6, "GYM5"); + tbc(allTrainers, 58, 0, "GYM7"); + tbc(allTrainers, 58, 1, "GYM7"); + tbc(allTrainers, 58, 2, "GYM7"); + tbc(allTrainers, 33, 0, "GYM7"); + tbc(allTrainers, 33, 1, "GYM7"); + tbc(allTrainers, 27, 2, "GYM8"); + tbc(allTrainers, 27, 4, "GYM8"); + tbc(allTrainers, 27, 3, "GYM8"); + tbc(allTrainers, 28, 2, "GYM8"); + tbc(allTrainers, 28, 3, "GYM8"); + tbc(allTrainers, 54, 17, "GYM9"); + tbc(allTrainers, 38, 20, "GYM10"); + tbc(allTrainers, 39, 17, "GYM10"); + tbc(allTrainers, 39, 18, "GYM10"); + tbc(allTrainers, 49, 2, "GYM11"); + tbc(allTrainers, 43, 1, "GYM11"); + tbc(allTrainers, 32, 2, "GYM11"); + tbc(allTrainers, 61, 4, "GYM12"); + tbc(allTrainers, 61, 5, "GYM12"); + tbc(allTrainers, 25, 8, "GYM12"); + tbc(allTrainers, 53, 18, "GYM12"); + tbc(allTrainers, 29, 13, "GYM12"); + tbc(allTrainers, 25, 2, "GYM13"); + tbc(allTrainers, 25, 5, "GYM13"); + tbc(allTrainers, 53, 4, "GYM13"); + tbc(allTrainers, 54, 4, "GYM13"); + tbc(allTrainers, 57, 5, "GYM14"); + tbc(allTrainers, 57, 6, "GYM14"); + tbc(allTrainers, 52, 1, "GYM14"); + tbc(allTrainers, 52, 10, "GYM14"); + } + + private void crystalTags(List allTrainers) { + tbc(allTrainers, 24, 0, "GYM1"); + tbc(allTrainers, 24, 1, "GYM1"); + tbc(allTrainers, 36, 4, "GYM2"); + tbc(allTrainers, 36, 5, "GYM2"); + tbc(allTrainers, 36, 6, "GYM2"); + tbc(allTrainers, 61, 0, "GYM2"); + tbc(allTrainers, 61, 3, "GYM2"); + tbc(allTrainers, 25, 0, "GYM3"); + tbc(allTrainers, 25, 1, "GYM3"); + tbc(allTrainers, 29, 0, "GYM3"); + tbc(allTrainers, 29, 1, "GYM3"); + tbc(allTrainers, 56, 4, "GYM4"); + tbc(allTrainers, 56, 5, "GYM4"); + tbc(allTrainers, 57, 0, "GYM4"); + tbc(allTrainers, 57, 1, "GYM4"); + tbc(allTrainers, 50, 1, "GYM5"); + tbc(allTrainers, 50, 3, "GYM5"); + tbc(allTrainers, 50, 4, "GYM5"); + tbc(allTrainers, 50, 6, "GYM5"); + tbc(allTrainers, 58, 0, "GYM7"); + tbc(allTrainers, 58, 1, "GYM7"); + tbc(allTrainers, 58, 2, "GYM7"); + tbc(allTrainers, 33, 0, "GYM7"); + tbc(allTrainers, 33, 1, "GYM7"); + tbc(allTrainers, 27, 2, "GYM8"); + tbc(allTrainers, 27, 4, "GYM8"); + tbc(allTrainers, 27, 3, "GYM8"); + tbc(allTrainers, 28, 2, "GYM8"); + tbc(allTrainers, 28, 3, "GYM8"); + tbc(allTrainers, 54, 17, "GYM9"); + tbc(allTrainers, 38, 20, "GYM10"); + tbc(allTrainers, 39, 17, "GYM10"); + tbc(allTrainers, 39, 18, "GYM10"); + tbc(allTrainers, 49, 2, "GYM11"); + tbc(allTrainers, 43, 1, "GYM11"); + tbc(allTrainers, 32, 2, "GYM11"); + tbc(allTrainers, 61, 4, "GYM12"); + tbc(allTrainers, 61, 5, "GYM12"); + tbc(allTrainers, 25, 8, "GYM12"); + tbc(allTrainers, 53, 18, "GYM12"); + tbc(allTrainers, 29, 13, "GYM12"); + tbc(allTrainers, 25, 2, "GYM13"); + tbc(allTrainers, 25, 5, "GYM13"); + tbc(allTrainers, 53, 4, "GYM13"); + tbc(allTrainers, 54, 4, "GYM13"); + tbc(allTrainers, 57, 5, "GYM14"); + tbc(allTrainers, 57, 6, "GYM14"); + tbc(allTrainers, 52, 1, "GYM14"); + tbc(allTrainers, 52, 10, "GYM14"); + } + + private void tbc(List allTrainers, int classNum, int number, + String tag) { + int currnum = -1; + for (Trainer t : allTrainers) { + if (t.trainerclass == classNum) { + currnum++; + if (currnum == number) { + t.tag = tag; + return; + } + } + } + } + + @Override + public List getPokemon() { + return pokemonList; + } + + @Override + public Map> getMovesLearnt() { + Map> movesets = new TreeMap>(); + int pointersOffset = romEntry.getValue("PokemonMovesetsTableOffset"); + for (int i = 1; i <= 251; i++) { + int pointer = readWord(pointersOffset + (i - 1) * 2); + int realPointer = calculateOffset(bankOf(pointersOffset), pointer); + Pokemon pkmn = pokes[i]; + // Skip over evolution data + while (rom[realPointer] != 0) { + if (rom[realPointer] == 5) { + realPointer += 4; + } else { + realPointer += 3; + } + } + List ourMoves = new ArrayList(); + realPointer++; + while (rom[realPointer] != 0) { + MoveLearnt learnt = new MoveLearnt(); + learnt.level = rom[realPointer] & 0xFF; + learnt.move = rom[realPointer + 1] & 0xFF; + ourMoves.add(learnt); + realPointer += 2; + } + movesets.put(pkmn, ourMoves); + } + return movesets; + } + + @Override + public void setMovesLearnt(Map> movesets) { + writeEvosAndMovesLearnt(null, movesets); + } + + @Override + public List getStaticPokemon() { + List statics = new ArrayList(); + if (romEntry.getValue("StaticPokemonSupport") > 0) { + for (int offset : romEntry.staticPokemonSingle) { + statics.add(pokes[rom[offset] & 0xFF]); + } + // Game Corner + for (int offset : romEntry.staticPokemonGameCorner.keySet()) { + statics.add(pokes[rom[offset] & 0xFF]); + } + } + return statics; + } + + @Override + public boolean setStaticPokemon(List staticPokemon) { + if (romEntry.getValue("StaticPokemonSupport") == 0) { + return false; + } + if (!havePatchedFleeing) { + patchFleeing(); + } + if (staticPokemon.size() != romEntry.staticPokemonSingle.size() + + romEntry.staticPokemonGameCorner.size()) { + return false; + } + for (Pokemon pkmn : staticPokemon) { + if (!isInGame(pkmn)) { + return false; + } + } + + Iterator statics = staticPokemon.iterator(); + for (int offset : romEntry.staticPokemonSingle) { + rom[offset] = (byte) statics.next().number; + } + + int gcNameLength = romEntry.getValue("GameCornerPokemonNameLength"); + + // Sort out static Pokemon + for (int offset : romEntry.staticPokemonGameCorner.keySet()) { + rom[offset] = (byte) statics.next().number; + rom[offset + 0x11] = rom[offset]; + rom[offset + 0x16] = rom[offset]; + writePaddedPokemonName(pokes[rom[offset] & 0xFF].name, + gcNameLength, romEntry.staticPokemonGameCorner.get(offset)); + } + + // Copies? + for (int offset : romEntry.staticPokemonCopy.keySet()) { + int copyTo = romEntry.staticPokemonCopy.get(offset); + rom[copyTo] = rom[offset]; + } + return true; + } + + @Override + public boolean canChangeStaticPokemon() { + return (romEntry.getValue("StaticPokemonSupport") > 0); + } + + @Override + public List bannedForStaticPokemon() { + return Arrays.asList(pokes[201]); // Unown banned + } + + private void writePaddedPokemonName(String name, int length, int offset) { + String paddedName = String.format("%-" + length + "s", name); + byte[] rawData = traduire(paddedName); + for (int i = 0; i < length; i++) { + rom[offset + i] = rawData[i]; + } + } + + @Override + public List getTMMoves() { + List tms = new ArrayList(); + int offset = romEntry.getValue("TMMovesOffset"); + for (int i = 1; i <= 50; i++) { + tms.add(rom[offset + (i - 1)] & 0xFF); + } + return tms; + } + + @Override + public List getHMMoves() { + List hms = new ArrayList(); + int offset = romEntry.getValue("TMMovesOffset"); + for (int i = 1; i <= 7; i++) { + hms.add(rom[offset + 50 + (i - 1)] & 0xFF); + } + return hms; + } + + @Override + public void setTMMoves(List moveIndexes) { + int offset = romEntry.getValue("TMMovesOffset"); + for (int i = 1; i <= 50; i++) { + rom[offset + (i - 1)] = moveIndexes.get(i - 1).byteValue(); + } + + // TM Text + String[] moveNames = readMoveNames(); + for (TMTextEntry tte : romEntry.tmTexts) { + String moveName = moveNames[moveIndexes.get(tte.number - 1)]; + String text = tte.template.replace("%m", moveName); + writeFixedLengthScriptString(text, tte.offset, + lengthOfStringAt(tte.offset)); + } + } + + @Override + public int getTMCount() { + return 50; + } + + @Override + public int getHMCount() { + return 7; + } + + @Override + public Map getTMHMCompatibility() { + Map compat = new TreeMap(); + for (int i = 1; i <= 251; i++) { + int baseStatsOffset = romEntry.getValue("PokemonStatsOffset") + + (i - 1) * 0x20; + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[58]; + for (int j = 0; j < 8; j++) { + readByteIntoFlags(flags, j * 8 + 1, baseStatsOffset + 0x18 + j); + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setTMHMCompatibility(Map compatData) { + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + int baseStatsOffset = romEntry.getValue("PokemonStatsOffset") + + (pkmn.number - 1) * 0x20; + for (int j = 0; j < 8; j++) { + if (!romEntry.isCrystal || j != 7) { + rom[baseStatsOffset + 0x18 + j] = getByteFromFlags(flags, + j * 8 + 1); + } else { + // Move tutor data + // bits 1,2,3 of byte 7 + int changedByte = getByteFromFlags(flags, j * 8 + 1) & 0xFF; + int currentByte = rom[baseStatsOffset + 0x18 + j]; + changedByte |= ((currentByte >> 1) & 0x01) << 1; + changedByte |= ((currentByte >> 2) & 0x01) << 2; + changedByte |= ((currentByte >> 3) & 0x01) << 3; + rom[baseStatsOffset + 0x18 + j] = (byte) changedByte; + } + } + } + } + + @Override + public boolean hasMoveTutors() { + return romEntry.isCrystal; + } + + @Override + public List getMoveTutorMoves() { + if (romEntry.isCrystal) { + List mtMoves = new ArrayList(); + for (int offset : romEntry.arrayEntries.get("MoveTutorMoves")) { + mtMoves.add(rom[offset] & 0xFF); + } + return mtMoves; + } + return new ArrayList(); + } + + @Override + public void setMoveTutorMoves(List moves) { + if (!romEntry.isCrystal) { + return; + } + if (moves.size() != 3) { + return; + } + Iterator mvList = moves.iterator(); + for (int offset : romEntry.arrayEntries.get("MoveTutorMoves")) { + rom[offset] = mvList.next().byteValue(); + } + + // Construct a new menu + if (romEntry.getValue("MoveTutorMenuOffset") > 0 + && romEntry.getValue("MoveTutorMenuNewSpace") > 0) { + String[] moveNames = readMoveNames(); + String[] names = new String[] { moveNames[moves.get(0)], + moveNames[moves.get(1)], moveNames[moves.get(2)], "CANCEL" }; + int menuOffset = romEntry.getValue("MoveTutorMenuNewSpace"); + rom[menuOffset++] = (byte) 0x80; + rom[menuOffset++] = 0x4; + for (int i = 0; i < 4; i++) { + byte[] trans = traduire(names[i]); + System.arraycopy(trans, 0, rom, menuOffset, trans.length); + menuOffset += trans.length; + rom[menuOffset++] = 0x50; + } + int pointerOffset = romEntry.getValue("MoveTutorMenuOffset"); + writeWord(pointerOffset, + makeGBPointer(romEntry.getValue("MoveTutorMenuNewSpace"))); + } + } + + @Override + public Map getMoveTutorCompatibility() { + if (!romEntry.isCrystal) { + return new TreeMap(); + } + Map compat = new TreeMap(); + for (int i = 1; i <= 251; i++) { + int baseStatsOffset = romEntry.getValue("PokemonStatsOffset") + + (i - 1) * 0x20; + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[4]; + int mtByte = rom[baseStatsOffset + 0x1F] & 0xFF; + for (int j = 1; j <= 3; j++) { + flags[j] = ((mtByte >> j) & 0x01) > 0; + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setMoveTutorCompatibility(Map compatData) { + if (!romEntry.isCrystal) { + return; + } + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + int baseStatsOffset = romEntry.getValue("PokemonStatsOffset") + + (pkmn.number - 1) * 0x20; + int origMtByte = rom[baseStatsOffset + 0x1F] & 0xFF; + int mtByte = origMtByte & 0x01; + for (int j = 1; j <= 3; j++) { + mtByte |= flags[j] ? (1 << j) : 0; + } + rom[baseStatsOffset + 0x1F] = (byte) mtByte; + } + } + + @Override + public String getROMName() { + if (isVietCrystal) { + return "Pokemon VietCrystal"; + } + return "Pokemon " + romEntry.name; + } + + @Override + public String getROMCode() { + return romEntry.romCode; + } + + @Override + public String getSupportLevel() { + return "Complete"; + } + + @Override + public boolean hasTimeBasedEncounters() { + return true; // All GSC do + } + + @Override + public List getEvolutions() { + List evos = new ArrayList(); + int pointersOffset = romEntry.getValue("PokemonMovesetsTableOffset"); + List evosForThisPoke = new ArrayList(); + for (int i = 1; i <= 251; i++) { + int pointer = readWord(pointersOffset + (i - 1) * 2); + int realPointer = calculateOffset(bankOf(pointersOffset), pointer); + evosForThisPoke.clear(); + int thisPoke = i; + while (rom[realPointer] != 0) { + int method = rom[realPointer] & 0xFF; + int otherPoke = rom[realPointer + 2 + (method == 5 ? 1 : 0)] & 0xFF; + EvolutionType type = EvolutionType.fromIndex(2, method); + int extraInfo = 0; + if (type == EvolutionType.TRADE) { + int itemNeeded = rom[realPointer + 1] & 0xFF; + if (itemNeeded != 0xFF) { + type = EvolutionType.TRADE_ITEM; + extraInfo = itemNeeded; + } + } else if (type == EvolutionType.LEVEL_ATTACK_HIGHER) { + int tyrogueCond = rom[realPointer + 2] & 0xFF; + if (tyrogueCond == 2) { + type = EvolutionType.LEVEL_DEFENSE_HIGHER; + } else if (tyrogueCond == 3) { + type = EvolutionType.LEVEL_ATK_DEF_SAME; + } + extraInfo = rom[realPointer + 1] & 0xFF; + } else if (type == EvolutionType.HAPPINESS) { + int happCond = rom[realPointer + 1] & 0xFF; + if (happCond == 2) { + type = EvolutionType.HAPPINESS_DAY; + } else if (happCond == 3) { + type = EvolutionType.HAPPINESS_NIGHT; + } + } else { + extraInfo = rom[realPointer + 1] & 0xFF; + } + Evolution evo = new Evolution(thisPoke, otherPoke, true, type, + extraInfo); + if (!evos.contains(evo)) { + evos.add(evo); + evosForThisPoke.add(evo); + } + realPointer += (method == 5 ? 4 : 3); + } + // split evos don't carry stats + if (evosForThisPoke.size() > 1) { + for (Evolution e : evosForThisPoke) { + e.carryStats = false; + } + } + } + return evos; + } + + @Override + public void removeTradeEvolutions(boolean changeMoveEvos) { + // no move evos, so no need to check for those + log("--Removing Trade Evolutions--"); + List evos = this.getEvolutions(); + for (Evolution evol : evos) { + if (evol.type == EvolutionType.TRADE + || evol.type == EvolutionType.TRADE_ITEM) { + // change + if (evol.from == 79) { + // Slowpoke: Make water stone => Slowking + evol.type = EvolutionType.STONE; + evol.extraInfo = 24; // water stone + logEvoChangeStone(pokes[evol.from].name, + pokes[evol.to].name, itemNames[24]); + } else if (evol.from == 117) { + // Seadra: level 40 + evol.type = EvolutionType.LEVEL; + evol.extraInfo = 40; // level + logEvoChangeLevel(pokes[evol.from].name, + pokes[evol.to].name, 40); + } else if (evol.from == 61 || evol.type == EvolutionType.TRADE) { + // Poliwhirl or any of the original 4 trade evos + // Level 37 + evol.type = EvolutionType.LEVEL; + evol.extraInfo = 37; // level + logEvoChangeLevel(pokes[evol.from].name, + pokes[evol.to].name, 37); + } else { + // A new trade evo of a single stage Pokemon + // level 30 + evol.type = EvolutionType.LEVEL; + evol.extraInfo = 30; // level + logEvoChangeLevel(pokes[evol.from].name, + pokes[evol.to].name, 30); + } + } + } + writeEvosAndMovesLearnt(evos, null); + logBlankLine(); + + } + + @Override + public List getTrainerNames() { + int traineroffset = romEntry.getValue("TrainerDataTableOffset"); + int traineramount = romEntry.getValue("TrainerClassAmount"); + int[] trainerclasslimits = romEntry.arrayEntries + .get("TrainerDataClassCounts"); + + int[] pointers = new int[traineramount + 1]; + for (int i = 1; i <= traineramount; i++) { + int pointer = readWord(traineroffset + (i - 1) * 2); + pointers[i] = calculateOffset(bankOf(traineroffset), pointer); + } + + List allTrainers = new ArrayList(); + for (int i = 1; i <= traineramount; i++) { + int offs = pointers[i]; + int limit = trainerclasslimits[i]; + for (int trnum = 0; trnum < limit; trnum++) { + String name = readVariableLengthString(offs); + allTrainers.add(name); + offs += name.length() + 1; + int dataType = rom[offs] & 0xFF; + offs++; + while ((rom[offs] & 0xFF) != 0xFF) { + offs += 2; + if (dataType == 2 || dataType == 3) { + offs++; + } + if (dataType % 2 == 1) { + offs += 4; + } + } + offs++; + } + } + return allTrainers; + } + + @Override + public void setTrainerNames(List trainerNames) { + if (romEntry.getValue("CanChangeTrainerText") != 0) { + int traineroffset = romEntry.getValue("TrainerDataTableOffset"); + int traineramount = romEntry.getValue("TrainerClassAmount"); + int[] trainerclasslimits = romEntry.arrayEntries + .get("TrainerDataClassCounts"); + + int[] pointers = new int[traineramount + 1]; + for (int i = 1; i <= traineramount; i++) { + int pointer = readWord(traineroffset + (i - 1) * 2); + pointers[i] = calculateOffset(bankOf(traineroffset), pointer); + } + // Build up new trainer data using old as a guideline. + int[] offsetsInNew = new int[traineramount + 1]; + int oInNewCurrent = 0; + Iterator allTrainers = trainerNames.iterator(); + ByteArrayOutputStream newData = new ByteArrayOutputStream(); + try { + for (int i = 1; i <= traineramount; i++) { + int offs = pointers[i]; + int limit = trainerclasslimits[i]; + offsetsInNew[i] = oInNewCurrent; + for (int trnum = 0; trnum < limit; trnum++) { + String name = readVariableLengthString(offs); + String newName = allTrainers.next(); + byte[] newNameStr = translateString(newName); + newData.write(newNameStr); + newData.write(0x50); + oInNewCurrent += newNameStr.length + 1; + offs += internalStringLength(name) + 1; + int dataType = rom[offs] & 0xFF; + offs++; + newData.write(dataType); + oInNewCurrent++; + while ((rom[offs] & 0xFF) != 0xFF) { + newData.write(rom, offs, 2); + oInNewCurrent += 2; + offs += 2; + if (dataType == 2 || dataType == 3) { + newData.write(rom, offs, 1); + oInNewCurrent += 1; + offs++; + } + if (dataType % 2 == 1) { + newData.write(rom, offs, 4); + oInNewCurrent += 4; + offs += 4; + } + } + newData.write(0xFF); + oInNewCurrent++; + offs++; + } + } + + // Copy new data into ROM + byte[] newTrainerData = newData.toByteArray(); + int tdBase = pointers[1]; + System.arraycopy(newTrainerData, 0, rom, pointers[1], + newTrainerData.length); + + // Finally, update the pointers + for (int i = 2; i <= traineramount; i++) { + int newOffset = tdBase + offsetsInNew[i]; + writeWord(traineroffset + (i - 1) * 2, + makeGBPointer(newOffset)); + } + } catch (IOException ex) { + // This should never happen, but abort if it does. + } + } + + } + + @Override + public TrainerNameMode trainerNameMode() { + return TrainerNameMode.MAX_LENGTH_WITH_CLASS; + } + + @Override + public int maxTrainerNameLength() { + // line size minus one for space + return 17; + } + + @Override + public List getTCNameLengthsByTrainer() { + int traineramount = romEntry.getValue("TrainerClassAmount"); + int[] trainerclasslimits = romEntry.arrayEntries + .get("TrainerDataClassCounts"); + List tcNames = this.getTrainerClassNames(); + List tcLengthsByT = new ArrayList(); + + for (int i = 1; i <= traineramount; i++) { + int len = internalStringLength(tcNames.get(i - 1)); + for (int k = 0; k < trainerclasslimits[i]; k++) { + tcLengthsByT.add(len); + } + } + + return tcLengthsByT; + } + + @Override + public List getTrainerClassNames() { + int amount = romEntry.getValue("TrainerClassAmount"); + int offset = romEntry.getValue("TrainerClassNamesOffset"); + List trainerClassNames = new ArrayList(); + for (int j = 0; j < amount; j++) { + String name = readVariableLengthString(offset); + offset += lengthOfStringAt(offset) + 1; + trainerClassNames.add(name); + } + return trainerClassNames; + } + + @Override + public void setTrainerClassNames(List trainerClassNames) { + if (romEntry.getValue("CanChangeTrainerText") != 0) { + int amount = romEntry.getValue("TrainerClassAmount"); + int offset = romEntry.getValue("TrainerClassNamesOffset"); + Iterator trainerClassNamesI = trainerClassNames.iterator(); + for (int j = 0; j < amount; j++) { + int len = lengthOfStringAt(offset) + 1; + String newName = trainerClassNamesI.next(); + writeFixedLengthString(newName, offset, len); + offset += len; + } + } + } + + @Override + public boolean fixedTrainerClassNamesLength() { + return true; + } + + @Override + public String getDefaultExtension() { + return "gbc"; + } + + @Override + public int abilitiesPerPokemon() { + return 0; + } + + @Override + public int highestAbilityIndex() { + return 0; + } + + @Override + public int internalStringLength(String string) { + return translateString(string).length; + } + + @Override + public int codeTweaksAvailable() { + int available = 0; + if (romEntry.codeTweaks.get("BWXPTweak") != null) { + available |= CodeTweaks.BW_EXP_PATCH; + } + return available; + } + + @Override + public void applyBWEXPPatch() { + String patchName = romEntry.codeTweaks.get("BWXPTweak"); + if (patchName == null) { + return; + } + + try { + FileFunctions.applyPatch(rom, patchName); + } catch (IOException e) { + + } + } + + @Override + public void applySignature() { + // Intro sprite + + // Pick a pokemon + int pokemon = RandomSource.nextInt(251) + 1; + while (pokemon == 201) { + // Unown is banned + pokemon = RandomSource.nextInt(251) + 1; + } + + rom[romEntry.getValue("IntroSpriteOffset")] = (byte) pokemon; + rom[romEntry.getValue("IntroCryOffset")] = (byte) pokemon; + + } + + @Override + public ItemList getAllowedItems() { + return allowedItems; + } + + private void loadItemNames() { + itemNames = new String[256]; + itemNames[0] = "glitch"; + // trying to emulate pretty much what the game does here + // normal items + int origOffset = romEntry.getValue("ItemNamesOffset"); + int itemNameOffset = origOffset; + for (int index = 1; index <= 0x100; index++) { + if (itemNameOffset / 0x4000 > origOffset / 0x4000) { + // the game would continue making its merry way into VRAM here, + // but we don't have VRAM to simulate. + // just give up. + break; + } + int startOfText = itemNameOffset; + while ((rom[itemNameOffset] & 0xFF) != 0x50) { + itemNameOffset++; + } + itemNameOffset++; + itemNames[index % 256] = readFixedLengthString(startOfText, 20); + } + } + + @Override + public String[] getItemNames() { + return itemNames; + } + + private void patchFleeing() { + havePatchedFleeing = true; + int offset = romEntry.getValue("FleeingDataOffset"); + rom[offset] = (byte) 0xFF; + rom[offset + 0xE] = (byte) 0xFF; + rom[offset + 0x17] = (byte) 0xFF; + } + + private void loadLandmarkNames() { + + int lmOffset = romEntry.getValue("LandmarkTableOffset"); + int lmBank = bankOf(lmOffset); + int lmCount = romEntry.getValue("LandmarkCount"); + + landmarkNames = new String[lmCount]; + + for (int i = 0; i < lmCount; i++) { + int lmNameOffset = calculateOffset(lmBank, readWord(lmOffset + i + * 4 + 2)); + landmarkNames[i] = readVariableLengthString(lmNameOffset).replace( + "\\x1F", " "); + } + + } + + private void preprocessMaps() { + itemOffs = new ArrayList(); + + int mhOffset = romEntry.getValue("MapHeaders"); + int mgCount = 26; + int mLastGroup = 11; + int mhBank = bankOf(mhOffset); + mapNames = new String[mgCount + 1][100]; + + int[] groupOffsets = new int[mgCount]; + for (int i = 0; i < mgCount; i++) { + groupOffsets[i] = calculateOffset(mhBank, + readWord(mhOffset + i * 2)); + } + + // Read maps + for (int mg = 0; mg < mgCount; mg++) { + int offset = groupOffsets[mg]; + int maxOffset = (mg == mgCount - 1) ? (mhBank + 1) * 0x4000 + : groupOffsets[mg + 1]; + int map = 0; + int maxMap = (mg == mgCount - 1) ? mLastGroup : 999; + while (offset < maxOffset && map < maxMap) { + processMapAt(offset, mg + 1, map + 1); + offset += 9; + map++; + } + } + } + + private void processMapAt(int offset, int mapBank, int mapNumber) { + + // second map header + int smhBank = rom[offset] & 0xFF; + int smhPointer = readWord(offset + 3); + int smhOffset = calculateOffset(smhBank, smhPointer); + + // map name + int mapLandmark = rom[offset + 5] & 0xFF; + mapNames[mapBank][mapNumber] = landmarkNames[mapLandmark]; + + // event header + // event header is in same bank as script header + int ehBank = rom[smhOffset + 6] & 0xFF; + int ehPointer = readWord(smhOffset + 9); + int ehOffset = calculateOffset(ehBank, ehPointer); + + // skip over filler + ehOffset += 2; + + // warps + int warpCount = rom[ehOffset++] & 0xFF; + // warps are skipped + ehOffset += warpCount * 5; + + // xy triggers + int triggerCount = rom[ehOffset++] & 0xFF; + // xy triggers are skipped + ehOffset += triggerCount * 8; + + // signposts + int signpostCount = rom[ehOffset++] & 0xFF; + // we do care about these + for (int sp = 0; sp < signpostCount; sp++) { + // type=7 are hidden items + int spType = rom[ehOffset + sp * 5 + 2] & 0xFF; + if (spType == 7) { + // get event pointer + int spPointer = readWord(ehOffset + sp * 5 + 3); + int spOffset = calculateOffset(ehBank, spPointer); + // item is at spOffset+2 (first two bytes are the flag id) + itemOffs.add(spOffset + 2); + } + } + // now skip past them + ehOffset += signpostCount * 5; + + // visible objects/people + int peopleCount = rom[ehOffset++] & 0xFF; + // we also care about these + for (int p = 0; p < peopleCount; p++) { + // color_function & 1 = 1 if itemball + int pColorFunction = rom[ehOffset + p * 13 + 7]; + if ((pColorFunction & 1) == 1) { + // get event pointer + int pPointer = readWord(ehOffset + p * 13 + 9); + int pOffset = calculateOffset(ehBank, pPointer); + // item is at the pOffset for non-hidden items + itemOffs.add(pOffset); + } + } + + } + + @Override + public List getRequiredFieldTMs() { + return Arrays.asList(new Integer[] { 4, 20, 22, 26, 28, 34, 35, 39, 40, + 43, 44, 46 }); + } + + @Override + public List getCurrentFieldTMs() { + List fieldTMs = new ArrayList(); + + for (int offset : itemOffs) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isTM(itemHere)) { + int thisTM = 0; + if (itemHere >= 191 && itemHere <= 194) { + thisTM = itemHere - 190; // TM block 1 offset + } else if (itemHere >= 196 && itemHere <= 219) { + thisTM = itemHere - 191; // TM block 2 offset + } else { + thisTM = itemHere - 192; // TM block 3 offset + } + // hack for the bug catching contest repeat TM28 + if (fieldTMs.contains(thisTM) == false) { + fieldTMs.add(thisTM); + } + } + } + return fieldTMs; + } + + @Override + public void setFieldTMs(List fieldTMs) { + Iterator iterTMs = fieldTMs.iterator(); + int[] givenTMs = new int[256]; + + for (int offset : itemOffs) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isTM(itemHere)) { + // Cache replaced TMs to duplicate bug catching contest TM + if (givenTMs[itemHere] != 0) { + rom[offset] = (byte) givenTMs[itemHere]; + } else { + // Replace this with a TM from the list + int tm = iterTMs.next(); + if (tm >= 1 && tm <= 4) { + tm += 190; + } else if (tm >= 5 && tm <= 28) { + tm += 191; + } else { + tm += 192; + } + givenTMs[itemHere] = tm; + rom[offset] = (byte) tm; + } + } + } + } + + @Override + public List getRegularFieldItems() { + List fieldItems = new ArrayList(); + + for (int offset : itemOffs) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isAllowed(itemHere) + && !(allowedItems.isTM(itemHere))) { + fieldItems.add(itemHere); + } + } + return fieldItems; + } + + @Override + public void setRegularFieldItems(List items) { + Iterator iterItems = items.iterator(); + + for (int offset : itemOffs) { + int itemHere = rom[offset] & 0xFF; + if (allowedItems.isAllowed(itemHere) + && !(allowedItems.isTM(itemHere))) { + // Replace it + rom[offset] = (byte) (iterItems.next().intValue()); + } + } + + } + + @Override + public List getIngameTrades() { + List trades = new ArrayList(); + + // info + int tableOffset = romEntry.getValue("TradeTableOffset"); + int tableSize = romEntry.getValue("TradeTableSize"); + int nicknameLength = romEntry.getValue("TradeNameLength"); + int otLength = romEntry.getValue("TradeOTLength"); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + int entryLength = nicknameLength + otLength + 10; + + for (int entry = 0; entry < tableSize; entry++) { + if (unusedOffset < unused.length && unused[unusedOffset] == entry) { + unusedOffset++; + continue; + } + IngameTrade trade = new IngameTrade(); + int entryOffset = tableOffset + entry * entryLength; + trade.requestedPokemon = pokes[rom[entryOffset + 1] & 0xFF]; + trade.givenPokemon = pokes[rom[entryOffset + 2] & 0xFF]; + trade.nickname = readString(entryOffset + 3, nicknameLength); + int atkdef = rom[entryOffset + 3 + nicknameLength] & 0xFF; + int spdspc = rom[entryOffset + 4 + nicknameLength] & 0xFF; + trade.ivs = new int[] { (atkdef >> 4) & 0xF, atkdef & 0xF, + (spdspc >> 4) & 0xF, spdspc & 0xF }; + trade.item = rom[entryOffset + 5 + nicknameLength] & 0xFF; + trade.otId = readWord(entryOffset + 6 + nicknameLength); + trade.otName = readString(entryOffset + 8 + nicknameLength, + otLength); + trades.add(trade); + } + + return trades; + + } + + @Override + public void setIngameTrades(List trades) { + // info + int tableOffset = romEntry.getValue("TradeTableOffset"); + int tableSize = romEntry.getValue("TradeTableSize"); + int nicknameLength = romEntry.getValue("TradeNameLength"); + int otLength = romEntry.getValue("TradeOTLength"); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + int entryLength = nicknameLength + otLength + 9; + if (entryLength % 2 != 0) { + entryLength++; + } + int tradeOffset = 0; + + for (int entry = 0; entry < tableSize; entry++) { + if (unusedOffset < unused.length && unused[unusedOffset] == entry) { + unusedOffset++; + continue; + } + IngameTrade trade = trades.get(tradeOffset++); + int entryOffset = tableOffset + entry * entryLength; + rom[entryOffset + 1] = (byte) trade.requestedPokemon.number; + rom[entryOffset + 2] = (byte) trade.givenPokemon.number; + if (romEntry.getValue("CanChangeTrainerText") > 0) { + writeFixedLengthString(trade.nickname, entryOffset + 3, + nicknameLength); + } + rom[entryOffset + 3 + nicknameLength] = (byte) (trade.ivs[0] << 4 | trade.ivs[1]); + rom[entryOffset + 4 + nicknameLength] = (byte) (trade.ivs[2] << 4 | trade.ivs[3]); + rom[entryOffset + 5 + nicknameLength] = (byte) trade.item; + writeWord(entryOffset + 6 + nicknameLength, trade.otId); + if (romEntry.getValue("CanChangeTrainerText") > 0) { + writeFixedLengthString(trade.otName, entryOffset + 8 + + nicknameLength, otLength); + } + // remove gender req + rom[entryOffset + 8 + nicknameLength + otLength] = 0; + + } + } + + @Override + public boolean hasDVs() { + return true; + } + + @Override + public int generationOfPokemon() { + return 2; + } + + @Override + public void removeEvosForPokemonPool() { + List pokemonIncluded = this.mainPokemonList; + List currentEvos = this.getEvolutions(); + List keepEvos = new ArrayList(); + for (Evolution evol : currentEvos) { + if (pokemonIncluded.contains(pokes[evol.from]) + && pokemonIncluded.contains(pokes[evol.to])) { + keepEvos.add(evol); + } + } + writeEvosAndMovesLearnt(keepEvos, null); + } + + private void writeEvosAndMovesLearnt(List evos, + Map> movesets) { + // this assumes that the evo/attack pointers & data + // are at the end of the bank + // which, in every clean G/S/C rom supported, they are + // specify null to either argument to copy old values + int movesEvosStart = romEntry.getValue("PokemonMovesetsTableOffset"); + int movesEvosBank = bankOf(movesEvosStart); + byte[] pointerTable = new byte[251 * 2]; + int startOfNextBank = ((movesEvosStart / 0x4000) + 1) * 0x4000; + int dataBlockSize = startOfNextBank + - (movesEvosStart + pointerTable.length); + int dataBlockOffset = movesEvosStart + pointerTable.length; + byte[] dataBlock = new byte[dataBlockSize]; + int offsetInData = 0; + for (int i = 1; i <= 251; i++) { + // determine pointer + int oldDataOffset = calculateOffset(movesEvosBank, + readWord(movesEvosStart + (i - 1) * 2)); + int offsetStart = dataBlockOffset + offsetInData; + boolean evoWritten = false; + if (evos == null) { + // copy old + int evoOffset = oldDataOffset; + while (rom[evoOffset] != 0x00) { + int method = rom[evoOffset] & 0xFF; + int limiter = (method == 5) ? 4 : 3; + for (int b = 0; b < limiter; b++) { + dataBlock[offsetInData++] = rom[evoOffset++]; + } + evoWritten = true; + } + } else { + for (Evolution evo : evos) { + // write evos + if (evo.from == i) { + // write this one + dataBlock[offsetInData++] = (byte) evo.type.toIndex(2); + if (evo.type == EvolutionType.LEVEL + || evo.type == EvolutionType.STONE + || evo.type == EvolutionType.TRADE_ITEM) { + // simple types + dataBlock[offsetInData++] = (byte) evo.extraInfo; + } else if (evo.type == EvolutionType.TRADE) { + // non-item trade + dataBlock[offsetInData++] = (byte) 0xFF; + } else if (evo.type == EvolutionType.HAPPINESS) { + // cond 01 + dataBlock[offsetInData++] = 0x01; + } else if (evo.type == EvolutionType.HAPPINESS_DAY) { + // cond 02 + dataBlock[offsetInData++] = 0x02; + } else if (evo.type == EvolutionType.HAPPINESS_NIGHT) { + // cond 03 + dataBlock[offsetInData++] = 0x03; + } else if (evo.type == EvolutionType.LEVEL_ATTACK_HIGHER) { + dataBlock[offsetInData++] = (byte) evo.extraInfo; + dataBlock[offsetInData++] = 0x01; + } else if (evo.type == EvolutionType.LEVEL_DEFENSE_HIGHER) { + dataBlock[offsetInData++] = (byte) evo.extraInfo; + dataBlock[offsetInData++] = 0x02; + } else if (evo.type == EvolutionType.LEVEL_ATK_DEF_SAME) { + dataBlock[offsetInData++] = (byte) evo.extraInfo; + dataBlock[offsetInData++] = 0x03; + } + dataBlock[offsetInData++] = (byte) evo.to; + evoWritten = true; + } + } + } + // can we reuse a terminator? + if (!evoWritten && offsetStart != dataBlockOffset) { + // reuse last pokemon's move terminator for our evos + offsetStart -= 1; + } else { + // write a terminator + dataBlock[offsetInData++] = 0x00; + } + // write table entry now that we're sure of its location + int pointerNow = makeGBPointer(offsetStart); + writeWord(pointerTable, (i - 1) * 2, pointerNow); + // moveset + if (movesets == null) { + // copy old + int movesOffset = oldDataOffset; + // move past evos + while (rom[movesOffset] != 0x00) { + int method = rom[movesOffset] & 0xFF; + movesOffset += (method == 5) ? 4 : 3; + } + movesOffset++; + // copy moves + while (rom[movesOffset] != 0x00) { + dataBlock[offsetInData++] = rom[movesOffset++]; + dataBlock[offsetInData++] = rom[movesOffset++]; + } + } else { + List moves = movesets.get(pokes[i]); + for (MoveLearnt ml : moves) { + dataBlock[offsetInData++] = (byte) ml.level; + dataBlock[offsetInData++] = (byte) ml.move; + } + } + // terminator + dataBlock[offsetInData++] = 0x00; + } + // write new data + System.arraycopy(pointerTable, 0, rom, movesEvosStart, + pointerTable.length); + System.arraycopy(dataBlock, 0, rom, dataBlockOffset, dataBlock.length); + } + + @Override + public boolean supportsFourStartingMoves() { + return (romEntry.getValue("SupportsFourStartingMoves") > 0); + } +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/Gen3RomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/Gen3RomHandler.java new file mode 100755 index 000000000..8bed7132f --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/Gen3RomHandler.java @@ -0,0 +1,2674 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- Gen3RomHandler.java - randomizer handler for R/S/E/FR/LG. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.FileNotFoundException; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import com.dabomstew.pkrandom.FileFunctions; +import com.dabomstew.pkrandom.RandomSource; +import com.dabomstew.pkrandom.RomFunctions; +import com.dabomstew.pkrandom.pokemon.Encounter; +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.EvolutionType; +import com.dabomstew.pkrandom.pokemon.ExpCurve; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.ItemList; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.TrainerPokemon; +import com.dabomstew.pkrandom.pokemon.Type; + +public class Gen3RomHandler extends AbstractGBRomHandler { + + private static final int[] hoennToNum = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 290, + 291, 292, 276, 277, 285, 286, 327, 278, 279, 283, 284, 320, 321, + 300, 301, 352, 343, 344, 299, 324, 302, 339, 340, 370, 341, 342, + 349, 350, 318, 319, 328, 329, 330, 296, 297, 309, 310, 322, 323, + 363, 364, 365, 331, 332, 361, 362, 337, 338, 298, 325, 326, 311, + 312, 303, 307, 308, 333, 334, 360, 355, 356, 315, 287, 288, 289, + 316, 317, 357, 293, 294, 295, 366, 367, 368, 359, 353, 354, 336, + 335, 369, 304, 305, 306, 351, 313, 314, 345, 346, 347, 348, 280, + 281, 282, 371, 372, 373, 374, 375, 376, 377, 378, 379, 382, 383, + 384, 380, 381, 385, 386, 358 }; + private static final int[] numToHoenn = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, + 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 279, + 280, 284, 285, 367, 368, 369, 286, 287, 281, 282, 339, 340, 341, + 276, 277, 278, 345, 346, 347, 310, 311, 325, 295, 290, 291, 297, + 330, 357, 358, 359, 331, 332, 312, 313, 328, 329, 361, 362, 338, + 342, 343, 305, 306, 288, 289, 314, 315, 296, 326, 327, 283, 307, + 308, 309, 319, 320, 333, 334, 355, 354, 323, 324, 298, 299, 301, + 302, 293, 294, 363, 364, 365, 366, 303, 304, 360, 292, 352, 353, + 336, 337, 344, 386, 351, 335, 321, 322, 316, 317, 318, 348, 349, + 350, 356, 300, 370, 371, 372, 373, 374, 375, 376, 377, 378, 382, + 383, 379, 380, 381, 384, 385 }; + + private static int pokeNumTo3GIndex(int pokenum) { + if (pokenum < 252) { + return pokenum; + } else { + return numToHoenn[pokenum] + 25; + } + } + + private static int poke3GIndexToNum(int thirdgindex) { + if (thirdgindex < 252) { + return thirdgindex; + } else { + return hoennToNum[thirdgindex - 25]; + } + } + + private static class RomEntry { + private String name; + private String romCode; + private String tableFile; + private int version; + private int romType; + private boolean copyStaticPokemon; + private Map entries = new HashMap(); + private Map arrayEntries = new HashMap(); + private List staticPokemon = new ArrayList(); + + private int getValue(String key) { + if (!entries.containsKey(key)) { + entries.put(key, 0); + } + return entries.get(key); + } + } + + private static List roms; + private static ItemList allowedItems; + + private static final Type[] typeTable = constructTypeTable(); + + static { + loadROMInfo(); + setupAllowedItems(); + } + + private static Type[] constructTypeTable() { + Type[] table = new Type[256]; + table[0x00] = Type.NORMAL; + table[0x01] = Type.FIGHTING; + table[0x02] = Type.FLYING; + table[0x03] = Type.POISON; + table[0x04] = Type.GROUND; + table[0x05] = Type.ROCK; + table[0x06] = Type.BUG; + table[0x07] = Type.GHOST; + table[0x08] = Type.STEEL; + table[0x0A] = Type.FIRE; + table[0x0B] = Type.WATER; + table[0x0C] = Type.GRASS; + table[0x0D] = Type.ELECTRIC; + table[0x0E] = Type.PSYCHIC; + table[0x0F] = Type.ICE; + table[0x10] = Type.DRAGON; + table[0x11] = Type.DARK; + return table; + } + + private static void loadROMInfo() { + roms = new ArrayList(); + RomEntry current = null; + try { + Scanner sc = new Scanner( + FileFunctions.openConfig("gen3_offsets.ini"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine().trim(); + if (q.contains("//")) { + q = q.substring(0, q.indexOf("//")).trim(); + } + if (!q.isEmpty()) { + if (q.startsWith("[") && q.endsWith("]")) { + // New rom + current = new RomEntry(); + current.name = q.substring(1, q.length() - 1); + roms.add(current); + } else { + String[] r = q.split("=", 2); + if (r.length == 1) { + System.err.println("invalid entry " + q); + continue; + } + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + r[1] = r[1].trim(); + // Static Pokemon? + if (r[0].equals("StaticPokemon[]")) { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + current.staticPokemon.add(new StaticPokemon( + offs)); + } else { + int offs = parseRIInt(r[1]); + current.staticPokemon.add(new StaticPokemon( + offs)); + } + } else if (r[0].equals("Game")) { + current.romCode = r[1]; + } else if (r[0].equals("Version")) { + current.version = parseRIInt(r[1]); + } else if (r[0].equals("Type")) { + if (r[1].equalsIgnoreCase("Ruby")) { + current.romType = RomType_Ruby; + } else if (r[1].equalsIgnoreCase("Sapp")) { + current.romType = RomType_Sapp; + } else if (r[1].equalsIgnoreCase("Em")) { + current.romType = RomType_Em; + } else if (r[1].equalsIgnoreCase("FRLG")) { + current.romType = RomType_FRLG; + } else { + System.err.println("unrecognised rom type: " + + r[1]); + } + } else if (r[0].equals("TableFile")) { + current.tableFile = r[1]; + } else if (r[0].equals("CopyStaticPokemon")) { + int csp = parseRIInt(r[1]); + current.copyStaticPokemon = (csp > 0); + } else if (r[0].equals("CopyFrom")) { + for (RomEntry otherEntry : roms) { + if (r[1].equalsIgnoreCase(otherEntry.name)) { + // copy from here + current.arrayEntries + .putAll(otherEntry.arrayEntries); + current.entries.putAll(otherEntry.entries); + if (current.copyStaticPokemon) { + current.staticPokemon + .addAll(otherEntry.staticPokemon); + current.entries.put( + "StaticPokemonSupport", 1); + } else { + current.entries.put( + "StaticPokemonSupport", 0); + } + current.tableFile = otherEntry.tableFile; + } + } + } else { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + if (offsets.length == 1 + && offsets[0].trim().isEmpty()) { + current.arrayEntries.put(r[0], new int[0]); + } else { + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + current.arrayEntries.put(r[0], offs); + } + } else { + int offs = parseRIInt(r[1]); + current.entries.put(r[0], offs); + } + } + } + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + private static int parseRIInt(String off) { + int radix = 10; + off = off.trim().toLowerCase(); + if (off.startsWith("0x") || off.startsWith("&h")) { + radix = 16; + off = off.substring(2); + } + try { + return Integer.parseInt(off, radix); + } catch (NumberFormatException ex) { + System.err.println("invalid base " + radix + "number " + off); + return 0; + } + } + + private void loadTextTable(String filename) { + try { + Scanner sc = new Scanner( + FileFunctions.openConfig(filename + ".tbl"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine(); + if (!q.trim().isEmpty()) { + String[] r = q.split("=", 2); + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + tb[Integer.parseInt(r[0], 16)] = r[1]; + d.put(r[1], (byte) Integer.parseInt(r[0], 16)); + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + private static void setupAllowedItems() { + allowedItems = new ItemList(376); + // Key items (+1 unknown item) + allowedItems.banRange(259, 30); + allowedItems.banRange(349, 28); + // Unknown blank items + allowedItems.banRange(52, 11); + allowedItems.banRange(87, 6); + allowedItems.banRange(99, 4); + allowedItems.banRange(112, 9); + allowedItems.banRange(176, 3); + allowedItems.banRange(226, 28); + allowedItems.banRange(347, 2); + allowedItems.banSingles(72, 82, 105, 267); + // HMs + allowedItems.banRange(339, 8); + // TMs + allowedItems.tmRange(289, 50); + } + + private static byte typeToByte(Type type) { + if (type == null) { + return 0x09; // ???-type + } + switch (type) { + case NORMAL: + return 0x00; + case FIGHTING: + return 0x01; + case FLYING: + return 0x02; + case POISON: + return 0x03; + case GROUND: + return 0x04; + case ROCK: + return 0x05; + case BUG: + return 0x06; + case GHOST: + return 0x07; + case FIRE: + return 0x0A; + case WATER: + return 0x0B; + case GRASS: + return 0x0C; + case ELECTRIC: + return 0x0D; + case PSYCHIC: + return 0x0E; + case ICE: + return 0x0F; + case DRAGON: + return 0x10; + case STEEL: + return 0x08; + case DARK: + return 0x11; + default: + return 0; // normal by default + } + } + + // This ROM's data + private Pokemon[] pokes; + private List pokemonList; + private Move[] moves; + private RomEntry romEntry; + private boolean havePatchedObedience; + public String[] tb; + public Map d; + private String[] abilityNames; + private String[] itemNames; + private boolean mapLoadingDone; + private List itemOffs; + private String[][] mapNames; + + private static final int RomType_Ruby = 0; + private static final int RomType_Sapp = 1; + private static final int RomType_Em = 2; + private static final int RomType_FRLG = 3; + + @Override + public boolean detectRom(byte[] rom) { + if (rom.length != 8388608 && rom.length != 16777216 + && rom.length != 33554432) { + return false; // size check + } + // Special case for Emerald unofficial translation + if (romName(rom, "YJencrypted")) { + // give it a rom code so it can be detected + rom[0xAC] = 'B'; + rom[0xAD] = 'P'; + rom[0xAE] = 'E'; + rom[0xAF] = 'T'; + rom[0xBD] = 0x66; + } + // Wild Pokemon header + if (find(rom, "0348048009E00000FFFF0000") == -1) { + return false; + } + // Map Banks header + if (find(rom, "80180068890B091808687047") == -1) { + return false; + } + for (RomEntry re : roms) { + if (romCode(rom, re.romCode) && (rom[0xBC] & 0xFF) == re.version) { + return true; // match + } + } + return false; // GBA rom we don't support yet + } + + @Override + public void loadedRom() { + for (RomEntry re : roms) { + if (romCode(rom, re.romCode) && (rom[0xBC] & 0xFF) == re.version) { + romEntry = re; + break; + } + } + + tb = new String[256]; + d = new HashMap(); + + // Pokemon names offset + if (romEntry.romType == RomType_Ruby + || romEntry.romType == RomType_Sapp) { + int baseNomOffset = find(rom, "30B50025084CC8F7"); + romEntry.entries + .put("PokemonNames", readPointer(baseNomOffset - 4)); + } else { + romEntry.entries.put("PokemonNames", readPointer(0x144)); + romEntry.entries.put("MoveNames", readPointer(0x148)); + romEntry.entries.put("AbilityNames", readPointer(0x1C0)); + romEntry.entries.put("ItemData", readPointer(0x1C8)); + romEntry.entries.put("MoveData", readPointer(0x1CC) + 0xC); + } + + loadTextTable(romEntry.tableFile); + loadPokemonStats(); + pokemonList = Arrays.asList(pokes); + loadMoves(); + + // Get wild Pokemon offset + int baseWPOffset = findMultiple(rom, "0348048009E00000FFFF0000").get(0); + romEntry.entries.put("WildPokemon", readPointer(baseWPOffset + 12)); + + // map banks + int baseMapsOffset = findMultiple(rom, "80180068890B091808687047").get( + 0); + romEntry.entries.put("MapHeaders", readPointer(baseMapsOffset + 12)); + + // map labels + if (romEntry.romType == RomType_FRLG) { + int baseMLOffset = find(rom, "AC470000AE470000B0470000"); + romEntry.entries.put("MapLabels", readPointer(baseMLOffset + 12)); + } else { + int baseMLOffset = find(rom, "C078288030BC01BC00470000"); + romEntry.entries.put("MapLabels", readPointer(baseMLOffset + 12)); + } + + mapLoadingDone = false; + loadAbilityNames(); + loadItemNames(); + + } + + @Override + public void savingRom() { + savePokemonStats(); + saveMoves(); + } + + private void loadPokemonStats() { + pokes = new Pokemon[387]; + // Fetch our names + String[] pokeNames = readPokemonNames(); + int offs = romEntry.getValue("PokemonStats"); + // Get base stats + for (int i = 1; i <= 386; i++) { + pokes[i] = new Pokemon(); + pokes[i].number = i; + loadBasicPokeStats(pokes[i], offs + (pokeNumTo3GIndex(i) - 1) + * 0x1C); + // Name? + pokes[i].name = pokeNames[pokeNumTo3GIndex(i)]; + } + + } + + private void savePokemonStats() { + // Write pokemon names + int offs = romEntry.getValue("PokemonNames"); + int nameLen = romEntry.getValue("PokemonNameLength"); + for (int i = 1; i <= 386; i++) { + int stringOffset = offs + pokeNumTo3GIndex(i) * nameLen; + writeFixedLengthString(pokes[i].name, stringOffset, nameLen); + } + // Write pokemon stats + int offs2 = romEntry.getValue("PokemonStats"); + for (int i = 1; i <= 386; i++) { + saveBasicPokeStats(pokes[i], offs2 + (pokeNumTo3GIndex(i) - 1) + * 0x1C); + } + } + + private void loadMoves() { + moves = new Move[355]; + int offs = romEntry.getValue("MoveData"); + int nameoffs = romEntry.getValue("MoveNames"); + int namelen = romEntry.getValue("MoveNameLength"); + for (int i = 1; i <= 354; i++) { + moves[i] = new Move(); + moves[i].name = readFixedLengthString(nameoffs + i * namelen, + namelen); + moves[i].number = i; + moves[i].effectIndex = rom[offs + (i - 1) * 0xC] & 0xFF; + moves[i].hitratio = ((rom[offs + (i - 1) * 0xC + 3] & 0xFF) + 0); + moves[i].power = rom[offs + (i - 1) * 0xC + 1] & 0xFF; + moves[i].pp = rom[offs + (i - 1) * 0xC + 4] & 0xFF; + moves[i].type = typeTable[rom[offs + (i - 1) * 0xC + 2]]; + } + + } + + private void saveMoves() { + int offs = romEntry.getValue("MoveData"); + for (int i = 1; i <= 354; i++) { + rom[offs + (i - 1) * 0xC] = (byte) moves[i].effectIndex; + rom[offs + (i - 1) * 0xC + 1] = (byte) moves[i].power; + rom[offs + (i - 1) * 0xC + 2] = typeToByte(moves[i].type); + int hitratio = (int) Math.round(moves[i].hitratio); + if (hitratio < 0) { + hitratio = 0; + } + if (hitratio > 100) { + hitratio = 100; + } + rom[offs + (i - 1) * 0xC + 3] = (byte) hitratio; + rom[offs + (i - 1) * 0xC + 4] = (byte) moves[i].pp; + } + } + + public List getMoves() { + return Arrays.asList(moves); + } + + private void loadBasicPokeStats(Pokemon pkmn, int offset) { + pkmn.hp = rom[offset] & 0xFF; + pkmn.attack = rom[offset + 1] & 0xFF; + pkmn.defense = rom[offset + 2] & 0xFF; + pkmn.speed = rom[offset + 3] & 0xFF; + pkmn.spatk = rom[offset + 4] & 0xFF; + pkmn.spdef = rom[offset + 5] & 0xFF; + // Type + pkmn.primaryType = typeTable[rom[offset + 6] & 0xFF]; + pkmn.secondaryType = typeTable[rom[offset + 7] & 0xFF]; + // Only one type? + if (pkmn.secondaryType == pkmn.primaryType) { + pkmn.secondaryType = null; + } + pkmn.catchRate = rom[offset + 8] & 0xFF; + pkmn.growthCurve = ExpCurve.fromByte(rom[offset + 19]); + // Abilities + pkmn.ability1 = rom[offset + 22] & 0xFF; + pkmn.ability2 = rom[offset + 23] & 0xFF; + + // Held Items? + int item1 = readWord(offset + 12); + int item2 = readWord(offset + 14); + + if (item1 == item2) { + // guaranteed + pkmn.guaranteedHeldItem = item1; + pkmn.commonHeldItem = 0; + pkmn.rareHeldItem = 0; + } else { + pkmn.guaranteedHeldItem = 0; + pkmn.commonHeldItem = item1; + pkmn.rareHeldItem = item2; + } + pkmn.darkGrassHeldItem = -1; + } + + private void saveBasicPokeStats(Pokemon pkmn, int offset) { + rom[offset] = (byte) pkmn.hp; + rom[offset + 1] = (byte) pkmn.attack; + rom[offset + 2] = (byte) pkmn.defense; + rom[offset + 3] = (byte) pkmn.speed; + rom[offset + 4] = (byte) pkmn.spatk; + rom[offset + 5] = (byte) pkmn.spdef; + rom[offset + 6] = typeToByte(pkmn.primaryType); + if (pkmn.secondaryType == null) { + rom[offset + 7] = rom[offset + 6]; + } else { + rom[offset + 7] = typeToByte(pkmn.secondaryType); + } + rom[offset + 8] = (byte) pkmn.catchRate; + rom[offset + 19] = pkmn.growthCurve.toByte(); + + rom[offset + 22] = (byte) pkmn.ability1; + if (pkmn.ability2 == 0) { + // required to not break evos with random ability + rom[offset + 23] = (byte) pkmn.ability1; + } else { + rom[offset + 23] = (byte) pkmn.ability2; + } + + // Held items + if (pkmn.guaranteedHeldItem > 0) { + writeWord(offset + 12, pkmn.guaranteedHeldItem); + writeWord(offset + 14, pkmn.guaranteedHeldItem); + } else { + writeWord(offset + 12, pkmn.commonHeldItem); + writeWord(offset + 14, pkmn.rareHeldItem); + } + } + + private String[] readPokemonNames() { + int offs = romEntry.getValue("PokemonNames"); + int nameLen = romEntry.getValue("PokemonNameLength"); + String[] names = new String[412]; + for (int i = 1; i <= 411; i++) { + names[i] = readFixedLengthString(offs + i * nameLen, nameLen); + } + return names; + } + + private String readString(int offset, int maxLength) { + StringBuilder string = new StringBuilder(); + for (int c = 0; c < maxLength; c++) { + int currChar = rom[offset + c] & 0xFF; + if (tb[currChar] != null) { + string.append(tb[currChar]); + } else { + if (currChar == 0xFF) { + break; + } else if (currChar == 0xFD) { + int nextChar = rom[offset + c + 1] & 0xFF; + string.append("\\v" + String.format("%02X", nextChar)); + c++; + } else { + string.append("\\x" + String.format("%02X", currChar)); + } + } + } + return string.toString(); + } + + private byte[] translateString(String text) { + List data = new ArrayList(); + while (text.length() != 0) { + int i = Math.max(0, 4 - text.length()); + if (text.charAt(0) == '\\' && text.charAt(1) == 'x') { + data.add((byte) Integer.parseInt(text.substring(2, 4), 16)); + text = text.substring(4); + } else if (text.charAt(0) == '\\' && text.charAt(1) == 'v') { + data.add((byte) 0xFD); + data.add((byte) Integer.parseInt(text.substring(2, 4), 16)); + text = text.substring(4); + } else { + while (!(d.containsKey(text.substring(0, 4 - i)) || (i == 4))) { + i++; + } + if (i == 4) { + text = text.substring(1); + } else { + data.add(d.get(text.substring(0, 4 - i))); + text = text.substring(4 - i); + } + } + } + byte[] ret = new byte[data.size()]; + for (int i = 0; i < ret.length; i++) { + ret[i] = data.get(i); + } + return ret; + } + + private String readFixedLengthString(int offset, int length) { + return readString(offset, length); + } + + public String readVariableLengthString(int offset) { + return readString(offset, Integer.MAX_VALUE); + } + + private void writeFixedLengthString(String str, int offset, int length) { + byte[] translated = translateString(str); + int len = Math.min(translated.length, length); + System.arraycopy(translated, 0, rom, offset, len); + if (len < length) { + rom[offset + len] = (byte) 0xFF; + len++; + } + while (len < length) { + rom[offset + len] = 0; + len++; + } + } + + private void writeVariableLengthString(String str, int offset) { + byte[] translated = translateString(str); + System.arraycopy(translated, 0, rom, offset, translated.length); + rom[offset + translated.length] = (byte) 0xFF; + } + + private int lengthOfStringAt(int offset) { + int len = 0; + while ((rom[offset + (len++)] & 0xFF) != 0xFF) { + } + return len - 1; + } + + public byte[] traduire(String str) { + return translateString(str); + } + + private boolean romName(byte[] rom, String name) { + try { + int sigOffset = 0xA0; + byte[] sigBytes = name.getBytes("US-ASCII"); + for (int i = 0; i < sigBytes.length; i++) { + if (rom[sigOffset + i] != sigBytes[i]) { + return false; + } + } + return true; + } catch (UnsupportedEncodingException ex) { + return false; + } + + } + + private boolean romCode(byte[] rom, String codeToCheck) { + try { + int sigOffset = 0xAC; + byte[] sigBytes = codeToCheck.getBytes("US-ASCII"); + for (int i = 0; i < sigBytes.length; i++) { + if (rom[sigOffset + i] != sigBytes[i]) { + return false; + } + } + return true; + } catch (UnsupportedEncodingException ex) { + return false; + } + + } + + private int readPointer(int offset) { + return (rom[offset] & 0xFF) + ((rom[offset + 1] & 0xFF) << 8) + + ((rom[offset + 2] & 0xFF) << 16) + + (((rom[offset + 3] & 0xFF) - 8) << 24); + } + + private void writePointer(int offset, int pointer) { + rom[offset] = (byte) (pointer & 0xFF); + rom[offset + 1] = (byte) ((pointer >> 8) & 0xFF); + rom[offset + 2] = (byte) ((pointer >> 16) & 0xFF); + rom[offset + 3] = (byte) (((pointer >> 24) & 0xFF) + 8); + } + + @Override + public boolean isInGame(Pokemon pkmn) { + return (pkmn.number >= 1 && pkmn.number <= 386); + } + + @Override + public boolean isInGame(int pokemonNumber) { + return (pokemonNumber >= 1 && pokemonNumber <= 386); + } + + @Override + public List getStarters() { + List starters = new ArrayList(); + int baseOffset = romEntry.getValue("StarterPokemon"); + if (romEntry.romType == RomType_Ruby + || romEntry.romType == RomType_Sapp + || romEntry.romType == RomType_Em) { + // do something + Pokemon starter1 = pokes[poke3GIndexToNum(readWord(baseOffset))]; + Pokemon starter2 = pokes[poke3GIndexToNum(readWord(baseOffset + 2))]; + Pokemon starter3 = pokes[poke3GIndexToNum(readWord(baseOffset + 4))]; + starters.add(starter1); + starters.add(starter2); + starters.add(starter3); + } else { + // do something else + Pokemon starter1 = pokes[poke3GIndexToNum(readWord(baseOffset))]; + Pokemon starter2 = pokes[poke3GIndexToNum(readWord(baseOffset + 515))]; + Pokemon starter3 = pokes[poke3GIndexToNum(readWord(baseOffset + 461))]; + starters.add(starter1); + starters.add(starter2); + starters.add(starter3); + } + return starters; + } + + @Override + public boolean setStarters(List newStarters) { + if (newStarters.size() != 3) { + return false; + } + for (Pokemon pkmn : newStarters) { + if (!isInGame(pkmn)) { + return false; + } + } + // Support Deoxys/Mew starters in E/FR/LG + if (!havePatchedObedience) { + attemptObedienceEvolutionPatches(); + } + int baseOffset = romEntry.getValue("StarterPokemon"); + + int starter0 = pokeNumTo3GIndex(newStarters.get(0).number); + int starter1 = pokeNumTo3GIndex(newStarters.get(1).number); + int starter2 = pokeNumTo3GIndex(newStarters.get(2).number); + if (romEntry.romType == RomType_Ruby + || romEntry.romType == RomType_Sapp + || romEntry.romType == RomType_Em) { + + // US + // order: 0, 1, 2 + writeWord(baseOffset, starter0); + writeWord(baseOffset + 2, starter1); + writeWord(baseOffset + 4, starter2); + + } else { + + // frlg: + + // US + // order: 0, 1, 2 + writeWord(baseOffset, starter0); + writeWord(baseOffset + 5, starter1); + + writeWord(baseOffset + 515, starter1); + writeWord(baseOffset + 520, starter2); + + writeWord(baseOffset + 461, starter2); + writeWord(baseOffset + 466, starter0); + + if (romEntry.romCode.charAt(3) != 'J' + && romEntry.romCode.charAt(3) != 'B') { + // Update PROF. Oak's descriptions for each starter + // First result for each STARTERNAME is the text we need + writeFRLGStarterText(pokes[1].name, newStarters.get(0), + "you want to go with\\nthe "); + writeFRLGStarterText(pokes[4].name, newStarters.get(1), + "you’re claiming the\\n"); + writeFRLGStarterText(pokes[7].name, newStarters.get(2), + "you’ve decided on the\\n"); + } + } + return true; + + } + + @Override + public List getStarterHeldItems() { + List sHeldItems = new ArrayList(); + if (romEntry.romType == RomType_FRLG) { + // offset from normal starter offset as a word + int baseOffset = romEntry.getValue("StarterPokemon"); + sHeldItems.add(readWord(baseOffset + 218)); + } else { + int baseOffset = romEntry.getValue("StarterItems"); + int i1 = rom[baseOffset] & 0xFF; + int i2 = rom[baseOffset + 2] & 0xFF; + if (i2 == 0) { + sHeldItems.add(i1); + } else { + sHeldItems.add(i2 + 255); + } + } + return sHeldItems; + } + + @Override + public void setStarterHeldItems(List items) { + if (items.size() != 1) { + return; + } + int item = items.get(0); + if (romEntry.romType == RomType_FRLG) { + // offset from normal starter offset as a word + int baseOffset = romEntry.getValue("StarterPokemon"); + writeWord(baseOffset + 218, item); + } else { + int baseOffset = romEntry.getValue("StarterItems"); + if (item < 256) { + rom[baseOffset] = (byte) item; + rom[baseOffset + 2] = 0; + rom[baseOffset + 3] = 0x32; + } else { + rom[baseOffset] = (byte) 0xFF; + rom[baseOffset + 2] = (byte) (item - 255); + rom[baseOffset + 3] = 0x32; + } + } + } + + private void writeFRLGStarterText(String findName, Pokemon pkmn, + String oakText) { + List foundTexts = RomFunctions.search(rom, traduire(findName)); + if (foundTexts.size() > 0) { + int offset = foundTexts.get(0); + String pokeName = pkmn.name; + String pokeType = pkmn.primaryType.toString(); + if (pokeType.equals("NORMAL") && pkmn.secondaryType != null) { + pokeType = pkmn.secondaryType.toString(); + } + String speech = pokeName + " is your choice.\\pSo, \\v01, " + + oakText + pokeType + " POKéMON " + pokeName + "?"; + writeFixedLengthString(speech, offset, lengthOfStringAt(offset) + 1); + } + } + + @Override + public void shufflePokemonStats() { + for (int i = 1; i <= 386; i++) { + pokes[i].shuffleStats(); + } + } + + @Override + public List getEncounters(boolean useTimeOfDay) { + if (!mapLoadingDone) { + preprocessMaps(); + mapLoadingDone = true; + } + + int startOffs = romEntry.getValue("WildPokemon"); + List encounterAreas = new ArrayList(); + Set seenOffsets = new TreeSet(); + int offs = startOffs; + while (true) { + // Read pointers + int bank = rom[offs] & 0xFF; + int map = rom[offs + 1] & 0xFF; + if (bank == 0xFF && map == 0xFF) { + break; + } + + String mapName = mapNames[bank][map]; + + int grassPokes = readPointer(offs + 4); + int waterPokes = readPointer(offs + 8); + int treePokes = readPointer(offs + 12); + int fishPokes = readPointer(offs + 16); + + // Add pokemanz + if (grassPokes != -134217728 && rom[grassPokes] != 0 + && !seenOffsets.contains(readPointer(grassPokes + 4))) { + encounterAreas.add(readWildArea(grassPokes, 12, mapName + + " Grass/Cave")); + seenOffsets.add(readPointer(grassPokes + 4)); + } + if (waterPokes != -134217728 && rom[waterPokes] != 0 + && !seenOffsets.contains(readPointer(waterPokes + 4))) { + encounterAreas.add(readWildArea(waterPokes, 5, mapName + + " Surfing")); + seenOffsets.add(readPointer(waterPokes + 4)); + } + if (treePokes != -134217728 && rom[treePokes] != 0 + && !seenOffsets.contains(readPointer(treePokes + 4))) { + encounterAreas.add(readWildArea(treePokes, 5, mapName + + " Rock Smash")); + seenOffsets.add(readPointer(treePokes + 4)); + } + if (fishPokes != -134217728 && rom[fishPokes] != 0 + && !seenOffsets.contains(readPointer(fishPokes + 4))) { + encounterAreas.add(readWildArea(fishPokes, 10, mapName + + " Fishing")); + seenOffsets.add(readPointer(fishPokes + 4)); + } + + offs += 20; + } + if (romEntry.arrayEntries.containsKey("BattleTrappersBanned")) { + // Some encounter sets aren't allowed to have Pokemon + // with Arena Trap, Shadow Tag etc. + int[] bannedAreas = romEntry.arrayEntries + .get("BattleTrappersBanned"); + for (int areaIdx : bannedAreas) { + encounterAreas.get(areaIdx).battleTrappersBanned = true; + } + } + return encounterAreas; + } + + private EncounterSet readWildArea(int offset, int numOfEntries, + String setName) { + EncounterSet thisSet = new EncounterSet(); + thisSet.rate = rom[offset]; + thisSet.displayName = setName; + // Grab the *real* pointer to data + int dataOffset = readPointer(offset + 4); + // Read the entries + for (int i = 0; i < numOfEntries; i++) { + // min, max, species, species + Encounter enc = new Encounter(); + enc.level = rom[dataOffset + i * 4]; + enc.maxLevel = rom[dataOffset + i * 4 + 1]; + try { + enc.pokemon = pokes[poke3GIndexToNum(readWord(dataOffset + i + * 4 + 2))]; + } catch (ArrayIndexOutOfBoundsException ex) { + throw ex; + } + thisSet.encounters.add(enc); + } + return thisSet; + } + + @Override + public void setEncounters(boolean useTimeOfDay, + List encounters) { + // Support Deoxys/Mew catches in E/FR/LG + if (!havePatchedObedience) { + attemptObedienceEvolutionPatches(); + } + + int startOffs = romEntry.getValue("WildPokemon"); + Iterator encounterAreas = encounters.iterator(); + Set seenOffsets = new TreeSet(); + int offs = startOffs; + while (true) { + // Read pointers + int bank = rom[offs] & 0xFF; + int map = rom[offs + 1] & 0xFF; + if (bank == 0xFF && map == 0xFF) { + break; + } + + int grassPokes = readPointer(offs + 4); + int waterPokes = readPointer(offs + 8); + int treePokes = readPointer(offs + 12); + int fishPokes = readPointer(offs + 16); + + // Add pokemanz + if (grassPokes != -134217728 && rom[grassPokes] != 0 + && !seenOffsets.contains(readPointer(grassPokes + 4))) { + writeWildArea(grassPokes, 12, encounterAreas.next()); + seenOffsets.add(readPointer(grassPokes + 4)); + } + if (waterPokes != -134217728 && rom[waterPokes] != 0 + && !seenOffsets.contains(readPointer(waterPokes + 4))) { + writeWildArea(waterPokes, 5, encounterAreas.next()); + seenOffsets.add(readPointer(waterPokes + 4)); + } + if (treePokes != -134217728 && rom[treePokes] != 0 + && !seenOffsets.contains(readPointer(treePokes + 4))) { + writeWildArea(treePokes, 5, encounterAreas.next()); + seenOffsets.add(readPointer(treePokes + 4)); + } + if (fishPokes != -134217728 && rom[fishPokes] != 0 + && !seenOffsets.contains(readPointer(fishPokes + 4))) { + writeWildArea(fishPokes, 10, encounterAreas.next()); + seenOffsets.add(readPointer(fishPokes + 4)); + } + + offs += 20; + } + } + + @Override + public List bannedForWildEncounters() { + return Arrays.asList(pokes[201]); // Unown banned + } + + @Override + public List getTrainers() { + int baseOffset = romEntry.getValue("TrainerData"); + int amount = romEntry.getValue("TrainerCount"); + int entryLen = romEntry.getValue("TrainerEntrySize"); + List theTrainers = new ArrayList(); + List tcnames = this.getTrainerClassNames(); + for (int i = 1; i < amount; i++) { + int trOffset = baseOffset + i * entryLen; + Trainer tr = new Trainer(); + tr.offset = trOffset; + int trainerclass = rom[trOffset + 1] & 0xFF; + tr.trainerclass = (rom[trOffset + 2] & 0x80) > 0 ? 1 : 0; + + int pokeDataType = rom[trOffset] & 0xFF; + int numPokes = rom[trOffset + (entryLen - 8)] & 0xFF; + int pointerToPokes = readPointer(trOffset + (entryLen - 4)); + tr.poketype = pokeDataType; + tr.name = this.readVariableLengthString(trOffset + 4); + tr.fullDisplayName = tcnames.get(trainerclass) + " " + tr.name; + // Pokemon data! + if (pokeDataType == 0) { + // blocks of 8 bytes + for (int poke = 0; poke < numPokes; poke++) { + TrainerPokemon thisPoke = new TrainerPokemon(); + thisPoke.AILevel = readWord(pointerToPokes + poke * 8); + thisPoke.level = readWord(pointerToPokes + poke * 8 + 2); + thisPoke.pokemon = pokes[poke3GIndexToNum(readWord(pointerToPokes + + poke * 8 + 4))]; + tr.pokemon.add(thisPoke); + } + } else if (pokeDataType == 2) { + // blocks of 8 bytes + for (int poke = 0; poke < numPokes; poke++) { + TrainerPokemon thisPoke = new TrainerPokemon(); + thisPoke.AILevel = readWord(pointerToPokes + poke * 8); + thisPoke.level = readWord(pointerToPokes + poke * 8 + 2); + thisPoke.pokemon = pokes[poke3GIndexToNum(readWord(pointerToPokes + + poke * 8 + 4))]; + thisPoke.heldItem = readWord(pointerToPokes + poke * 8 + 6); + tr.pokemon.add(thisPoke); + } + } else if (pokeDataType == 1) { + // blocks of 16 bytes + for (int poke = 0; poke < numPokes; poke++) { + TrainerPokemon thisPoke = new TrainerPokemon(); + thisPoke.AILevel = readWord(pointerToPokes + poke * 16); + thisPoke.level = readWord(pointerToPokes + poke * 16 + 2); + thisPoke.pokemon = pokes[poke3GIndexToNum(readWord(pointerToPokes + + poke * 16 + 4))]; + thisPoke.move1 = readWord(pointerToPokes + poke * 16 + 6); + thisPoke.move2 = readWord(pointerToPokes + poke * 16 + 8); + thisPoke.move3 = readWord(pointerToPokes + poke * 16 + 10); + thisPoke.move4 = readWord(pointerToPokes + poke * 16 + 12); + tr.pokemon.add(thisPoke); + } + } else if (pokeDataType == 3) { + // blocks of 16 bytes + for (int poke = 0; poke < numPokes; poke++) { + TrainerPokemon thisPoke = new TrainerPokemon(); + thisPoke.AILevel = readWord(pointerToPokes + poke * 16); + thisPoke.level = readWord(pointerToPokes + poke * 16 + 2); + thisPoke.pokemon = pokes[poke3GIndexToNum(readWord(pointerToPokes + + poke * 16 + 4))]; + thisPoke.heldItem = readWord(pointerToPokes + poke * 16 + 6); + thisPoke.move1 = readWord(pointerToPokes + poke * 16 + 8); + thisPoke.move2 = readWord(pointerToPokes + poke * 16 + 10); + thisPoke.move3 = readWord(pointerToPokes + poke * 16 + 12); + thisPoke.move4 = readWord(pointerToPokes + poke * 16 + 14); + tr.pokemon.add(thisPoke); + } + } + theTrainers.add(tr); + } + + if (romEntry.romType == RomType_Ruby + || romEntry.romType == RomType_Sapp) { + trainerTagsRS(theTrainers); + } else if (romEntry.romType == RomType_Em) { + trainerTagsE(theTrainers); + } else { + trainerTagsFRLG(theTrainers); + } + return theTrainers; + } + + private void trainerTagsRS(List trs) { + // Gym Trainers + tag(trs, "GYM1", 0x140, 0x141); + tag(trs, "GYM2", 0x1AA, 0x1A9, 0xB3); + tag(trs, "GYM3", 0xBF, 0x143, 0xC2, 0x289); + tag(trs, "GYM4", 0xC9, 0x288, 0xCB, 0x28A, 0xCD); + tag(trs, "GYM5", 0x47, 0x59, 0x49, 0x5A, 0x48, 0x5B, 0x4A); + tag(trs, "GYM6", 0x191, 0x28F, 0x28E, 0x194); + tag(trs, "GYM7", 0xE9, 0xEA, 0xEB, 0xF4, 0xF5, 0xF6); + tag(trs, "GYM8", 0x82, 0x266, 0x83, 0x12D, 0x81, 0x74, 0x80, 0x265); + + // Gym Leaders + tag(trs, 0x109, "GYM1"); + tag(trs, 0x10A, "GYM2"); + tag(trs, 0x10B, "GYM3"); + tag(trs, 0x10C, "GYM4"); + tag(trs, 0x10D, "GYM5"); + tag(trs, 0x10E, "GYM6"); + tag(trs, 0x10F, "GYM7"); + tag(trs, 0x110, "GYM8"); + // Elite 4 + tag(trs, 0x105, "ELITE1"); + tag(trs, 0x106, "ELITE2"); + tag(trs, 0x107, "ELITE3"); + tag(trs, 0x108, "ELITE4"); + tag(trs, 0x14F, "CHAMPION"); + // Brendan + tag(trs, 0x208, "RIVAL1-2"); + tag(trs, 0x20B, "RIVAL1-0"); + tag(trs, 0x20E, "RIVAL1-1"); + + tag(trs, 0x209, "RIVAL2-2"); + tag(trs, 0x20C, "RIVAL2-0"); + tag(trs, 0x20F, "RIVAL2-1"); + + tag(trs, 0x20A, "RIVAL3-2"); + tag(trs, 0x20D, "RIVAL3-0"); + tag(trs, 0x210, "RIVAL3-1"); + + tag(trs, 0x295, "RIVAL4-2"); + tag(trs, 0x296, "RIVAL4-0"); + tag(trs, 0x297, "RIVAL4-1"); + + // May + tag(trs, 0x211, "RIVAL1-2"); + tag(trs, 0x214, "RIVAL1-0"); + tag(trs, 0x217, "RIVAL1-1"); + + tag(trs, 0x212, "RIVAL2-2"); + tag(trs, 0x215, "RIVAL2-0"); + tag(trs, 0x218, "RIVAL2-1"); + + tag(trs, 0x213, "RIVAL3-2"); + tag(trs, 0x216, "RIVAL3-0"); + tag(trs, 0x219, "RIVAL3-1"); + + tag(trs, 0x298, "RIVAL4-2"); + tag(trs, 0x299, "RIVAL4-0"); + tag(trs, 0x29A, "RIVAL4-1"); + + if (romEntry.romType == RomType_Ruby) { + tag(trs, "THEMED:MAXIE", 0x259, 0x25A); + tag(trs, "THEMED:COURTNEY", 0x257, 0x258); + tag(trs, "THEMED:TABITHA", 0x254, 0x255); + } else { + tag(trs, "THEMED:ARCHIE", 0x23, 0x22); + tag(trs, "THEMED:MATT", 0x1E, 0x1F); + tag(trs, "THEMED:SHELLY", 0x20, 0x21); + } + + } + + private void trainerTagsE(List trs) { + // Gym Trainers + tag(trs, "GYM1", 0x140, 0x141, 0x23B); + tag(trs, "GYM2", 0x1AA, 0x1A9, 0xB3, 0x23C, 0x23D, 0x23E); + tag(trs, "GYM3", 0xBF, 0x143, 0xC2, 0x289, 0x322); + tag(trs, "GYM4", 0x288, 0xC9, 0xCB, 0x28A, 0xCA, 0xCC, 0x1F5, 0xCD); + tag(trs, "GYM5", 0x47, 0x59, 0x49, 0x5A, 0x48, 0x5B, 0x4A); + tag(trs, "GYM6", 0x192, 0x28F, 0x191, 0x28E, 0x194, 0x323); + tag(trs, "GYM7", 0xE9, 0xEA, 0xEB, 0xF4, 0xF5, 0xF6, 0x24F, 0x248, + 0x247, 0x249, 0x246, 0x23F); + tag(trs, "GYM8", 0x265, 0x80, 0x1F6, 0x73, 0x81, 0x76, 0x82, 0x12D, + 0x83, 0x266); + + // Gym Leaders + Emerald Rematches! + tag(trs, "GYM1", 0x109, 0x302, 0x303, 0x304, 0x305); + tag(trs, "GYM2", 0x10A, 0x306, 0x307, 0x308, 0x309); + tag(trs, "GYM3", 0x10B, 0x30A, 0x30B, 0x30C, 0x30D); + tag(trs, "GYM4", 0x10C, 0x30E, 0x30F, 0x310, 0x311); + tag(trs, "GYM5", 0x10D, 0x312, 0x313, 0x314, 0x315); + tag(trs, "GYM6", 0x10E, 0x316, 0x317, 0x318, 0x319); + tag(trs, "GYM7", 0x10F, 0x31A, 0x31B, 0x31C, 0x31D); + tag(trs, "GYM8", 0x110, 0x31E, 0x31F, 0x320, 0x321); + + // Elite 4 + tag(trs, 0x105, "ELITE1"); + tag(trs, 0x106, "ELITE2"); + tag(trs, 0x107, "ELITE3"); + tag(trs, 0x108, "ELITE4"); + tag(trs, 0x14F, "CHAMPION"); + + // Brendan + tag(trs, 0x208, "RIVAL1-2"); + tag(trs, 0x20B, "RIVAL1-0"); + tag(trs, 0x20E, "RIVAL1-1"); + + tag(trs, 0x251, "RIVAL2-2"); + tag(trs, 0x250, "RIVAL2-0"); + tag(trs, 0x257, "RIVAL2-1"); + + tag(trs, 0x209, "RIVAL3-2"); + tag(trs, 0x20C, "RIVAL3-0"); + tag(trs, 0x20F, "RIVAL3-1"); + + tag(trs, 0x20A, "RIVAL4-2"); + tag(trs, 0x20D, "RIVAL4-0"); + tag(trs, 0x210, "RIVAL4-1"); + + tag(trs, 0x295, "RIVAL5-2"); + tag(trs, 0x296, "RIVAL5-0"); + tag(trs, 0x297, "RIVAL5-1"); + + // May + tag(trs, 0x211, "RIVAL1-2"); + tag(trs, 0x214, "RIVAL1-0"); + tag(trs, 0x217, "RIVAL1-1"); + + tag(trs, 0x258, "RIVAL2-2"); + tag(trs, 0x300, "RIVAL2-0"); + tag(trs, 0x301, "RIVAL2-1"); + + tag(trs, 0x212, "RIVAL3-2"); + tag(trs, 0x215, "RIVAL3-0"); + tag(trs, 0x218, "RIVAL3-1"); + + tag(trs, 0x213, "RIVAL4-2"); + tag(trs, 0x216, "RIVAL4-0"); + tag(trs, 0x219, "RIVAL4-1"); + + tag(trs, 0x298, "RIVAL5-2"); + tag(trs, 0x299, "RIVAL5-0"); + tag(trs, 0x29A, "RIVAL5-1"); + + // Themed + tag(trs, "THEMED:MAXIE", 0x259, 0x25A, 0x2DE); + tag(trs, "THEMED:TABITHA", 0x202, 0x255, 0x2DC); + tag(trs, "THEMED:ARCHIE", 0x22); + tag(trs, "THEMED:MATT", 0x1E); + tag(trs, "THEMED:SHELLY", 0x20, 0x21); + + // Steven + tag(trs, 0x324, "UBER"); + + } + + private void trainerTagsFRLG(List trs) { + + // Gym Trainers + tag(trs, "GYM1", 0x8E); + tag(trs, "GYM2", 0xEA, 0x96); + tag(trs, "GYM3", 0xDC, 0x8D, 0x1A7); + tag(trs, "GYM4", 0x10A, 0x84, 0x109, 0xA0, 0x192, 0x10B, 0x85); + tag(trs, "GYM5", 0x125, 0x124, 0x120, 0x127, 0x126, 0x121); + tag(trs, "GYM6", 0x11A, 0x119, 0x1CF, 0x11B, 0x1CE, 0x1D0, 0x118); + tag(trs, "GYM7", 0xD5, 0xB1, 0xB2, 0xD6, 0xB3, 0xD7, 0xB4); + tag(trs, "GYM8", 0x129, 0x143, 0x188, 0x190, 0x142, 0x128, 0x191, 0x144); + + // Gym Leaders + tag(trs, 0x19E, "GYM1"); + tag(trs, 0x19F, "GYM2"); + tag(trs, 0x1A0, "GYM3"); + tag(trs, 0x1A1, "GYM4"); + tag(trs, 0x1A2, "GYM5"); + tag(trs, 0x1A4, "GYM6"); + tag(trs, 0x1A3, "GYM7"); + tag(trs, 0x15E, "GYM8"); + + // Giovanni + tag(trs, 0x15C, "GIO1"); + tag(trs, 0x15D, "GIO2"); + + // E4 Round 1 + tag(trs, 0x19A, "ELITE1-1"); + tag(trs, 0x19B, "ELITE2-1"); + tag(trs, 0x19C, "ELITE3-1"); + tag(trs, 0x19D, "ELITE4-1"); + + // E4 Round 2 + tag(trs, 0x2DF, "ELITE1-2"); + tag(trs, 0x2E0, "ELITE2-2"); + tag(trs, 0x2E1, "ELITE3-2"); + tag(trs, 0x2E2, "ELITE4-2"); + + // Rival Battles + + // Initial Rival + tag(trs, 0x148, "RIVAL1-0"); + tag(trs, 0x146, "RIVAL1-1"); + tag(trs, 0x147, "RIVAL1-2"); + + // Route 22 (weak) + tag(trs, 0x14B, "RIVAL2-0"); + tag(trs, 0x149, "RIVAL2-1"); + tag(trs, 0x14A, "RIVAL2-2"); + + // Cerulean + tag(trs, 0x14E, "RIVAL3-0"); + tag(trs, 0x14C, "RIVAL3-1"); + tag(trs, 0x14D, "RIVAL3-2"); + + // SS Anne + tag(trs, 0x1AC, "RIVAL4-0"); + tag(trs, 0x1AA, "RIVAL4-1"); + tag(trs, 0x1AB, "RIVAL4-2"); + + // Pokemon Tower + tag(trs, 0x1AF, "RIVAL5-0"); + tag(trs, 0x1AD, "RIVAL5-1"); + tag(trs, 0x1AE, "RIVAL5-2"); + + // Silph Co + tag(trs, 0x1B2, "RIVAL6-0"); + tag(trs, 0x1B0, "RIVAL6-1"); + tag(trs, 0x1B1, "RIVAL6-2"); + + // Route 22 (strong) + tag(trs, 0x1B5, "RIVAL7-0"); + tag(trs, 0x1B3, "RIVAL7-1"); + tag(trs, 0x1B4, "RIVAL7-2"); + + // E4 Round 1 + tag(trs, 0x1B8, "RIVAL8-0"); + tag(trs, 0x1B6, "RIVAL8-1"); + tag(trs, 0x1B7, "RIVAL8-2"); + + // E4 Round 2 + tag(trs, 0x2E5, "RIVAL9-0"); + tag(trs, 0x2E3, "RIVAL9-1"); + tag(trs, 0x2E4, "RIVAL9-2"); + + } + + private void tag(List trainers, int trainerNum, String tag) { + trainers.get(trainerNum - 1).tag = tag; + } + + private void tag(List allTrainers, String tag, int... numbers) { + for (int num : numbers) { + allTrainers.get(num - 1).tag = tag; + } + } + + @Override + public void setTrainers(List trainerData) { + int baseOffset = romEntry.getValue("TrainerData"); + int amount = romEntry.getValue("TrainerCount"); + int entryLen = romEntry.getValue("TrainerEntrySize"); + Iterator theTrainers = trainerData.iterator(); + for (int i = 1; i < amount; i++) { + int trOffset = baseOffset + i * entryLen; + Trainer tr = theTrainers.next(); + // Write out the data as type 0 to avoid moves & hold items carrying + // over + rom[trOffset] = 0; + rom[trOffset + (entryLen - 8)] = (byte) tr.pokemon.size(); + int pointerToPokes = readPointer(trOffset + (entryLen - 4)); + Iterator pokes = tr.pokemon.iterator(); + // Pokemon data! + // if (pokeDataType == 0) { + // blocks of 8 bytes + for (int poke = 0; poke < tr.pokemon.size(); poke++) { + TrainerPokemon thisPoke = pokes.next(); + writeWord(pointerToPokes + poke * 8, thisPoke.AILevel); + writeWord(pointerToPokes + poke * 8 + 2, thisPoke.level); + writeWord(pointerToPokes + poke * 8 + 4, + pokeNumTo3GIndex(thisPoke.pokemon.number)); + writeWord(pointerToPokes + poke * 8 + 6, 0); + } + } + + } + + private void writeWildArea(int offset, int numOfEntries, + EncounterSet encounters) { + // Grab the *real* pointer to data + int dataOffset = readPointer(offset + 4); + // Write the entries + for (int i = 0; i < numOfEntries; i++) { + Encounter enc = encounters.encounters.get(i); + // min, max, species, species + writeWord(dataOffset + i * 4 + 2, + pokeNumTo3GIndex(enc.pokemon.number)); + } + } + + @Override + public List getPokemon() { + return pokemonList; + } + + @Override + public Map> getMovesLearnt() { + Map> movesets = new TreeMap>(); + int baseOffset = romEntry.getValue("PokemonMovesets"); + for (int i = 1; i <= 411; i++) { + int offsToPtr = baseOffset + (i - 1) * 4; + int moveDataLoc = readPointer(offsToPtr); + if (i >= 252 && i <= 276) { + continue; + } + Pokemon pkmn = pokes[poke3GIndexToNum(i)]; + List moves = new ArrayList(); + while ((rom[moveDataLoc] & 0xFF) != 0xFF + || (rom[moveDataLoc + 1] & 0xFF) != 0xFF) { + int move = (rom[moveDataLoc] & 0xFF); + int level = (rom[moveDataLoc + 1] & 0xFE) >> 1; + if ((rom[moveDataLoc + 1] & 0x01) == 0x01) { + move += 256; + } + MoveLearnt ml = new MoveLearnt(); + ml.level = level; + ml.move = move; + moves.add(ml); + moveDataLoc += 2; + } + movesets.put(pkmn, moves); + } + return movesets; + } + + @Override + public void setMovesLearnt(Map> movesets) { + int baseOffset = romEntry.getValue("PokemonMovesets"); + int fso = romEntry.getValue("FreeSpace"); + for (int i = 1; i <= 411; i++) { + int offsToPtr = baseOffset + (i - 1) * 4; + int moveDataLoc = readPointer(offsToPtr); + if (i >= 252 && i <= 276) { + continue; + } + Pokemon pkmn = pokes[poke3GIndexToNum(i)]; + List moves = movesets.get(pkmn); + int mloc = moveDataLoc; + while ((rom[mloc] & 0xFF) != 0xFF || (rom[mloc + 1] & 0xFF) != 0xFF) { + mloc += 2; + } + int currentMoveCount = (mloc - moveDataLoc); + int newMoveCount = moves.size(); + if (currentMoveCount <= newMoveCount) { + int looplimit = Math.min(currentMoveCount, newMoveCount); + for (int mv = 0; mv < looplimit; mv++) { + MoveLearnt ml = moves.get(mv); + rom[moveDataLoc] = (byte) (ml.move & 0xFF); + int levelPart = (ml.level << 1) & 0xFE; + if (ml.move > 255) { + levelPart++; + } + rom[moveDataLoc + 1] = (byte) levelPart; + moveDataLoc += 2; + } + if (looplimit < currentMoveCount) { + // need a new terminator + rom[moveDataLoc] = (byte) 0xFF; + rom[moveDataLoc + 1] = (byte) 0xFF; + } + } else { + // repoint! + int newBytesNeeded = newMoveCount * 2 + 4; + int writeSpace = RomFunctions.freeSpaceFinder(rom, (byte) 0xFF, + newBytesNeeded, fso); + if (writeSpace < fso) { + throw new RuntimeException("ROM is full"); + } + writePointer(offsToPtr, writeSpace); + moveDataLoc = writeSpace; + for (int mv = 0; mv < newMoveCount; mv++) { + MoveLearnt ml = moves.get(mv); + rom[moveDataLoc] = (byte) (ml.move & 0xFF); + int levelPart = (ml.level << 1) & 0xFE; + if (ml.move > 255) { + levelPart++; + } + rom[moveDataLoc + 1] = (byte) levelPart; + moveDataLoc += 2; + } + // need a new terminator + rom[moveDataLoc] = (byte) 0xFF; + rom[moveDataLoc + 1] = (byte) 0xFF; + // for safety (the freespace finder should prevent the + // terminator being overwritten but...) + rom[moveDataLoc + 2] = 0x00; + rom[moveDataLoc + 3] = 0x00; + } + } + + } + + private static class StaticPokemon { + private int[] offsets; + + public StaticPokemon(int... offsets) { + this.offsets = offsets; + } + + public Pokemon getPokemon(Gen3RomHandler parent) { + return parent.pokes[poke3GIndexToNum(parent.readWord(offsets[0]))]; + } + + public void setPokemon(Gen3RomHandler parent, Pokemon pkmn) { + int value = pokeNumTo3GIndex(pkmn.number); + for (int offset : offsets) { + parent.writeWord(offset, value); + } + } + } + + @Override + public List getStaticPokemon() { + List statics = new ArrayList(); + List staticsHere = romEntry.staticPokemon; + for (StaticPokemon staticPK : staticsHere) { + statics.add(staticPK.getPokemon(this)); + } + return statics; + } + + @Override + public boolean setStaticPokemon(List staticPokemon) { + // Support Deoxys/Mew gifts/catches in E/FR/LG + if (!havePatchedObedience) { + attemptObedienceEvolutionPatches(); + } + + List staticsHere = romEntry.staticPokemon; + if (staticPokemon.size() != staticsHere.size()) { + return false; + } + for (Pokemon pkmn : staticPokemon) { + if (!isInGame(pkmn)) { + return false; + } + } + for (int i = 0; i < staticsHere.size(); i++) { + staticsHere.get(i).setPokemon(this, staticPokemon.get(i)); + } + return true; + } + + @Override + public List getTMMoves() { + List tms = new ArrayList(); + int offset = romEntry.getValue("TmMoves"); + for (int i = 1; i <= 50; i++) { + tms.add(readWord(offset + (i - 1) * 2)); + } + return tms; + } + + @Override + public List getHMMoves() { + return Arrays.asList(0xf, 0x13, 0x39, 0x46, 0x94, 0xf9, 0x7f, 0x123); + } + + @Override + public void setTMMoves(List moveIndexes) { + int offset = romEntry.getValue("TmMoves"); + for (int i = 1; i <= 50; i++) { + writeWord(offset + (i - 1) * 2, moveIndexes.get(i - 1)); + } + int otherOffset = romEntry.getValue("TmMovesDuplicate"); + if (otherOffset > 0) { + // Emerald/FR/LG have *two* TM tables + System.arraycopy(rom, offset, rom, otherOffset, 100); + } + + int iiOffset = romEntry.getValue("ItemImages"); + if (iiOffset > 0) { + int[] pals = romEntry.arrayEntries.get("TmPals"); + // Update the item image palettes + // Gen3 TMs are 289-338 + for (int i = 0; i < 50; i++) { + Move mv = moves[moveIndexes.get(i)]; + int typeID = typeToByte(mv.type); + writePointer(iiOffset + (289 + i) * 8 + 4, pals[typeID]); + } + } + + // Item descriptions + if (romEntry.getValue("MoveDescriptions") > 0) { + // JP blocked for now - uses different item structure anyway + int idOffset = romEntry.getValue("ItemData"); + int mdOffset = romEntry.getValue("MoveDescriptions"); + int entrySize = romEntry.getValue("ItemEntrySize"); + int limitPerLine = (romEntry.romType == RomType_FRLG) ? 24 : 18; + for (int i = 0; i < 50; i++) { + int itemBaseOffset = idOffset + (i + 289) * entrySize; + int moveBaseOffset = mdOffset + (moveIndexes.get(i) - 1) * 4; + int moveTextPointer = readPointer(moveBaseOffset); + String moveDesc = readVariableLengthString(moveTextPointer); + String newItemDesc = RomFunctions + .rewriteDescriptionForNewLineSize(moveDesc, "\\n", + limitPerLine, ssd); + // Find freespace + int fsBytesNeeded = translateString(newItemDesc).length + 1; + int newItemDescOffset = RomFunctions.freeSpaceFinder(rom, + (byte) 0xFF, fsBytesNeeded, + romEntry.getValue("FreeSpace")); + if (newItemDescOffset < romEntry.getValue("FreeSpace")) { + String nl = System.getProperty("line.separator"); + log("Couldn't insert new item description." + nl); + return; + } + writeVariableLengthString(newItemDesc, newItemDescOffset); + writePointer(itemBaseOffset + 0x14, newItemDescOffset); + } + } + } + + private RomFunctions.StringSizeDeterminer ssd = new RomFunctions.StringSizeDeterminer() { + + @Override + public int lengthFor(String encodedText) { + return translateString(encodedText).length; + } + }; + + @Override + public int getTMCount() { + return 50; + } + + @Override + public int getHMCount() { + return 8; + } + + @Override + public Map getTMHMCompatibility() { + Map compat = new TreeMap(); + int offset = romEntry.getValue("PokemonTMHMCompat"); + for (int i = 1; i <= 386; i++) { + int compatOffset = offset + (pokeNumTo3GIndex(i) - 1) * 8; + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[59]; + for (int j = 0; j < 8; j++) { + readByteIntoFlags(flags, j * 8 + 1, compatOffset + j); + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setTMHMCompatibility(Map compatData) { + int offset = romEntry.getValue("PokemonTMHMCompat"); + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + int compatOffset = offset + (pokeNumTo3GIndex(pkmn.number) - 1) * 8; + for (int j = 0; j < 8; j++) { + rom[compatOffset + j] = getByteFromFlags(flags, j * 8 + 1); + } + } + } + + @Override + public boolean hasMoveTutors() { + return (romEntry.romType == RomType_Em || romEntry.romType == RomType_FRLG); + } + + @Override + public List getMoveTutorMoves() { + if (!hasMoveTutors()) { + return new ArrayList(); + } + List mts = new ArrayList(); + int moveCount = romEntry.getValue("MoveTutorMoves"); + int offset = romEntry.getValue("MoveTutorData"); + for (int i = 0; i < moveCount; i++) { + mts.add(readWord(offset + i * 2)); + } + return mts; + } + + @Override + public void setMoveTutorMoves(List moves) { + if (!hasMoveTutors()) { + return; + } + int moveCount = romEntry.getValue("MoveTutorMoves"); + int offset = romEntry.getValue("MoveTutorData"); + if (moveCount != moves.size()) { + return; + } + for (int i = 0; i < moveCount; i++) { + writeWord(offset + i * 2, moves.get(i)); + } + } + + @Override + public Map getMoveTutorCompatibility() { + if (!hasMoveTutors()) { + return new TreeMap(); + } + Map compat = new TreeMap(); + int moveCount = romEntry.getValue("MoveTutorMoves"); + int offset = romEntry.getValue("MoveTutorData") + moveCount * 2; + int bytesRequired = ((moveCount + 7) & ~7) / 8; + for (int i = 1; i <= 386; i++) { + int compatOffset = offset + pokeNumTo3GIndex(i) * moveCount; + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[moveCount + 1]; + for (int j = 0; j < bytesRequired; j++) { + readByteIntoFlags(flags, j * 8 + 1, compatOffset + j); + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setMoveTutorCompatibility(Map compatData) { + if (!hasMoveTutors()) { + return; + } + int moveCount = romEntry.getValue("MoveTutorMoves"); + int offset = romEntry.getValue("MoveTutorData") + moveCount * 2; + int bytesRequired = ((moveCount + 7) & ~7) / 8; + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + int compatOffset = offset + pokeNumTo3GIndex(pkmn.number) + * bytesRequired; + for (int j = 0; j < bytesRequired; j++) { + rom[compatOffset + j] = getByteFromFlags(flags, j * 8 + 1); + } + } + } + + @Override + public String getROMName() { + return romEntry.name; + } + + @Override + public String getROMCode() { + return romEntry.romCode; + } + + @Override + public String getSupportLevel() { + return (romEntry.getValue("StaticPokemonSupport") > 0) ? "Complete" + : "No Static Pokemon"; + } + + // For dynamic offsets later + private int find(String hexString) { + return find(rom, hexString); + } + + private int find(byte[] haystack, String hexString) { + if (hexString.length() % 2 != 0) { + return -3; // error + } + byte[] searchFor = new byte[hexString.length() / 2]; + for (int i = 0; i < searchFor.length; i++) { + searchFor[i] = (byte) Integer.parseInt( + hexString.substring(i * 2, i * 2 + 2), 16); + } + List found = RomFunctions.search(haystack, searchFor); + if (found.size() == 0) { + return -1; // not found + } else if (found.size() > 1) { + return -2; // not unique + } else { + return found.get(0); + } + } + + private List findMultiple(String hexString) { + return findMultiple(rom, hexString); + } + + private List findMultiple(byte[] haystack, String hexString) { + if (hexString.length() % 2 != 0) { + return new ArrayList(); // error + } + byte[] searchFor = new byte[hexString.length() / 2]; + for (int i = 0; i < searchFor.length; i++) { + searchFor[i] = (byte) Integer.parseInt( + hexString.substring(i * 2, i * 2 + 2), 16); + } + List found = RomFunctions.search(haystack, searchFor); + return found; + } + + private void writeHexString(String hexString, int offset) { + if (hexString.length() % 2 != 0) { + return; // error + } + for (int i = 0; i < hexString.length() / 2; i++) { + rom[offset + i] = (byte) Integer.parseInt( + hexString.substring(i * 2, i * 2 + 2), 16); + } + } + + private void attemptObedienceEvolutionPatches() { + havePatchedObedience = true; + // This routine *appears* to only exist in E/FR/LG... + // Look for the deoxys part which is + // MOVS R1, 0x19A + // CMP R0, R1 + // BEQ + // Hex is CD214900 8842 0FD0 + int deoxysObOffset = find("CD21490088420FD0"); + if (deoxysObOffset > 0) { + // We found the deoxys check... + // Replacing it with MOVS R1, 0x0 would work fine. + // This would make it so species 0x0 (glitch only) would disobey. + // But MOVS R1, 0x0 (the version I know) is 2-byte + // So we just use it twice... + // the equivalent of nop'ing the second time. + rom[deoxysObOffset] = 0x00; + rom[deoxysObOffset + 1] = 0x21; + rom[deoxysObOffset + 2] = 0x00; + rom[deoxysObOffset + 3] = 0x21; + // Look for the mew check too... it's 0x16 ahead + if (readWord(deoxysObOffset + 0x16) == 0x2897) { + // Bingo, thats CMP R0, 0x97 + // change to CMP R0, 0x0 + writeWord(deoxysObOffset + 0x16, 0x2800); + } + } + + // Look for evolutions too + if (romEntry.romType == RomType_FRLG) { + int evoJumpOffset = find("972814DD"); + if (evoJumpOffset > 0) { + // This currently compares species to 0x97 and then allows + // evolution if it's <= that. + // Allow it regardless by using an unconditional jump instead + writeWord(evoJumpOffset, 0x0000); + writeWord(evoJumpOffset + 2, 0xE014); + } + } + } + + public void patchForNationalDex() { + log("--Patching for National Dex at Start of Game--"); + String nl = System.getProperty("line.separator"); + int fso = romEntry.getValue("FreeSpace"); + if (romEntry.romType == RomType_Ruby + || romEntry.romType == RomType_Sapp) { + // Find the original pokedex script + int pkDexOffset = find("326629010803"); + if (pkDexOffset < 0) { + log("Patch unsuccessful." + nl); + return; + } + int textPointer = readPointer(pkDexOffset - 4); + int realScriptLocation = pkDexOffset - 8; + int pointerLocToScript = find(pointerToHexString(realScriptLocation)); + if (pointerLocToScript < 0) { + log("Patch unsuccessful." + nl); + return; + } + // Find free space for our new routine + int writeSpace = RomFunctions.freeSpaceFinder(rom, (byte) 0xFF, 44, + fso); + if (writeSpace < fso) { + log("Patch unsuccessful." + nl); + // Somehow this ROM is full + return; + } + writePointer(pointerLocToScript, writeSpace); + writeHexString("31720167", writeSpace); + writePointer(writeSpace + 4, textPointer); + writeHexString( + "32662901082B00801102006B02021103016B020211DABE4E020211675A6A02022A008003", + writeSpace + 8); + + } else if (romEntry.romType == RomType_FRLG) { + // Find the original pokedex script + int pkDexOffset = find("292908258101"); + if (pkDexOffset < 0) { + log("Patch unsuccessful." + nl); + return; + } + // Find free space for our new routine + int writeSpace = RomFunctions.freeSpaceFinder(rom, (byte) 0xFF, 10, + fso); + if (writeSpace < fso) { + // Somehow this ROM is full + log("Patch unsuccessful." + nl); + return; + } + rom[pkDexOffset] = 4; + writePointer(pkDexOffset + 1, writeSpace); + rom[pkDexOffset + 5] = 0; // NOP + + // Now write our new routine + writeHexString("292908258101256F0103", writeSpace); + + // Fix people using the national dex flag + List ndexChecks = findMultiple("260D809301210D800100"); + for (int ndexCheckOffset : ndexChecks) { + // change to a flag-check + // 82C = "beaten e4/gary once" + writeHexString("2B2C0800000000000000", ndexCheckOffset); + } + + // Fix oak in his lab + int oakLabCheckOffs = find("257D011604800000260D80D400"); + if (oakLabCheckOffs > 0) { + // replace it + writeHexString("257D011604800100", oakLabCheckOffs); + } + + // Fix oak outside your house + int oakHouseCheckOffs = find("1604800000260D80D4001908800580190980068083000880830109802109803C"); + if (oakHouseCheckOffs > 0) { + // fix him to use ndex count + writeHexString("1604800100", oakHouseCheckOffs); + } + } else { + // Find the original pokedex script + int pkDexOffset = find("3229610825F00129E40816CD40010003"); + if (pkDexOffset < 0) { + log("Patch unsuccessful." + nl); + return; + } + int textPointer = readPointer(pkDexOffset - 4); + int realScriptLocation = pkDexOffset - 8; + int pointerLocToScript = find(pointerToHexString(realScriptLocation)); + if (pointerLocToScript < 0) { + log("Patch unsuccessful." + nl); + return; + } + // Find free space for our new routine + int writeSpace = RomFunctions.freeSpaceFinder(rom, (byte) 0xFF, 27, + fso); + if (writeSpace < fso) { + // Somehow this ROM is full + log("Patch unsuccessful." + nl); + return; + } + writePointer(pointerLocToScript, writeSpace); + writeHexString("31720167", writeSpace); + writePointer(writeSpace + 4, textPointer); + writeHexString("3229610825F00129E40825F30116CD40010003", + writeSpace + 8); + } + log("Patch successful!" + nl); + } + + public String pointerToHexString(int pointer) { + String hex = String.format("%08X", pointer + 0x08000000); + return new String(new char[] { hex.charAt(6), hex.charAt(7), + hex.charAt(4), hex.charAt(5), hex.charAt(2), hex.charAt(3), + hex.charAt(0), hex.charAt(1) }); + } + + @Override + public List getEvolutions() { + int baseOffset = romEntry.getValue("PokemonEvolutions"); + List evos = new ArrayList(); + List evosForThisPoke = new ArrayList(); + for (int i = 1; i <= 386; i++) { + evosForThisPoke.clear(); + int idx = pokeNumTo3GIndex(i); + int evoOffset = baseOffset + (idx - 1) * 0x28; + for (int j = 0; j < 5; j++) { + int method = readWord(evoOffset + j * 8); + int evolvingTo = readWord(evoOffset + j * 8 + 4); + if (method >= 1 && method <= 15 && evolvingTo >= 1 + && evolvingTo <= 411) { + evolvingTo = poke3GIndexToNum(evolvingTo); + int extraInfo = readWord(evoOffset + j * 8 + 2); + EvolutionType et = EvolutionType.fromIndex(3, method); + Evolution evo = new Evolution(i, evolvingTo, true, et, + extraInfo); + if (!evos.contains(evo)) { + evos.add(evo); + evosForThisPoke.add(evo); + } + } + } + // split evos don't carry stats + if (evosForThisPoke.size() > 1) { + for (Evolution e : evosForThisPoke) { + e.carryStats = false; + } + } + } + return evos; + } + + @Override + public void removeTradeEvolutions(boolean changeMoveEvos) { + // no move evos, so no need to check for those + int baseOffset = romEntry.getValue("PokemonEvolutions"); + log("--Removing Trade Evolutions--"); + for (int i = 1; i <= 386; i++) { + int idx = pokeNumTo3GIndex(i); + int evoOffset = baseOffset + (idx - 1) * 0x28; + // Types: + // 1 0 Pokemon 0 (happiness any time) + // 2 0 Pokemon 0 (happiness day) + // 3 0 Pokemon 0 (happiness night) + // 4 LV Pokemon 0 + // 5 0 Pokemon 0 (trade w/o item) + // 6 Item Pokemon 0 (trade w/ item) + // 7 Stone Pokemon 0 + // 8 Level Pokemon 0 (Attack>Defense) + // 9 Level Pokemon 0 (Attack=Defense) + // 10 Level Pokemon 0 (Attack= 5) + // 13 Level Pokemon 0 + // (split evolution level up, pokemon becomes this) + // 14 Level Pokemon 0 + // (split evolution level up, pokemon splits to this) + // 15 Value Pokemon 0 (Level up w/ Beauty >= Value) + for (int j = 0; j < 5; j++) { + int method = readWord(evoOffset + j * 8); + int evolvingTo = poke3GIndexToNum(readWord(evoOffset + j * 8 + + 4)); + // Not trades, but impossible without trading + if (method == 2 && romEntry.romType == RomType_FRLG) { + // happiness day change to Sun Stone + writeWord(evoOffset + j * 8, 7); + writeWord(evoOffset + j * 8 + 2, 93); + logEvoChangeStone(pokes[i].name, pokes[evolvingTo].name, + itemNames[93]); + } + if (method == 3 && romEntry.romType == RomType_FRLG) { + // happiness night change to Moon Stone + writeWord(evoOffset + j * 8, 7); + writeWord(evoOffset + j * 8 + 2, 94); + logEvoChangeStone(pokes[i].name, pokes[evolvingTo].name, + itemNames[94]); + } + if (method == 15 && romEntry.romType == RomType_FRLG) { + // beauty change to level 35 + writeWord(evoOffset + j * 8, 4); + writeWord(evoOffset + j * 8 + 2, 35); + logEvoChangeLevel(pokes[i].name, pokes[evolvingTo].name, 35); + } + // Pure Trade + if (method == 5) { + // Haunter, Machoke, Kadabra, Graveler + // Make it into level 37, we're done. + writeWord(evoOffset + j * 8, 4); + writeWord(evoOffset + j * 8 + 2, 37); + logEvoChangeLevel(pokes[i].name, pokes[evolvingTo].name, 37); + } + // Trade w/ Held Item + if (method == 6) { + if (i == 61) { + // Poliwhirl: Lv 37 + writeWord(evoOffset + j * 8, 4); + writeWord(evoOffset + j * 8 + 2, 37); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 37); + } else if (i == 79) { + // Slowpoke: Water Stone + writeWord(evoOffset + j * 8, 7); + writeWord(evoOffset + j * 8 + 2, 97); + logEvoChangeStone(pokes[i].name, + pokes[evolvingTo].name, itemNames[97]); + } else if (i == 117) { + // Seadra: Lv 40 + writeWord(evoOffset + j * 8, 4); + writeWord(evoOffset + j * 8 + 2, 40); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 40); + } else if (i == 366 && evolvingTo == 367) { + // Clamperl -> Huntail: Lv30 + writeWord(evoOffset + j * 8, 4); + writeWord(evoOffset + j * 8 + 2, 30); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 30); + } else if (i == 366 && evolvingTo == 368) { + // Clamperl -> Gorebyss: Water Stone + writeWord(evoOffset + j * 8, 7); + writeWord(evoOffset + j * 8 + 2, 97); + logEvoChangeStone(pokes[i].name, + pokes[evolvingTo].name, itemNames[97]); + } else { + // Onix, Scyther or Porygon: Lv30 + writeWord(evoOffset + j * 8, 4); + writeWord(evoOffset + j * 8 + 2, 30); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 30); + } + } + } + } + logBlankLine(); + + } + + @Override + public List getTrainerNames() { + int baseOffset = romEntry.getValue("TrainerData"); + int amount = romEntry.getValue("TrainerCount"); + int entryLen = romEntry.getValue("TrainerEntrySize"); + List theTrainers = new ArrayList(); + for (int i = 1; i < amount; i++) { + theTrainers.add(readVariableLengthString(baseOffset + i * entryLen + + 4)); + } + return theTrainers; + } + + @Override + public void setTrainerNames(List trainerNames) { + int baseOffset = romEntry.getValue("TrainerData"); + int amount = romEntry.getValue("TrainerCount"); + int entryLen = romEntry.getValue("TrainerEntrySize"); + int nameLen = romEntry.getValue("TrainerNameLength"); + Iterator theTrainers = trainerNames.iterator(); + for (int i = 1; i < amount; i++) { + String newName = theTrainers.next(); + writeFixedLengthString(newName, baseOffset + i * entryLen + 4, + nameLen); + } + + } + + @Override + public TrainerNameMode trainerNameMode() { + return TrainerNameMode.MAX_LENGTH; + } + + @Override + public List getTCNameLengthsByTrainer() { + // not needed + return new ArrayList(); + } + + @Override + public int maxTrainerNameLength() { + return romEntry.getValue("TrainerNameLength") - 1; + } + + @Override + public List getTrainerClassNames() { + int baseOffset = romEntry.getValue("TrainerClassNames"); + int amount = romEntry.getValue("TrainerClassCount"); + int length = romEntry.getValue("TrainerClassNameLength"); + List trainerClasses = new ArrayList(); + for (int i = 0; i < amount; i++) { + trainerClasses + .add(readVariableLengthString(baseOffset + i * length)); + } + return trainerClasses; + } + + @Override + public void setTrainerClassNames(List trainerClassNames) { + int baseOffset = romEntry.getValue("TrainerClassNames"); + int amount = romEntry.getValue("TrainerClassCount"); + int length = romEntry.getValue("TrainerClassNameLength"); + Iterator trainerClasses = trainerClassNames.iterator(); + for (int i = 0; i < amount; i++) { + writeFixedLengthString(trainerClasses.next(), baseOffset + i + * length, length); + } + } + + @Override + public int maxTrainerClassNameLength() { + return romEntry.getValue("TrainerClassNameLength") - 1; + } + + @Override + public boolean fixedTrainerClassNamesLength() { + return false; + } + + @Override + public boolean canChangeStaticPokemon() { + return (romEntry.getValue("StaticPokemonSupport") > 0); + } + + @Override + public String getDefaultExtension() { + return "gba"; + } + + @Override + public int abilitiesPerPokemon() { + return 2; + } + + @Override + public int highestAbilityIndex() { + return 77; + } + + private void loadAbilityNames() { + int nameoffs = romEntry.getValue("AbilityNames"); + int namelen = romEntry.getValue("AbilityNameLength"); + abilityNames = new String[78]; + for (int i = 0; i <= 77; i++) { + abilityNames[i] = readFixedLengthString(nameoffs + namelen * i, + namelen); + } + } + + @Override + public String abilityName(int number) { + return abilityNames[number]; + } + + @Override + public int internalStringLength(String string) { + return translateString(string).length; + } + + @Override + public void applySignature() { + // FRLG + if (romEntry.romType == RomType_FRLG) { + // intro sprites : first 251 only due to size + int introPokemon = RandomSource.nextInt(251) + 1; + int frontSprites = readPointer(0x128); + int palettes = readPointer(0x130); + + rom[romEntry.getValue("IntroCryOffset")] = (byte) introPokemon; + rom[romEntry.getValue("IntroOtherOffset")] = (byte) introPokemon; + + int spriteBase = romEntry.getValue("IntroSpriteOffset"); + writePointer(spriteBase, frontSprites + introPokemon * 8); + writePointer(spriteBase + 4, palettes + introPokemon * 8); + } else if (romEntry.romType == RomType_Ruby + || romEntry.romType == RomType_Sapp) { + // intro sprites : hoenn pokes only + int introPokemon = pokeNumTo3GIndex(RandomSource.nextInt(135) + 252); + int frontSprites = romEntry.getValue("PokemonFrontSprites"); + int palettes = romEntry.getValue("PokemonNormalPalettes"); + int cryCommand = romEntry.getValue("IntroCryOffset"); + int otherCommand = romEntry.getValue("IntroOtherOffset"); + + if (introPokemon > 255) { + rom[cryCommand] = (byte) 0xFF; + rom[cryCommand + 1] = 0x20; + + rom[cryCommand + 2] = (byte) (introPokemon - 255); + rom[cryCommand + 3] = 0x30; + + rom[otherCommand] = (byte) 0xFF; + rom[otherCommand + 1] = 0x24; + + rom[otherCommand + 2] = (byte) (introPokemon - 255); + rom[otherCommand + 3] = 0x34; + } else { + rom[cryCommand] = (byte) introPokemon; + rom[cryCommand + 1] = 0x20; + + writeWord(cryCommand + 2, 0x46C0); + + rom[otherCommand] = (byte) introPokemon; + rom[otherCommand + 1] = 0x20; + + writeWord(otherCommand + 2, 0x46C0); + } + + writePointer(romEntry.getValue("IntroSpriteOffset"), frontSprites + + introPokemon * 8); + writePointer(romEntry.getValue("IntroPaletteOffset"), palettes + + introPokemon * 8); + } else { + // Emerald, intro sprite: any Pokemon. + int introPokemon = pokeNumTo3GIndex(randomPokemon().number); + writeWord(romEntry.getValue("IntroSpriteOffset"), introPokemon); + writeWord(romEntry.getValue("IntroCryOffset"), introPokemon); + } + + } + + private void preprocessMaps() { + itemOffs = new ArrayList(); + int bankCount = romEntry.getValue("MapBankCount"); + int[] bankMapCounts = romEntry.arrayEntries.get("MapBankSizes"); + int itemBall = romEntry.getValue("ItemBallPic"); + mapNames = new String[bankCount][]; + int mbpsOffset = romEntry.getValue("MapHeaders"); + int mapLabels = romEntry.getValue("MapLabels"); + Map mapLabelsM = new HashMap(); + for (int bank = 0; bank < bankCount; bank++) { + int bankOffset = readPointer(mbpsOffset + bank * 4); + mapNames[bank] = new String[bankMapCounts[bank]]; + for (int map = 0; map < bankMapCounts[bank]; map++) { + int mhOffset = readPointer(bankOffset + map * 4); + + // map name + int mapLabel = rom[mhOffset + 0x14] & 0xFF; + if (mapLabelsM.containsKey(mapLabel)) { + mapNames[bank][map] = mapLabelsM.get(mapLabel); + } else { + if (romEntry.romType == RomType_FRLG) { + mapNames[bank][map] = readVariableLengthString(readPointer(mapLabels + + (mapLabel - 0x58) * 4)); + } else { + mapNames[bank][map] = readVariableLengthString(readPointer(mapLabels + + mapLabel * 8 + 4)); + } + mapLabelsM.put(mapLabel, mapNames[bank][map]); + } + + // events + int eventOffset = readPointer(mhOffset + 4); + + int pCount = rom[eventOffset] & 0xFF; + // int wCount = rom[eventOffset + 1] & 0xFF; + // int scCount = rom[eventOffset + 2] & 0xFF; + int spCount = rom[eventOffset + 3] & 0xFF; + + if (pCount > 0) { + int peopleOffset = readPointer(eventOffset + 4); + for (int p = 0; p < pCount; p++) { + int pSprite = rom[peopleOffset + p * 24 + 1]; + if (pSprite == itemBall + && readPointer(peopleOffset + p * 24 + 16) >= 0) { + // Get script and look inside + int scriptOffset = readPointer(peopleOffset + p + * 24 + 16); + if (rom[scriptOffset] == 0x1A + && rom[scriptOffset + 1] == 0x00 + && (rom[scriptOffset + 2] & 0xFF) == 0x80 + && rom[scriptOffset + 5] == 0x1A + && rom[scriptOffset + 6] == 0x01 + && (rom[scriptOffset + 7] & 0xFF) == 0x80 + && rom[scriptOffset + 10] == 0x09 + && (rom[scriptOffset + 11] == 0x00 || rom[scriptOffset + 11] == 0x01)) { + // item ball script + // int itemHere = readWord(scriptOffset + 3); + itemOffs.add(scriptOffset + 3); + } + } + } + } + + if (spCount > 0) { + int signpostsOffset = readPointer(eventOffset + 16); + for (int sp = 0; sp < spCount; sp++) { + int spType = rom[signpostsOffset + sp * 12 + 5]; + if (spType >= 5 && spType <= 7) { + // hidden item + int itemHere = readWord(signpostsOffset + sp * 12 + + 8); + if (itemHere != 0) { + // itemid 0 is coins + itemOffs.add(signpostsOffset + sp * 12 + 8); + } + } + } + } + } + } + } + + @Override + public ItemList getAllowedItems() { + return allowedItems; + } + + private void loadItemNames() { + int nameoffs = romEntry.getValue("ItemData"); + int structlen = romEntry.getValue("ItemEntrySize"); + int maxcount = romEntry.getValue("ItemCount"); + itemNames = new String[maxcount + 1]; + for (int i = 0; i <= maxcount; i++) { + itemNames[i] = readVariableLengthString(nameoffs + structlen * i); + } + } + + @Override + public String[] getItemNames() { + return itemNames; + } + + @Override + public List getRequiredFieldTMs() { + if (romEntry.romType == RomType_FRLG) { + return Arrays + .asList(new Integer[] { 1, 2, 7, 8, 9, 11, 12, 14, 17, 18, + 21, 22, 25, 32, 36, 37, 40, 41, 44, 46, 47, 48, 49, + 50 }); + } else if (romEntry.romType == RomType_Ruby + || romEntry.romType == RomType_Sapp) { + return Arrays.asList(new Integer[] { 1, 2, 6, 7, 11, 18, 22, 23, + 26, 30, 37, 48 }); + } else { + // emerald has a few TMs from pickup + return Arrays.asList(new Integer[] { 2, 6, 7, 11, 18, 22, 23, 30, + 37, 48 }); + } + } + + @Override + public List getCurrentFieldTMs() { + if (!mapLoadingDone) { + preprocessMaps(); + mapLoadingDone = true; + } + List fieldTMs = new ArrayList(); + + for (int offset : itemOffs) { + int itemHere = readWord(offset); + if (allowedItems.isTM(itemHere)) { + int thisTM = itemHere - 0x120; + // hack for repeat TMs + if (fieldTMs.contains(thisTM) == false) { + fieldTMs.add(thisTM); + } + } + } + return fieldTMs; + } + + @Override + public void setFieldTMs(List fieldTMs) { + if (!mapLoadingDone) { + preprocessMaps(); + mapLoadingDone = true; + } + Iterator iterTMs = fieldTMs.iterator(); + int[] givenTMs = new int[512]; + + for (int offset : itemOffs) { + int itemHere = readWord(offset); + if (allowedItems.isTM(itemHere)) { + // Cache replaced TMs to duplicate repeats + if (givenTMs[itemHere] != 0) { + rom[offset] = (byte) givenTMs[itemHere]; + } else { + // Replace this with a TM from the list + int tm = iterTMs.next(); + tm += 0x120; + givenTMs[itemHere] = tm; + writeWord(offset, tm); + } + } + } + } + + @Override + public List getRegularFieldItems() { + if (!mapLoadingDone) { + preprocessMaps(); + mapLoadingDone = true; + } + List fieldItems = new ArrayList(); + + for (int offset : itemOffs) { + int itemHere = readWord(offset); + if (allowedItems.isAllowed(itemHere) + && !(allowedItems.isTM(itemHere))) { + fieldItems.add(itemHere); + } + } + return fieldItems; + } + + @Override + public void setRegularFieldItems(List items) { + if (!mapLoadingDone) { + preprocessMaps(); + mapLoadingDone = true; + } + Iterator iterItems = items.iterator(); + + for (int offset : itemOffs) { + int itemHere = readWord(offset); + if (allowedItems.isAllowed(itemHere) + && !(allowedItems.isTM(itemHere))) { + // Replace it + writeWord(offset, iterItems.next()); + } + } + + } + + @Override + public List getIngameTrades() { + List trades = new ArrayList(); + + // info + int tableOffset = romEntry.getValue("TradeTableOffset"); + int tableSize = romEntry.getValue("TradeTableSize"); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + int entryLength = 60; + + for (int entry = 0; entry < tableSize; entry++) { + if (unusedOffset < unused.length && unused[unusedOffset] == entry) { + unusedOffset++; + continue; + } + IngameTrade trade = new IngameTrade(); + int entryOffset = tableOffset + entry * entryLength; + trade.nickname = readVariableLengthString(entryOffset); + trade.givenPokemon = pokes[poke3GIndexToNum(readWord(entryOffset + 12))]; + trade.ivs = new int[6]; + for (int i = 0; i < 6; i++) { + trade.ivs[i] = rom[entryOffset + 14 + i] & 0xFF; + } + trade.otId = readWord(entryOffset + 24); + trade.item = readWord(entryOffset + 40); + trade.otName = readVariableLengthString(entryOffset + 43); + trade.requestedPokemon = pokes[poke3GIndexToNum(readWord(entryOffset + 56))]; + trades.add(trade); + } + + return trades; + + } + + @Override + public void setIngameTrades(List trades) { + // info + int tableOffset = romEntry.getValue("TradeTableOffset"); + int tableSize = romEntry.getValue("TradeTableSize"); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + int entryLength = 60; + int tradeOffset = 0; + + for (int entry = 0; entry < tableSize; entry++) { + if (unusedOffset < unused.length && unused[unusedOffset] == entry) { + unusedOffset++; + continue; + } + IngameTrade trade = trades.get(tradeOffset++); + int entryOffset = tableOffset + entry * entryLength; + writeFixedLengthString(trade.nickname, entryOffset, 12); + writeWord(entryOffset + 12, + pokeNumTo3GIndex(trade.givenPokemon.number)); + for (int i = 0; i < 6; i++) { + rom[entryOffset + 14 + i] = (byte) trade.ivs[i]; + } + writeWord(entryOffset + 24, trade.otId); + writeWord(entryOffset + 40, trade.item); + writeFixedLengthString(trade.otName, entryOffset + 43, 11); + writeWord(entryOffset + 56, + pokeNumTo3GIndex(trade.requestedPokemon.number)); + } + } + + @Override + public boolean hasDVs() { + return false; + } + + @Override + public int generationOfPokemon() { + return 3; + } + + @Override + public void removeEvosForPokemonPool() { + List pokemonIncluded = this.mainPokemonList; + int baseOffset = romEntry.getValue("PokemonEvolutions"); + for (int i = 1; i <= 386; i++) { + boolean included = pokemonIncluded.contains(pokes[i]); + int idx = pokeNumTo3GIndex(i); + int evoOffset = baseOffset + (idx - 1) * 0x28; + for (int j = 0; j < 5; j++) { + int method = readWord(evoOffset + j * 8); + int evolvingTo = readWord(evoOffset + j * 8 + 4); + if (method >= 1 && method <= 15 && evolvingTo >= 1 + && evolvingTo <= 411) { + Pokemon evolvingInto = pokes[poke3GIndexToNum(evolvingTo)]; + if (!included || !pokemonIncluded.contains(evolvingInto)) { + // remove this evolution + writeWord(evoOffset + j * 8, 0); + writeWord(evoOffset + j * 8 + 2, 0); + writeWord(evoOffset + j * 8 + 4, 0); + } + } + } + } + } + + @Override + public boolean supportsFourStartingMoves() { + return true; + } +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/Gen4RomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/Gen4RomHandler.java new file mode 100755 index 000000000..f5d7ea9ef --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/Gen4RomHandler.java @@ -0,0 +1,2808 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- Gen4RomHandler.java - randomizer handler for D/P/Pt/HG/SS. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.TreeMap; + +import thenewpoketext.PokeTextData; +import thenewpoketext.TextToPoke; + +import com.dabomstew.pkrandom.FileFunctions; +import com.dabomstew.pkrandom.RandomSource; +import com.dabomstew.pkrandom.RomFunctions; +import com.dabomstew.pkrandom.pokemon.Encounter; +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.EvolutionType; +import com.dabomstew.pkrandom.pokemon.ExpCurve; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.ItemList; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.TrainerPokemon; +import com.dabomstew.pkrandom.pokemon.Type; + +public class Gen4RomHandler extends AbstractDSRomHandler { + + // Statics + private static final Type[] typeTable = constructTypeTable(); + + private static Type[] constructTypeTable() { + Type[] table = new Type[256]; + table[0x00] = Type.NORMAL; + table[0x01] = Type.FIGHTING; + table[0x02] = Type.FLYING; + table[0x03] = Type.POISON; + table[0x04] = Type.GROUND; + table[0x05] = Type.ROCK; + table[0x06] = Type.BUG; + table[0x07] = Type.GHOST; + table[0x08] = Type.STEEL; + table[0x0A] = Type.FIRE; + table[0x0B] = Type.WATER; + table[0x0C] = Type.GRASS; + table[0x0D] = Type.ELECTRIC; + table[0x0E] = Type.PSYCHIC; + table[0x0F] = Type.ICE; + table[0x10] = Type.DRAGON; + table[0x11] = Type.DARK; + return table; + } + + private static byte typeToByte(Type type) { + if (type == null) { + return 0x09; // ???-type + } + switch (type) { + case NORMAL: + return 0x00; + case FIGHTING: + return 0x01; + case FLYING: + return 0x02; + case POISON: + return 0x03; + case GROUND: + return 0x04; + case ROCK: + return 0x05; + case BUG: + return 0x06; + case GHOST: + return 0x07; + case FIRE: + return 0x0A; + case WATER: + return 0x0B; + case GRASS: + return 0x0C; + case ELECTRIC: + return 0x0D; + case PSYCHIC: + return 0x0E; + case ICE: + return 0x0F; + case DRAGON: + return 0x10; + case STEEL: + return 0x08; + case DARK: + return 0x11; + default: + return 0; // normal by default + } + } + + private static class RomEntry { + private String name; + private String romCode; + private int romType; + private boolean staticPokemonSupport = false, + copyStaticPokemon = false; + private Map strings = new HashMap(); + private Map numbers = new HashMap(); + private Map arrayEntries = new HashMap(); + private List staticPokemon = new ArrayList(); + + private int getInt(String key) { + if (!numbers.containsKey(key)) { + numbers.put(key, 0); + } + return numbers.get(key); + } + + private String getString(String key) { + if (!strings.containsKey(key)) { + strings.put(key, ""); + } + return strings.get(key); + } + } + + private static List roms; + private static ItemList allowedItems; + + static { + loadROMInfo(); + setupAllowedItems(); + } + + private static void loadROMInfo() { + roms = new ArrayList(); + RomEntry current = null; + try { + Scanner sc = new Scanner( + FileFunctions.openConfig("gen4_offsets.ini"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine().trim(); + if (q.contains("//")) { + q = q.substring(0, q.indexOf("//")).trim(); + } + if (!q.isEmpty()) { + if (q.startsWith("[") && q.endsWith("]")) { + // New rom + current = new RomEntry(); + current.name = q.substring(1, q.length() - 1); + roms.add(current); + } else { + String[] r = q.split("=", 2); + if (r.length == 1) { + System.err.println("invalid entry " + q); + continue; + } + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + r[1] = r[1].trim(); + if (r[0].equals("Game")) { + current.romCode = r[1]; + } else if (r[0].equals("Type")) { + if (r[1].equalsIgnoreCase("DP")) { + current.romType = Type_DP; + } else if (r[1].equalsIgnoreCase("Plat")) { + current.romType = Type_Plat; + } else if (r[1].equalsIgnoreCase("HGSS")) { + current.romType = Type_HGSS; + } else { + System.err.println("unrecognised rom type: " + + r[1]); + } + } else if (r[0].equals("CopyFrom")) { + for (RomEntry otherEntry : roms) { + if (r[1].equalsIgnoreCase(otherEntry.romCode)) { + // copy from here + current.arrayEntries + .putAll(otherEntry.arrayEntries); + current.numbers.putAll(otherEntry.numbers); + current.strings.putAll(otherEntry.strings); + if (current.copyStaticPokemon) { + current.staticPokemon + .addAll(otherEntry.staticPokemon); + current.staticPokemonSupport = true; + } else { + current.staticPokemonSupport = false; + } + } + } + } else if (r[0].equals("StaticPokemon[]")) { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + int[] offs = new int[offsets.length]; + int[] files = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + String[] parts = off.split("\\:"); + files[c] = parseRIInt(parts[0]); + offs[c++] = parseRIInt(parts[1]); + } + StaticPokemon sp = new StaticPokemon(); + sp.files = files; + sp.offsets = offs; + current.staticPokemon.add(sp); + } else { + String[] parts = r[1].split("\\:"); + int files = parseRIInt(parts[0]); + int offs = parseRIInt(parts[1]); + StaticPokemon sp = new StaticPokemon(); + sp.files = new int[] { files }; + sp.offsets = new int[] { offs }; + } + } else if (r[0].equals("StaticPokemonSupport")) { + int spsupport = parseRIInt(r[1]); + current.staticPokemonSupport = (spsupport > 0); + } else if (r[0].equals("CopyStaticPokemon")) { + int csp = parseRIInt(r[1]); + current.copyStaticPokemon = (csp > 0); + } else { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + if (offsets.length == 1 + && offsets[0].trim().isEmpty()) { + current.arrayEntries.put(r[0], new int[0]); + } else { + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + current.arrayEntries.put(r[0], offs); + } + } else if (r[0].endsWith("Offset") + || r[0].endsWith("Count") + || r[0].endsWith("Number")) { + int offs = parseRIInt(r[1]); + current.numbers.put(r[0], offs); + } else { + current.strings.put(r[0], r[1]); + } + } + } + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + private static int parseRIInt(String off) { + int radix = 10; + off = off.trim().toLowerCase(); + if (off.startsWith("0x") || off.startsWith("&h")) { + radix = 16; + off = off.substring(2); + } + try { + return Integer.parseInt(off, radix); + } catch (NumberFormatException ex) { + System.err.println("invalid base " + radix + "number " + off); + return 0; + } + } + + private static void setupAllowedItems() { + allowedItems = new ItemList(536); + // Key items + version exclusives + allowedItems.banRange(428, 109); + // Unknown blank items or version exclusives + allowedItems.banRange(112, 23); + // HMs + allowedItems.banRange(420, 8); + // TMs + allowedItems.tmRange(328, 92); + } + + // This rom + private Pokemon[] pokes; + private List pokemonList; + private Move[] moves; + private NARCContents pokeNarc, moveNarc; + private NARCContents msgNarc; + private NARCContents scriptNarc; + private NARCContents eventNarc; + private byte[] arm9; + private List abilityNames; + private List itemNames; + + private RomEntry romEntry; + + private static final int Type_DP = 0; + private static final int Type_Plat = 1; + private static final int Type_HGSS = 2; + + @Override + protected boolean detectNDSRom(String ndsCode) { + for (RomEntry re : roms) { + if (ndsCode.equals(re.romCode)) { + this.romEntry = re; + return true; // match + } + } + return false; + } + + @Override + protected void loadedROM() { + try { + arm9 = readARM9(); + } catch (IOException e) { + arm9 = new byte[0]; + } + try { + msgNarc = readNARC(romEntry.getString("Text")); + } catch (IOException e) { + msgNarc = null; + } + try { + scriptNarc = readNARC(romEntry.getString("Scripts")); + } catch (IOException e) { + scriptNarc = null; + } + try { + eventNarc = readNARC(romEntry.getString("Events")); + } catch (IOException e) { + eventNarc = null; + } + loadPokemonStats(); + pokemonList = Arrays.asList(pokes); + loadMoves(); + abilityNames = getStrings(romEntry.getInt("AbilityNamesTextOffset")); + itemNames = getStrings(romEntry.getInt("ItemNamesTextOffset")); + + } + + private void loadMoves() { + try { + moveNarc = this.readNARC(romEntry.getString("MoveData")); + moves = new Move[468]; + List moveNames = getStrings(romEntry + .getInt("MoveNamesTextOffset")); + for (int i = 1; i <= 467; i++) { + byte[] moveData = moveNarc.files.get(i); + moves[i] = new Move(); + moves[i].name = moveNames.get(i); + moves[i].number = i; + moves[i].effectIndex = readWord(moveData, 0); + moves[i].hitratio = (moveData[5] & 0xFF); + moves[i].power = moveData[3] & 0xFF; + moves[i].pp = moveData[6] & 0xFF; + moves[i].type = typeTable[moveData[4] & 0xFF]; + } + } catch (IOException e) { + // change this later + e.printStackTrace(); + } + + } + + private void loadPokemonStats() { + try { + String pstatsnarc = romEntry.getString("PokemonStats"); + pokeNarc = this.readNARC(pstatsnarc); + String[] pokeNames = readPokemonNames(); + pokes = new Pokemon[494]; + for (int i = 1; i <= 493; i++) { + pokes[i] = new Pokemon(); + pokes[i].number = i; + loadBasicPokeStats(pokes[i], pokeNarc.files.get(i)); + // Name? + pokes[i].name = pokeNames[i]; + } + } catch (IOException e) { + // change this later + e.printStackTrace(); + } + + } + + private void loadBasicPokeStats(Pokemon pkmn, byte[] stats) { + pkmn.hp = stats[0] & 0xFF; + pkmn.attack = stats[1] & 0xFF; + pkmn.defense = stats[2] & 0xFF; + pkmn.speed = stats[3] & 0xFF; + pkmn.spatk = stats[4] & 0xFF; + pkmn.spdef = stats[5] & 0xFF; + // Type + pkmn.primaryType = typeTable[stats[6] & 0xFF]; + pkmn.secondaryType = typeTable[stats[7] & 0xFF]; + // Only one type? + if (pkmn.secondaryType == pkmn.primaryType) { + pkmn.secondaryType = null; + } + pkmn.catchRate = stats[8] & 0xFF; + pkmn.growthCurve = ExpCurve.fromByte(stats[19]); + + // Abilities + pkmn.ability1 = stats[22] & 0xFF; + pkmn.ability2 = stats[23] & 0xFF; + + // Held Items? + int item1 = readWord(stats, 12); + int item2 = readWord(stats, 14); + + if (item1 == item2) { + // guaranteed + pkmn.guaranteedHeldItem = item1; + pkmn.commonHeldItem = 0; + pkmn.rareHeldItem = 0; + } else { + pkmn.guaranteedHeldItem = 0; + pkmn.commonHeldItem = item1; + pkmn.rareHeldItem = item2; + } + pkmn.darkGrassHeldItem = -1; + } + + private String[] readPokemonNames() { + String[] pokeNames = new String[494]; + List nameList = getStrings(romEntry + .getInt("PokemonNamesTextOffset")); + for (int i = 1; i <= 493; i++) { + pokeNames[i] = nameList.get(i); + } + return pokeNames; + } + + @Override + protected void savingROM() { + savePokemonStats(); + saveMoves(); + try { + writeARM9(arm9); + } catch (IOException e) { + } + try { + writeNARC(romEntry.getString("Text"), msgNarc); + } catch (IOException e) { + } + try { + writeNARC(romEntry.getString("Scripts"), scriptNarc); + } catch (IOException e) { + } + try { + writeNARC(romEntry.getString("Events"), eventNarc); + } catch (IOException e) { + } + } + + private void saveMoves() { + for (int i = 1; i <= 467; i++) { + byte[] data = moveNarc.files.get(i); + writeWord(data, 0, moves[i].effectIndex); + data[3] = (byte) moves[i].power; + data[4] = typeToByte(moves[i].type); + int hitratio = (int) Math.round(moves[i].hitratio); + if (hitratio < 0) { + hitratio = 0; + } + if (hitratio > 100) { + hitratio = 100; + } + data[5] = (byte) hitratio; + data[6] = (byte) moves[i].pp; + } + + try { + this.writeNARC(romEntry.getString("MoveData"), moveNarc); + } catch (IOException e) { + // // change this later + e.printStackTrace(); + } + + } + + private void savePokemonStats() { + // Update the "a/an X" list too, if it exists + List namesList = getStrings(romEntry + .getInt("PokemonNamesTextOffset")); + if (romEntry.getString("HasExtraPokemonNames").equalsIgnoreCase("Yes")) { + List namesList2 = getStrings(romEntry + .getInt("PokemonNamesTextOffset") + 1); + for (int i = 1; i <= 493; i++) { + saveBasicPokeStats(pokes[i], pokeNarc.files.get(i)); + String oldName = namesList.get(i); + namesList.set(i, pokes[i].name); + namesList2.set(i, + namesList2.get(i).replace(oldName, pokes[i].name)); + } + setStrings(romEntry.getInt("PokemonNamesTextOffset") + 1, + namesList2, false); + } else { + for (int i = 1; i <= 493; i++) { + saveBasicPokeStats(pokes[i], pokeNarc.files.get(i)); + namesList.set(i, pokes[i].name); + } + } + setStrings(romEntry.getInt("PokemonNamesTextOffset"), namesList, false); + + try { + String pstatsnarc = romEntry.getString("PokemonStats"); + this.writeNARC(pstatsnarc, pokeNarc); + } catch (IOException e) { + // change this later + e.printStackTrace(); + } + + } + + private void saveBasicPokeStats(Pokemon pkmn, byte[] stats) { + stats[0] = (byte) pkmn.hp; + stats[1] = (byte) pkmn.attack; + stats[2] = (byte) pkmn.defense; + stats[3] = (byte) pkmn.speed; + stats[4] = (byte) pkmn.spatk; + stats[5] = (byte) pkmn.spdef; + stats[6] = typeToByte(pkmn.primaryType); + if (pkmn.secondaryType == null) { + stats[7] = stats[6]; + } else { + stats[7] = typeToByte(pkmn.secondaryType); + } + stats[8] = (byte) pkmn.catchRate; + stats[19] = pkmn.growthCurve.toByte(); + + stats[22] = (byte) pkmn.ability1; + stats[23] = (byte) pkmn.ability2; + + // Held items + if (pkmn.guaranteedHeldItem > 0) { + writeWord(stats, 12, pkmn.guaranteedHeldItem); + writeWord(stats, 14, pkmn.guaranteedHeldItem); + } else { + writeWord(stats, 12, pkmn.commonHeldItem); + writeWord(stats, 14, pkmn.rareHeldItem); + } + } + + @Override + public boolean isInGame(Pokemon pkmn) { + return isInGame(pkmn.number); + } + + @Override + public boolean isInGame(int pokemonNumber) { + return pokemonNumber >= 1 && pokemonNumber <= 493; + } + + @Override + public List getPokemon() { + return pokemonList; + } + + @Override + public List getStarters() { + if (romEntry.romType == Type_HGSS) { + List tailOffsets = RomFunctions.search(arm9, new byte[] { + 0x03, 0x03, 0x1A, 0x12, 0x1, 0x23, 0x0, 0x0 }); + if (tailOffsets.size() == 1) { + // Found starters + int starterOffset = tailOffsets.get(0) - 13; + int poke1 = readWord(arm9, starterOffset); + int poke2 = readWord(arm9, starterOffset + 4); + int poke3 = readWord(arm9, starterOffset + 8); + return Arrays.asList(pokes[poke1], pokes[poke2], pokes[poke3]); + } else { + return Arrays.asList(pokes[152], pokes[155], pokes[158]); + } + } else { + try { + byte[] starterData = readOverlay(romEntry + .getInt("StarterPokemonOvlNumber")); + int poke1 = readWord(starterData, + romEntry.getInt("StarterPokemonOffset")); + int poke2 = readWord(starterData, + romEntry.getInt("StarterPokemonOffset") + 4); + int poke3 = readWord(starterData, + romEntry.getInt("StarterPokemonOffset") + 8); + return Arrays.asList(pokes[poke1], pokes[poke2], pokes[poke3]); + } catch (IOException e) { + return Arrays.asList(pokes[387], pokes[390], pokes[393]); + } + } + } + + @Override + public boolean setStarters(List newStarters) { + if (newStarters.size() != 3) { + return false; + } + for (Pokemon pkmn : newStarters) { + if (!isInGame(pkmn)) { + return false; + } + } + if (romEntry.romType == Type_HGSS) { + List tailOffsets = RomFunctions.search(arm9, new byte[] { + 0x03, 0x03, 0x1A, 0x12, 0x1, 0x23, 0x0, 0x0 }); + if (tailOffsets.size() == 1) { + // Found starters + int starterOffset = tailOffsets.get(0) - 13; + writeWord(arm9, starterOffset, newStarters.get(0).number); + writeWord(arm9, starterOffset + 4, newStarters.get(1).number); + writeWord(arm9, starterOffset + 8, newStarters.get(2).number); + // Go fix the rival scripts, which rely on fixed pokemon numbers + // The logic to be changed each time is roughly: + // Set 0x800C = player starter + // If(0x800C==152) { trainerbattle rival w/ cynda } + // ElseIf(0x800C==155) { trainerbattle rival w/ totodile } + // Else { trainerbattle rival w/ chiko } + // So we basically have to adjust the 152 and the 155. + int[] filesWithRivalScript = new int[] { 7, 23, 96, 110, 819, + 850, 866 }; + // below code represents a rival script for sure + // it means: StoreStarter2 0x800C; If 0x800C 152; CheckLR B_!= + // + byte[] magic = new byte[] { (byte) 0xCE, 0x00, 0x0C, + (byte) 0x80, 0x11, 0x00, 0x0C, (byte) 0x80, (byte) 152, + 0, 0x1C, 0x00, 0x05 }; + NARCContents scriptNARC = scriptNarc; + for (int i = 0; i < filesWithRivalScript.length; i++) { + int fileCheck = filesWithRivalScript[i]; + byte[] file = scriptNARC.files.get(fileCheck); + List rivalOffsets = RomFunctions.search(file, + magic); + if (rivalOffsets.size() == 1) { + // found, adjust + int baseOffset = rivalOffsets.get(0); + // Replace 152 (chiko) with first starter + writeWord(file, baseOffset + 8, + newStarters.get(0).number); + int jumpAmount = readLong(file, baseOffset + 13); + int secondBase = jumpAmount + baseOffset + 17; + if (file[secondBase] != 0x11 + || (file[secondBase + 4] & 0xFF) != 155) { + // This isn't what we were expecting... + } else { + // Replace 155 (cynda) with 2nd starter + writeWord(file, secondBase + 4, + newStarters.get(1).number); + } + } + } + // Fix starter text + List spStrings = getStrings(romEntry + .getInt("StarterScreenTextOffset")); + String[] intros = new String[] { "So, you like", "You’ll take", + "Do you want" }; + for (int i = 0; i < 3; i++) { + Pokemon newStarter = newStarters.get(i); + int color = (i == 0) ? 3 : i; + String newStarterDesc = "Professor Elm: " + intros[i] + + " \\vFF00\\z000" + color + newStarter.name + + "\\vFF00\\z0000,\\nthe " + + newStarter.primaryType.camelCase() + + "-type Pokémon?"; + spStrings.set(i + 1, newStarterDesc); + String altStarterDesc = "\\vFF00\\z000" + color + + newStarter.name + "\\vFF00\\z0000, the " + + newStarter.primaryType.camelCase() + + "-type Pokémon, is\\nin this Poké Ball!"; + spStrings.set(i + 4, altStarterDesc); + } + setStrings(romEntry.getInt("StarterScreenTextOffset"), + spStrings); + return true; + } else { + return false; + } + } else { + try { + byte[] starterData = readOverlay(romEntry + .getInt("StarterPokemonOvlNumber")); + writeWord(starterData, romEntry.getInt("StarterPokemonOffset"), + newStarters.get(0).number); + writeWord(starterData, + romEntry.getInt("StarterPokemonOffset") + 4, + newStarters.get(1).number); + writeWord(starterData, + romEntry.getInt("StarterPokemonOffset") + 8, + newStarters.get(2).number); + writeOverlay(romEntry.getInt("StarterPokemonOvlNumber"), + starterData); + // Patch DPPt-style rival scripts + // these have a series of IfJump commands + // following pokemon IDs + // the jumps either go to trainer battles, or a HoF times + // checker, or the StarterBattle command (Pt only) + // the HoF times checker case is for the Fight Area or Survival + // Area (depending on version). + // the StarterBattle case is for Route 201 in Pt. + int[] filesWithRivalScript = (romEntry.romType == Type_Plat) ? new int[] { + 31, 36, 112, 123, 186, 427, 429, 1096 } + : new int[] { 34, 90, 118, 180, 195, 394 }; + byte[] magic = new byte[] { (byte) 0xDE, 0x00, 0x0C, + (byte) 0x80, 0x11, 0x00, 0x0C, (byte) 0x80, + (byte) 0x83, 0x01, 0x1C, 0x00, 0x01 }; + NARCContents scriptNARC = scriptNarc; + for (int i = 0; i < filesWithRivalScript.length; i++) { + int fileCheck = filesWithRivalScript[i]; + byte[] file = scriptNARC.files.get(fileCheck); + List rivalOffsets = RomFunctions.search(file, + magic); + if (rivalOffsets.size() > 0) { + for (int baseOffset : rivalOffsets) { + // found, check for trainer battle or HoF + // check at jump + int jumpLoc = baseOffset + magic.length; + int jumpTo = readLong(file, jumpLoc) + jumpLoc + 4; + if (readWord(file, jumpTo) != 0xE5 + && readWord(file, jumpTo) != 0x28F + && (readWord(file, jumpTo) != 0x125 || romEntry.romType != Type_Plat)) { + continue; // not a rival script + } + // Replace the two starter-words 387 and 390 + writeWord(file, baseOffset + 0x8, + newStarters.get(0).number); + writeWord(file, baseOffset + 0x15, + newStarters.get(1).number); + } + } + } + // Tag battles with rival or friend + // Have their own script magic + // 2 for Lucas/Dawn (=4 occurrences), 1 or 2 for Barry + byte[] tagBattleMagic = new byte[] { (byte) 0xDE, 0x00, 0x0C, + (byte) 0x80, 0x28, 0x00, 0x04, (byte) 0x80 }; + byte[] tagBattleMagic2 = new byte[] { 0x11, 0x00, 0x0C, + (byte) 0x80, (byte) 0x86, 0x01, 0x1C, 0x00, 0x01 }; + int[] filesWithTagBattleScript = (romEntry.romType == Type_Plat) ? new int[] { + 2, 136, 201, 236 } + : new int[] { 2, 131, 230 }; + for (int i = 0; i < filesWithTagBattleScript.length; i++) { + int fileCheck = filesWithTagBattleScript[i]; + byte[] file = scriptNARC.files.get(fileCheck); + List tbOffsets = RomFunctions.search(file, + tagBattleMagic); + if (tbOffsets.size() > 0) { + for (int baseOffset : tbOffsets) { + // found, check for second part + int secondPartStart = baseOffset + + tagBattleMagic.length + 2; + if (secondPartStart + tagBattleMagic2.length > file.length) { + continue; // match failed + } + boolean valid = true; + for (int spo = 0; spo < tagBattleMagic2.length; spo++) { + if (file[secondPartStart + spo] != tagBattleMagic2[spo]) { + valid = false; + break; + } + } + if (!valid) { + continue; + } + // Make sure the jump following the second + // part jumps to a command + int jumpLoc = secondPartStart + + tagBattleMagic2.length; + int jumpTo = readLong(file, jumpLoc) + jumpLoc + 4; + if (readWord(file, jumpTo) != 0x1B) { + continue; // not a tag battle script + } + // Replace the two starter-words + if (readWord(file, baseOffset + 0x21) == 387) { + // first starter + writeWord(file, baseOffset + 0x21, + newStarters.get(0).number); + } else { + // third starter + writeWord(file, baseOffset + 0x21, + newStarters.get(2).number); + } + // second starter + writeWord(file, baseOffset + 0xE, + newStarters.get(1).number); + } + } + } + // Fix starter script text + // The starter picking screen + List spStrings = getStrings(romEntry + .getInt("StarterScreenTextOffset")); + // Get pokedex info + List pokedexSpeciesStrings = getStrings(romEntry + .getInt("PokedexSpeciesTextOffset")); + for (int i = 0; i < 3; i++) { + Pokemon newStarter = newStarters.get(i); + int color = (i == 0) ? 3 : i; + String newStarterDesc = "\\vFF00\\z000" + color + + pokedexSpeciesStrings.get(newStarter.number) + + " " + newStarter.name + + "\\vFF00\\z0000!\\nWill you take this Pokémon?"; + spStrings.set(i + 1, newStarterDesc); + } + // rewrite starter picking screen + setStrings(romEntry.getInt("StarterScreenTextOffset"), + spStrings); + if (romEntry.romType == Type_DP) { + // what rival says after we get the Pokemon + List lakeStrings = getStrings(romEntry + .getInt("StarterLocationTextOffset")); + lakeStrings + .set(19, + "\\v0103\\z0000: Fwaaah!\\nYour Pokémon totally rocked!\\rBut mine was way tougher\\nthan yours!\\r...They were other people’s\\nPokémon, though...\\rBut we had to use them...\\nThey won’t mind, will they?\\r"); + setStrings(romEntry.getInt("StarterLocationTextOffset"), + lakeStrings); + } else { + // what rival says after we get the Pokemon + List r201Strings = getStrings(romEntry + .getInt("StarterLocationTextOffset")); + r201Strings + .set(36, + "\\v0103\\z0000\\z0000: Then, I choose you!\\nI’m picking this one!\\r"); + setStrings(romEntry.getInt("StarterLocationTextOffset"), + r201Strings); + } + } catch (IOException e) { + return false; + } + return true; + } + } + + @Override + public List getStarterHeldItems() { + // do nothing + return new ArrayList(); + } + + @Override + public void setStarterHeldItems(List items) { + // do nothing + } + + @Override + public void shufflePokemonStats() { + for (int i = 1; i <= 493; i++) { + pokes[i].shuffleStats(); + } + } + + @Override + public List getMoves() { + return Arrays.asList(moves); + } + + @Override + public List getEncounters(boolean useTimeOfDay) { + try { + if (romEntry.romType == Type_HGSS) { + return getEncountersHGSS(useTimeOfDay); + } else { + return getEncountersDPPt(); + } + } catch (IOException ex) { + // Uh-oh + ex.printStackTrace(); + return new ArrayList(); + } + } + + private List getEncountersDPPt() throws IOException { + // Determine file to use + String encountersFile = romEntry.getString("WildPokemon"); + + NARCContents encounterData = readNARC(encountersFile); + List encounters = new ArrayList(); + // Credit for + // https://github.com/magical/pokemon-encounters/blob/master/nds/encounters-gen4-sinnoh.py + // for the structure for this. + int c = -1; + for (byte[] b : encounterData.files) { + c++; + int grassRate = readLong(b, 0); + if (grassRate != 0) { + // up to 4 + List grassEncounters = readEncountersDPPt(b, 4, 12); + EncounterSet grass = new EncounterSet(); + grass.displayName = "grass"; + grass.encounters = grassEncounters; + grass.rate = grassRate; + grass.offset = c; + encounters.add(grass); + } + // // tmp: conditional replacements + // EncounterSet conds = new EncounterSet(); + // conds.displayName = "conds"; + // conds.rate = grassRate; + // conds.offset = c; + // for (int i = 0; i < 20; i++) { + // int offs = 100 + i * 8 + (i >= 10 ? 48 : 0); + // int pknum = readLong(b, offs); + // if (pknum >= 1 && pknum <= 493) { + // Pokemon pk = pokes[pknum]; + // Encounter enc = new Encounter(); + // enc.level = 5; + // enc.pokemon = pk; + // conds.encounters.add(enc); + // } + // } + // if (conds.encounters.size() > 0) + // encounters.add(conds); + // up to 204, 5 special ones to go + int offset = 204; + for (int i = 0; i < 5; i++) { + int rate = readLong(b, offset); + offset += 4; + List encountersHere = readSeaEncountersDPPt(b, + offset, 5); + offset += 40; + if (rate == 0 || i == 1) { + continue; + } + EncounterSet other = new EncounterSet(); + other.displayName = "special" + i; + other.offset = c; + other.encounters = encountersHere; + other.rate = rate; + encounters.add(other); + } + } + return encounters; + } + + private List readEncountersDPPt(byte[] data, int offset, + int amount) { + List encounters = new ArrayList(); + for (int i = 0; i < amount; i++) { + int level = readLong(data, offset + i * 8); + int pokemon = readLong(data, offset + 4 + i * 8); + Encounter enc = new Encounter(); + enc.level = level; + enc.pokemon = pokes[pokemon]; + encounters.add(enc); + } + return encounters; + } + + private List readSeaEncountersDPPt(byte[] data, int offset, + int amount) { + List encounters = new ArrayList(); + for (int i = 0; i < amount; i++) { + int level = readLong(data, offset + i * 8); + int pokemon = readLong(data, offset + 4 + i * 8); + Encounter enc = new Encounter(); + enc.level = level >> 8; + enc.maxLevel = level & 0xFF; + enc.pokemon = pokes[pokemon]; + encounters.add(enc); + } + return encounters; + } + + private List getEncountersHGSS(boolean useTimeOfDay) + throws IOException { + String encountersFile = romEntry.getString("WildPokemon"); + NARCContents encounterData = readNARC(encountersFile); + List encounters = new ArrayList(); + // Credit for + // https://github.com/magical/pokemon-encounters/blob/master/nds/encounters-gen4-johto.py + // for the structure for this. + int[] amounts = new int[] { 0, 5, 2, 5, 5, 5 }; + for (byte[] b : encounterData.files) { + int[] rates = new int[6]; + rates[0] = b[0] & 0xFF; + rates[1] = b[1] & 0xFF; + rates[2] = b[2] & 0xFF; + rates[3] = b[3] & 0xFF; + rates[4] = b[4] & 0xFF; + rates[5] = b[5] & 0xFF; + // Up to 8 after the rates + // Grass has to be handled on its own because the levels + // are reused for every time of day + int[] grassLevels = new int[12]; + for (int i = 0; i < 12; i++) { + grassLevels[i] = b[8 + i] & 0xFF; + } + // Up to 20 now (12 for levels) + Pokemon[][] grassPokes = new Pokemon[3][12]; + grassPokes[0] = readPokemonHGSS(b, 20, 12); + grassPokes[1] = readPokemonHGSS(b, 44, 12); + grassPokes[2] = readPokemonHGSS(b, 68, 12); + // Up to 92 now (12*2*3 for pokemon) + if (rates[0] != 0) { + if (!useTimeOfDay) { + // Just write "day" encounters + List grassEncounters = stitchEncsToLevels( + grassPokes[1], grassLevels); + EncounterSet grass = new EncounterSet(); + grass.encounters = grassEncounters; + grass.rate = rates[0]; + encounters.add(grass); + } else { + for (int i = 0; i < 3; i++) { + EncounterSet grass = new EncounterSet(); + grass.encounters = stitchEncsToLevels(grassPokes[i], + grassLevels); + grass.rate = rates[0]; + encounters.add(grass); + } + } + } + + // Hoenn/Sinnoh Radio + EncounterSet radio = readOptionalEncountersHGSS(b, 92, 4); + if (radio.encounters.size() > 0) { + encounters.add(radio); + } + + // Up to 100 now... 2*2*2 for radio pokemon + int offset = 100; + for (int i = 1; i < 6; i++) { + List encountersHere = readSeaEncountersHGSS(b, + offset, amounts[i]); + offset += 4 * amounts[i]; + if (rates[i] != 0) { + // Valid area. + EncounterSet other = new EncounterSet(); + other.encounters = encountersHere; + other.rate = rates[i]; + encounters.add(other); + } + } + + // Swarms + EncounterSet swarms = readOptionalEncountersHGSS(b, offset, 4); + if (swarms.encounters.size() > 0) { + encounters.add(swarms); + } + } + return encounters; + } + + private EncounterSet readOptionalEncountersHGSS(byte[] data, int offset, + int amount) { + EncounterSet es = new EncounterSet(); + es.rate = 1; + for (int i = 0; i < amount; i++) { + int pokemon = readWord(data, offset + i * 2); + if (pokemon != 0) { + Encounter e = new Encounter(); + e.level = 1; + e.pokemon = pokes[pokemon]; + es.encounters.add(e); + } + } + return es; + } + + private Pokemon[] readPokemonHGSS(byte[] data, int offset, int amount) { + Pokemon[] pokesHere = new Pokemon[amount]; + for (int i = 0; i < amount; i++) { + pokesHere[i] = pokes[readWord(data, offset + i * 2)]; + } + return pokesHere; + } + + private List readSeaEncountersHGSS(byte[] data, int offset, + int amount) { + List encounters = new ArrayList(); + for (int i = 0; i < amount; i++) { + int level = readWord(data, offset + i * 4); + int pokemon = readWord(data, offset + 2 + i * 4); + Encounter enc = new Encounter(); + enc.level = level & 0xFF; + enc.maxLevel = level >> 8; + enc.pokemon = pokes[pokemon]; + encounters.add(enc); + } + return encounters; + } + + @Override + public void setEncounters(boolean useTimeOfDay, + List encounters) { + try { + if (romEntry.romType == Type_HGSS) { + setEncountersHGSS(useTimeOfDay, encounters); + } else { + setEncountersDPPt(encounters); + } + } catch (IOException ex) { + // Uh-oh + ex.printStackTrace(); + } + } + + private void setEncountersDPPt(List encounterList) + throws IOException { + // Determine file to use + String encountersFile = romEntry.getString("WildPokemon"); + NARCContents encounterData = readNARC(encountersFile); + Iterator encounters = encounterList.iterator(); + // Credit for + // https://github.com/magical/pokemon-encounters/blob/master/nds/encounters-gen4-sinnoh.py + // for the structure for this. + for (byte[] b : encounterData.files) { + int grassRate = readLong(b, 0); + if (grassRate != 0) { + // grass encounters are a-go + EncounterSet grass = encounters.next(); + writeEncountersDPPt(b, 4, grass.encounters); + } + // up to 204, 5 special ones to go + // This is for surf, filler, old rod, good rod, super rod + // so we skip index 1 (filler) + int offset = 204; + for (int i = 0; i < 5; i++) { + int rate = readLong(b, offset); + offset += 4; + if (rate == 0 || i == 1) { + offset += 40; + continue; + } + + EncounterSet other = encounters.next(); + writeSeaEncountersDPPt(b, offset, other.encounters); + offset += 40; + } + } + + // Save + writeNARC(encountersFile, encounterData); + + } + + private void writeEncountersDPPt(byte[] data, int offset, + List encounters) { + int enclength = encounters.size(); + for (int i = 0; i < enclength; i++) { + Encounter enc = encounters.get(i); + writeLong(data, offset + i * 8, enc.level); + writeLong(data, offset + i * 8 + 4, enc.pokemon.number); + } + } + + private void writeSeaEncountersDPPt(byte[] data, int offset, + List encounters) { + int enclength = encounters.size(); + for (int i = 0; i < enclength; i++) { + Encounter enc = encounters.get(i); + writeLong(data, offset + i * 8, (enc.level << 8) + enc.maxLevel); + writeLong(data, offset + i * 8 + 4, enc.pokemon.number); + } + } + + private void setEncountersHGSS(boolean useTimeOfDay, + List encounterList) throws IOException { + String encountersFile = romEntry.getString("WildPokemon"); + NARCContents encounterData = readNARC(encountersFile); + Iterator encounters = encounterList.iterator(); + // Credit for + // https://github.com/magical/pokemon-encounters/blob/master/nds/encounters-gen4-johto.py + // for the structure for this. + int[] amounts = new int[] { 0, 5, 2, 5, 5, 5 }; + for (byte[] b : encounterData.files) { + int[] rates = new int[6]; + rates[0] = b[0] & 0xFF; + rates[1] = b[1] & 0xFF; + rates[2] = b[2] & 0xFF; + rates[3] = b[3] & 0xFF; + rates[4] = b[4] & 0xFF; + rates[5] = b[5] & 0xFF; + // Up to 20 after the rates & levels + // Grass has to be handled on its own because the levels + // are reused for every time of day + if (rates[0] != 0) { + if (!useTimeOfDay) { + // Get a single set of encounters... + // Write the encounters we get 3x for morning, day, night + EncounterSet grass = encounters.next(); + writePokemonHGSS(b, 20, grass.encounters); + writePokemonHGSS(b, 44, grass.encounters); + writePokemonHGSS(b, 68, grass.encounters); + } else { + for (int i = 0; i < 3; i++) { + EncounterSet grass = encounters.next(); + writePokemonHGSS(b, 20 + i * 24, grass.encounters); + } + } + } + + // Write radio pokemon + writeOptionalEncountersHGSS(b, 92, 4, encounters); + + // Up to 100 now... 2*2*2 for radio pokemon + // Write rock smash, surf, et al + int offset = 100; + for (int i = 1; i < 6; i++) { + if (rates[i] != 0) { + // Valid area. + EncounterSet other = encounters.next(); + writeSeaEncountersHGSS(b, offset, other.encounters); + } + offset += 4 * amounts[i]; + } + + // Write swarm pokemon + writeOptionalEncountersHGSS(b, offset, 4, encounters); + } + + // Save + writeNARC(encountersFile, encounterData); + + } + + private void writeOptionalEncountersHGSS(byte[] data, int offset, + int amount, Iterator encounters) { + Iterator eIter = null; + for (int i = 0; i < amount; i++) { + int origPokemon = readWord(data, offset + i * 2); + if (origPokemon != 0) { + // Need an encounter set, yes. + if (eIter == null) { + eIter = encounters.next().encounters.iterator(); + } + Encounter here = eIter.next(); + writeWord(data, offset + i * 2, here.pokemon.number); + } + } + + } + + private void writePokemonHGSS(byte[] data, int offset, + List encounters) { + int enclength = encounters.size(); + for (int i = 0; i < enclength; i++) { + writeWord(data, offset + i * 2, encounters.get(i).pokemon.number); + } + + } + + private void writeSeaEncountersHGSS(byte[] data, int offset, + List encounters) { + int enclength = encounters.size(); + for (int i = 0; i < enclength; i++) { + Encounter enc = encounters.get(i); + data[offset + i * 4] = (byte) enc.level; + data[offset + i * 4 + 1] = (byte) enc.maxLevel; + writeWord(data, offset + i * 4 + 2, enc.pokemon.number); + } + + } + + private List stitchEncsToLevels(Pokemon[] pokemon, int[] levels) { + List encounters = new ArrayList(); + for (int i = 0; i < pokemon.length; i++) { + Encounter enc = new Encounter(); + enc.level = levels[i]; + enc.pokemon = pokemon[i]; + encounters.add(enc); + } + return encounters; + } + + @Override + public List getTrainers() { + List allTrainers = new ArrayList(); + try { + NARCContents trainers = this.readNARC(romEntry + .getString("TrainerData")); + NARCContents trpokes = this.readNARC(romEntry + .getString("TrainerPokemon")); + List tclasses = this.getTrainerClassNames(); + List tnames = this.getTrainerNames(); + int trainernum = trainers.files.size(); + for (int i = 1; i < trainernum; i++) { + byte[] trainer = trainers.files.get(i); + byte[] trpoke = trpokes.files.get(i); + Trainer tr = new Trainer(); + tr.poketype = trainer[0] & 0xFF; + tr.trainerclass = trainer[1] & 0xFF; + tr.offset = trainer[16] & 0xFF; + int numPokes = trainer[3] & 0xFF; + int pokeOffs = 0; + tr.fullDisplayName = tclasses.get(tr.trainerclass) + " " + + tnames.get(i - 1); + // printBA(trpoke); + for (int poke = 0; poke < numPokes; poke++) { + int ailevel = trpoke[pokeOffs] & 0xFF; + int level = trpoke[pokeOffs + 2] & 0xFF; + int species = (trpoke[pokeOffs + 4] & 0xFF) + + ((trpoke[pokeOffs + 5] & 0x01) << 8); + // int formnum = (trpoke[pokeOffs + 5] >> 2); + TrainerPokemon tpk = new TrainerPokemon(); + tpk.level = level; + tpk.pokemon = pokes[species]; + tpk.AILevel = ailevel; + tpk.ability = trpoke[pokeOffs + 1] & 0xFF; + pokeOffs += 6; + if (tr.poketype >= 2) { + int heldItem = readWord(trpoke, pokeOffs); + tpk.heldItem = heldItem; + pokeOffs += 2; + } + if (tr.poketype % 2 == 1) { + int attack1 = readWord(trpoke, pokeOffs); + int attack2 = readWord(trpoke, pokeOffs + 2); + int attack3 = readWord(trpoke, pokeOffs + 4); + int attack4 = readWord(trpoke, pokeOffs + 6); + tpk.move1 = attack1; + tpk.move2 = attack2; + tpk.move3 = attack3; + tpk.move4 = attack4; + pokeOffs += 8; + } + // Plat/HGSS have another random pokeOffs +=2 here. + if (romEntry.romType != Type_DP) { + pokeOffs += 2; + } + tr.pokemon.add(tpk); + } + allTrainers.add(tr); + } + if (romEntry.romType == Type_DP) { + tagTrainersDP(allTrainers); + } else if (romEntry.romType == Type_Plat) { + tagTrainersPt(allTrainers); + } else { + tagTrainersHGSS(allTrainers); + } + } catch (IOException ex) { + // change this later + ex.printStackTrace(); + } + return allTrainers; + } + + private void tagTrainersDP(List trs) { + // Gym Trainers + tag(trs, "GYM1", 0xf4, 0xf5); + tag(trs, "GYM2", 0x144, 0x103, 0x104, 0x15C); + tag(trs, "GYM3", 0x135, 0x136, 0x137, 0x138); + tag(trs, "GYM4", 0x1f1, 0x1f2, 0x191, 0x153, 0x125, 0x1E3); + tag(trs, "GYM5", 0x165, 0x145, 0x10a, 0x14a, 0x154, 0x157, 0x118, 0x11c); + tag(trs, "GYM6", 0x13a, 0x100, 0x101, 0x117, 0x16f, 0xe8, 0x11b); + tag(trs, "GYM7", 0x10c, 0x10d, 0x10e, 0x10f, 0x33b, 0x33c); + tag(trs, "GYM8", 0x158, 0x155, 0x12d, 0x12e, 0x12f, 0x11d, 0x119); + + // Gym Leaders + tag(trs, 0xf6, "GYM1"); + tag(trs, 0x13b, "GYM2"); + tag(trs, 0x13d, "GYM3"); // Maylene + tag(trs, 0x13c, "GYM4"); // Wake + tag(trs, 0x13e, "GYM5"); // Fantina + tag(trs, 0xfa, "GYM6"); // Byron + tag(trs, 0x13f, "GYM7"); // Candice + tag(trs, 0x140, "GYM8"); // Volkner + + // Elite 4 + tag(trs, 0x105, "ELITE1"); + tag(trs, 0x106, "ELITE2"); + tag(trs, 0x107, "ELITE3"); + tag(trs, 0x108, "ELITE4"); + tag(trs, 0x10b, "CHAMPION"); + + // Rival battles (8) + tagRivalConsecutive(trs, "RIVAL1", 0xf8); + tagRivalConsecutive(trs, "RIVAL2", 0x1d7); + tagRivalConsecutive(trs, "RIVAL3", 0x1da); + tagRivalConsecutive(trs, "RIVAL4", 0x1dd); + // Tag battle is not following ze usual format + tag(trs, 0x26b, "RIVAL5-0"); + tag(trs, 0x26c, "RIVAL5-1"); + tag(trs, 0x25f, "RIVAL5-2"); + // Back to normal + tagRivalConsecutive(trs, "RIVAL6", 0x1e0); + tagRivalConsecutive(trs, "RIVAL7", 0x346); + tagRivalConsecutive(trs, "RIVAL8", 0x349); + + // Themed + tag(trs, "THEMED:CYRUS", 0x193, 0x194); + tag(trs, "THEMED:MARS", 0x127, 0x195, 0x210); + tag(trs, "THEMED:JUPITER", 0x196, 0x197); + tag(trs, "THEMED:SATURN", 0x198, 0x199); + + // Lucas & Dawn tag battles + tagFriendConsecutive(trs, "FRIEND1", 0x265); + tagFriendConsecutive(trs, "FRIEND1", 0x268); + tagFriendConsecutive2(trs, "FRIEND2", 0x26D); + tagFriendConsecutive2(trs, "FRIEND2", 0x270); + + } + + private void tagTrainersPt(List trs) { + // Gym Trainers + tag(trs, "GYM1", 0xf4, 0xf5); + tag(trs, "GYM2", 0x144, 0x103, 0x104, 0x15C); + tag(trs, "GYM3", 0x165, 0x145, 0x154, 0x157, 0x118, 0x11c); + tag(trs, "GYM4", 0x135, 0x136, 0x137, 0x138); + tag(trs, "GYM5", 0x1f1, 0x1f2, 0x191, 0x153, 0x125, 0x1E3); + tag(trs, "GYM6", 0x13a, 0x100, 0x101, 0x117, 0x16f, 0xe8, 0x11b); + tag(trs, "GYM7", 0x10c, 0x10d, 0x10e, 0x10f, 0x33b, 0x33c); + tag(trs, "GYM8", 0x158, 0x155, 0x12d, 0x12e, 0x12f, 0x11d, 0x119, 0x14b); + + // Gym Leaders + tag(trs, 0xf6, "GYM1"); + tag(trs, 0x13b, "GYM2"); + tag(trs, 0x13e, "GYM3"); // Fantina + tag(trs, 0x13d, "GYM4"); // Maylene + tag(trs, 0x13c, "GYM5"); // Wake + tag(trs, 0xfa, "GYM6"); // Byron + tag(trs, 0x13f, "GYM7"); // Candice + tag(trs, 0x140, "GYM8"); // Volkner + + // Elite 4 + tag(trs, 0x105, "ELITE1"); + tag(trs, 0x106, "ELITE2"); + tag(trs, 0x107, "ELITE3"); + tag(trs, 0x108, "ELITE4"); + tag(trs, 0x10b, "CHAMPION"); + + // Rival battles (10) + tagRivalConsecutive(trs, "RIVAL1", 0x353); + tagRivalConsecutive(trs, "RIVAL2", 0xf8); + tagRivalConsecutive(trs, "RIVAL3", 0x1d7); + tagRivalConsecutive(trs, "RIVAL4", 0x1da); + tagRivalConsecutive(trs, "RIVAL5", 0x1dd); + // Tag battle is not following ze usual format + tag(trs, 0x26b, "RIVAL6-0"); + tag(trs, 0x26c, "RIVAL6-1"); + tag(trs, 0x25f, "RIVAL6-2"); + // Back to normal + tagRivalConsecutive(trs, "RIVAL7", 0x1e0); + tagRivalConsecutive(trs, "RIVAL8", 0x346); + tagRivalConsecutive(trs, "RIVAL9", 0x349); + tagRivalConsecutive(trs, "RIVAL10", 0x368); + + // Battleground Gym Leaders + tag(trs, 0x35A, "GYM1"); + tag(trs, 0x359, "GYM2"); + tag(trs, 0x35C, "GYM3"); + tag(trs, 0x356, "GYM4"); + tag(trs, 0x35B, "GYM5"); + tag(trs, 0x358, "GYM6"); + tag(trs, 0x355, "GYM7"); + tag(trs, 0x357, "GYM8"); + + // Match vs Volkner and Flint in Battle Frontier + tag(trs, 0x399, "GYM8"); + tag(trs, 0x39A, "ELITE3"); + + // E4 rematch + tag(trs, 0x362, "ELITE1"); + tag(trs, 0x363, "ELITE2"); + tag(trs, 0x364, "ELITE3"); + tag(trs, 0x365, "ELITE4"); + tag(trs, 0x366, "CHAMPION"); + + // Themed + tag(trs, "THEMED:CYRUS", 0x391, 0x193, 0x194); + tag(trs, "THEMED:MARS", 0x127, 0x195, 0x210, 0x39e); + tag(trs, "THEMED:JUPITER", 0x196, 0x197, 0x39f); + tag(trs, "THEMED:SATURN", 0x198, 0x199); + + // Lucas & Dawn tag battles + tagFriendConsecutive(trs, "FRIEND1", 0x265); + tagFriendConsecutive(trs, "FRIEND1", 0x268); + tagFriendConsecutive2(trs, "FRIEND2", 0x26D); + tagFriendConsecutive2(trs, "FRIEND2", 0x270); + + } + + private void tagTrainersHGSS(List trs) { + // Gym Trainers + tag(trs, "GYM1", 0x32, 0x1D); + tag(trs, "GYM2", 0x43, 0x44, 0x45, 0x0a); + tag(trs, "GYM3", 0x05, 0x46, 0x47, 0x16); + tag(trs, "GYM4", 0x57, 0x58, 0x59, 0x2e); + tag(trs, "GYM5", 0x9c, 0x9d, 0x9f, 0xfb); + tag(trs, "GYM7", 0x1e0, 0x1e1, 0x1e2, 0x1e3, 0x1e4); + tag(trs, "GYM8", 0x6e, 0x6f, 0x70, 0x75, 0x77); + + tag(trs, "GYM9", 0x134, 0x2ad); + tag(trs, "GYM10", 0x2a4, 0x2a5, 0x2a6, 0x129, 0x12a); + tag(trs, "GYM11", 0x18c, 0xe8, 0x151); + tag(trs, "GYM12", 0x150, 0x146, 0x164, 0x15a); + tag(trs, "GYM13", 0x53, 0x54, 0xb7, 0x88); + tag(trs, "GYM14", 0x170, 0x171, 0xe6, 0x19f); + tag(trs, "GYM15", 0x2b1, 0x2b2, 0x2b3, 0x2b4, 0x2b5, 0x2b6); + tag(trs, "GYM16", 0x2a9, 0x2aa, 0x2ab, 0x2ac); + + // Gym Leaders + tag(trs, 0x14, "GYM1"); + tag(trs, 0x15, "GYM2"); + tag(trs, 0x1e, "GYM3"); + tag(trs, 0x1f, "GYM4"); + tag(trs, 0x22, "GYM5"); + tag(trs, 0x21, "GYM6"); + tag(trs, 0x20, "GYM7"); + tag(trs, 0x23, "GYM8"); + + tag(trs, 0xFD, "GYM9"); + tag(trs, 0xFE, "GYM10"); + tag(trs, 0xFF, "GYM11"); + tag(trs, 0x100, "GYM12"); + tag(trs, 0x101, "GYM13"); + tag(trs, 0x102, "GYM14"); + tag(trs, 0x103, "GYM15"); + tag(trs, 0x105, "GYM16"); + + // Elite 4 + tag(trs, 0xf5, "ELITE1"); + tag(trs, 0xf7, "ELITE2"); + tag(trs, 0x1a2, "ELITE3"); + tag(trs, 0xf6, "ELITE4"); + tag(trs, 0xf4, "CHAMPION"); + + // Red + tag(trs, 0x104, "UBER"); + + // Gym Rematches + tag(trs, 0x2c8, "GYM1"); + tag(trs, 0x2c9, "GYM2"); + tag(trs, 0x2ca, "GYM3"); + tag(trs, 0x2cb, "GYM4"); + tag(trs, 0x2ce, "GYM5"); + tag(trs, 0x2cd, "GYM6"); + tag(trs, 0x2cc, "GYM7"); + tag(trs, 0x2cf, "GYM8"); + + tag(trs, 0x2d0, "GYM9"); + tag(trs, 0x2d1, "GYM10"); + tag(trs, 0x2d2, "GYM11"); + tag(trs, 0x2d3, "GYM12"); + tag(trs, 0x2d4, "GYM13"); + tag(trs, 0x2d5, "GYM14"); + tag(trs, 0x2d6, "GYM15"); + tag(trs, 0x2d7, "GYM16"); + + // Elite 4 Rematch + tag(trs, 0x2be, "ELITE1"); + tag(trs, 0x2bf, "ELITE2"); + tag(trs, 0x2c0, "ELITE3"); + tag(trs, 0x2c1, "ELITE4"); + tag(trs, 0x2bd, "CHAMPION"); + + // Rival Battles + tagRivalConsecutive(trs, "RIVAL1", 0x1F0); + + tag(trs, 0x10a, "RIVAL2-0"); + tag(trs, 0x10d, "RIVAL2-1"); + tag(trs, 0x1, "RIVAL2-2"); + + tag(trs, 0x10B, "RIVAL3-0"); + tag(trs, 0x10E, "RIVAL3-1"); + tag(trs, 0x107, "RIVAL3-2"); + + tag(trs, 0x121, "RIVAL4-0"); + tag(trs, 0x10f, "RIVAL4-1"); + tag(trs, 0x120, "RIVAL4-2"); + + tag(trs, 0x10C, "RIVAL5-0"); + tag(trs, 0x110, "RIVAL5-1"); + tag(trs, 0x108, "RIVAL5-2"); + + tagRivalConsecutive(trs, "RIVAL6", 0x11e); + tagRivalConsecutive(trs, "RIVAL7", 0x2e0); // dragons den tag battle + tagRivalConsecutive(trs, "RIVAL8", 0x1EA); + + // Clair & Lance match in Dragons Den + tag(trs, 0x2DE, "GYM8"); + tag(trs, 0x2DD, "CHAMPION"); + + // Themed + tag(trs, "THEMED:ARIANA", 0x1df, 0x1de); + tag(trs, "THEMED:PETREL", 0x1e8, 0x1e7); + tag(trs, "THEMED:PROTON", 0x1e6, 0x2c2); + tag(trs, "THEMED:SPROUTTOWER", 0x2b, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x122); + + } + + private void tag(List allTrainers, int number, String tag) { + allTrainers.get(number - 1).tag = tag; + } + + private void tag(List allTrainers, String tag, int... numbers) { + for (int num : numbers) { + allTrainers.get(num - 1).tag = tag; + } + } + + private void tagRivalConsecutive(List allTrainers, String tag, + int offsetFire) { + allTrainers.get(offsetFire - 1).tag = tag + "-0"; + allTrainers.get(offsetFire).tag = tag + "-1"; + allTrainers.get(offsetFire - 2).tag = tag + "-2"; + + } + + private void tagFriendConsecutive(List allTrainers, String tag, + int offsetGrass) { + allTrainers.get(offsetGrass - 1).tag = tag + "-1"; + allTrainers.get(offsetGrass).tag = tag + "-2"; + allTrainers.get(offsetGrass + 1).tag = tag + "-0"; + + } + + private void tagFriendConsecutive2(List allTrainers, String tag, + int offsetWater) { + allTrainers.get(offsetWater - 1).tag = tag + "-0"; + allTrainers.get(offsetWater).tag = tag + "-1"; + allTrainers.get(offsetWater + 1).tag = tag + "-2"; + + } + + @Override + public void setTrainers(List trainerData) { + Iterator allTrainers = trainerData.iterator(); + try { + NARCContents trainers = this.readNARC(romEntry + .getString("TrainerData")); + NARCContents trpokes = new NARCContents(); + // empty entry + trpokes.files.add(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); + int trainernum = trainers.files.size(); + for (int i = 1; i < trainernum; i++) { + byte[] trainer = trainers.files.get(i); + Trainer tr = allTrainers.next(); + tr.poketype = 0; // write as type 0 for no item/moves + trainer[0] = (byte) tr.poketype; + int numPokes = tr.pokemon.size(); + trainer[3] = (byte) numPokes; + + int bytesNeeded = 6 * numPokes; + if (romEntry.romType != Type_DP) { + bytesNeeded += 2 * numPokes; + } + if (tr.poketype % 2 == 1) { + bytesNeeded += 8 * numPokes; + } + if (tr.poketype >= 2) { + bytesNeeded += 2 * numPokes; + } + byte[] trpoke = new byte[bytesNeeded]; + int pokeOffs = 0; + Iterator tpokes = tr.pokemon.iterator(); + for (int poke = 0; poke < numPokes; poke++) { + TrainerPokemon tpk = tpokes.next(); + writeWord(trpoke, pokeOffs, tpk.AILevel); + writeWord(trpoke, pokeOffs + 2, tpk.level); + writeWord(trpoke, pokeOffs + 4, tpk.pokemon.number); + pokeOffs += 6; + if (tr.poketype >= 2) { + writeWord(trpoke, pokeOffs, tpk.heldItem); + pokeOffs += 2; + } + if (tr.poketype % 2 == 1) { + writeWord(trpoke, pokeOffs, tpk.move1); + writeWord(trpoke, pokeOffs + 2, tpk.move2); + writeWord(trpoke, pokeOffs + 4, tpk.move3); + writeWord(trpoke, pokeOffs + 6, tpk.move4); + pokeOffs += 8; + } + // Plat/HGSS have another random pokeOffs +=2 here. + if (romEntry.romType != Type_DP) { + pokeOffs += 2; + } + } + trpokes.files.add(trpoke); + } + this.writeNARC(romEntry.getString("TrainerData"), trainers); + this.writeNARC(romEntry.getString("TrainerPokemon"), trpokes); + } catch (IOException ex) { + // change this later + ex.printStackTrace(); + } + + } + + @Override + public Map> getMovesLearnt() { + Map> movesets = new TreeMap>(); + try { + NARCContents movesLearnt = this.readNARC(romEntry + .getString("PokemonMovesets")); + for (int i = 1; i <= 493; i++) { + Pokemon pkmn = pokes[i]; + byte[] rom = movesLearnt.files.get(i); + int moveDataLoc = 0; + List learnt = new ArrayList(); + while ((rom[moveDataLoc] & 0xFF) != 0xFF + || (rom[moveDataLoc + 1] & 0xFF) != 0xFF) { + int move = (rom[moveDataLoc] & 0xFF); + int level = (rom[moveDataLoc + 1] & 0xFE) >> 1; + if ((rom[moveDataLoc + 1] & 0x01) == 0x01) { + move += 256; + } + MoveLearnt ml = new MoveLearnt(); + ml.level = level; + ml.move = move; + learnt.add(ml); + moveDataLoc += 2; + } + movesets.put(pkmn, learnt); + } + } catch (IOException e) { + // change this later + e.printStackTrace(); + } + return movesets; + } + + @Override + public void setMovesLearnt(Map> movesets) { + // Get backup of movesets + Map> oldSets = this.getMovesLearnt(); + int[] extraLearnSets = new int[] { 7, 13, 13 }; + // Build up a new NARC + NARCContents movesLearnt = new NARCContents(); + // The blank moveset + byte[] blankSet = new byte[] { (byte) 0xFF, (byte) 0xFF, 0, 0 }; + movesLearnt.files.add(blankSet); + for (int i = 1; i <= 493; i++) { + Pokemon pkmn = pokes[i]; + List learnt = movesets.get(pkmn); + int sizeNeeded = learnt.size() * 2 + 2; + if ((sizeNeeded % 4) != 0) { + sizeNeeded += 2; + } + byte[] moveset = new byte[sizeNeeded]; + int j = 0; + for (; j < learnt.size(); j++) { + MoveLearnt ml = learnt.get(j); + moveset[j * 2] = (byte) (ml.move & 0xFF); + int levelPart = (ml.level << 1) & 0xFE; + if (ml.move > 255) { + levelPart++; + } + moveset[j * 2 + 1] = (byte) levelPart; + } + moveset[j * 2] = (byte) 0xFF; + moveset[j * 2 + 1] = (byte) 0xFF; + movesLearnt.files.add(moveset); + } + for (int j = 0; j < extraLearnSets[romEntry.romType]; j++) { + movesLearnt.files.add(blankSet); + } + // Save + try { + this.writeNARC(romEntry.getString("PokemonMovesets"), movesLearnt); + } catch (IOException e) { + } + + } + + private static class StaticPokemon { + private int[] files; + private int[] offsets; + + public Pokemon getPokemon(Gen4RomHandler parent, NARCContents scriptNARC) { + return parent.pokes[parent.readWord(scriptNARC.files.get(files[0]), + offsets[0])]; + } + + public void setPokemon(Gen4RomHandler parent, NARCContents scriptNARC, + Pokemon pkmn) { + int value = pkmn.number; + for (int i = 0; i < offsets.length; i++) { + byte[] file = scriptNARC.files.get(files[i]); + parent.writeWord(file, offsets[i], value); + } + } + } + + @Override + public List getStaticPokemon() { + List sp = new ArrayList(); + if (!romEntry.staticPokemonSupport) { + return sp; + } + try { + NARCContents scriptNARC = scriptNarc; + for (StaticPokemon statP : romEntry.staticPokemon) { + sp.add(statP.getPokemon(this, scriptNARC)); + } + if (romEntry.arrayEntries.containsKey("StaticPokemonTrades")) { + NARCContents tradeNARC = this.readNARC(romEntry + .getString("InGameTrades")); + int[] trades = romEntry.arrayEntries.get("StaticPokemonTrades"); + for (int tradeNum : trades) { + sp.add(pokes[readLong(tradeNARC.files.get(tradeNum), 0)]); + } + } + if (romEntry.getInt("MysteryEggOffset") > 0) { + byte[] ovOverlay = readOverlay(romEntry + .getInt("MoveTutorMovesOvlNumber")); + sp.add(pokes[ovOverlay[romEntry.getInt("MysteryEggOffset")] & 0xFF]); + } + } catch (IOException e) { + } + return sp; + } + + @Override + public boolean setStaticPokemon(List staticPokemon) { + if (!romEntry.staticPokemonSupport) { + return false; + } + int sptsize = romEntry.arrayEntries.containsKey("StaticPokemonTrades") ? romEntry.arrayEntries + .get("StaticPokemonTrades").length : 0; + int meggsize = romEntry.getInt("MysteryEggOffset") > 0 ? 1 : 0; + if (staticPokemon.size() != romEntry.staticPokemon.size() + sptsize + + meggsize) { + return false; + } + try { + Iterator statics = staticPokemon.iterator(); + NARCContents scriptNARC = scriptNarc; + for (StaticPokemon statP : romEntry.staticPokemon) { + statP.setPokemon(this, scriptNARC, statics.next()); + } + if (romEntry.arrayEntries.containsKey("StaticPokemonTrades")) { + NARCContents tradeNARC = this.readNARC(romEntry + .getString("InGameTrades")); + int[] trades = romEntry.arrayEntries.get("StaticPokemonTrades"); + for (int tradeNum : trades) { + Pokemon thisTrade = statics.next(); + List possibleAbilities = new ArrayList(); + possibleAbilities.add(thisTrade.ability1); + if (thisTrade.ability2 > 0) { + possibleAbilities.add(thisTrade.ability2); + } + if (thisTrade.ability3 > 0) { + possibleAbilities.add(thisTrade.ability3); + } + // Write species and ability + writeLong(tradeNARC.files.get(tradeNum), 0, + thisTrade.number); + writeLong(tradeNARC.files.get(tradeNum), 0x1C, + possibleAbilities.get(RandomSource + .nextInt(possibleAbilities.size()))); + } + writeNARC(romEntry.getString("InGameTrades"), tradeNARC); + } + if (romEntry.getInt("MysteryEggOffset") > 0) { + // Same overlay as MT moves + // Truncate the pokemon# to 1byte, unless it's 0 + int pokenum = statics.next().number; + if (pokenum > 255) { + pokenum = RandomSource.nextInt(255) + 1; + } + byte[] ovOverlay = readOverlay(romEntry + .getInt("MoveTutorMovesOvlNumber")); + ovOverlay[romEntry.getInt("MysteryEggOffset")] = (byte) pokenum; + writeOverlay(romEntry.getInt("MoveTutorMovesOvlNumber"), + ovOverlay); + } + } catch (IOException e) { + return false; + } + return true; + } + + @Override + public List getTMMoves() { + String tmDataPrefix; + if (romEntry.romType == Type_DP || romEntry.romType == Type_Plat) { + tmDataPrefix = "D100D200D300D400"; + } else { + tmDataPrefix = "1E003200"; + } + int offset = find(arm9, tmDataPrefix); + if (offset > 0) { + offset += tmDataPrefix.length()/2; // because it was a prefix + List tms = new ArrayList(); + for (int i = 0; i < 92; i++) { + tms.add(readWord(arm9, offset + i * 2)); + } + return tms; + } else { + return null; + } + } + + @Override + public List getHMMoves() { + String tmDataPrefix; + if (romEntry.romType == Type_DP || romEntry.romType == Type_Plat) { + tmDataPrefix = "D100D200D300D400"; + } else { + tmDataPrefix = "1E003200"; + } + int offset = find(arm9, tmDataPrefix); + if (offset > 0) { + offset += tmDataPrefix.length()/2; // because it was a prefix + offset += 184; // TM data + List hms = new ArrayList(); + for (int i = 0; i < 8; i++) { + hms.add(readWord(arm9, offset + i * 2)); + } + return hms; + } else { + return null; + } + } + + @Override + public void setTMMoves(List moveIndexes) { + String tmDataPrefix; + if (romEntry.romType == Type_DP || romEntry.romType == Type_Plat) { + tmDataPrefix = "D100D200D300D400"; + } else { + tmDataPrefix = "1E003200"; + } + int offset = find(arm9, tmDataPrefix); + if (offset > 0) { + offset += tmDataPrefix.length()/2; // because it was a prefix + for (int i = 0; i < 92; i++) { + writeWord(arm9, offset + i * 2, moveIndexes.get(i)); + } + + // Update TM item descriptions + List itemDescriptions = getStrings(romEntry + .getInt("ItemDescriptionsTextOffset")); + List moveDescriptions = getStrings(romEntry + .getInt("MoveDescriptionsTextOffset")); + // TM01 is item 328 and so on + for (int i = 0; i < 92; i++) { + // Rewrite 5-line move descs into 3-line item descs + itemDescriptions.set(i + 328, RomFunctions + .rewriteDescriptionForNewLineSize( + moveDescriptions.get(moveIndexes.get(i)), + "\\n", 40, ssd)); + } + // Save the new item descriptions + setStrings(romEntry.getInt("ItemDescriptionsTextOffset"), + itemDescriptions); + // Palettes update + String baseOfPalettes = "8D018E01210133018D018F0122013401"; + if (romEntry.romType == Type_DP) { + baseOfPalettes = "8D018E01210132018D018F0122013301"; + } + int offsPals = find(arm9, baseOfPalettes); + if (offsPals > 0) { + // Write pals + for (int i = 0; i < 92; i++) { + Move m = this.moves[moveIndexes.get(i)]; + int pal = this.typeTMPaletteNumber(m.type); + writeWord(arm9, offsPals + i * 8 + 2, pal); + } + } + // if we can't update the palettes its not a big deal... + } else { + } + } + + private static RomFunctions.StringSizeDeterminer ssd = new RomFunctions.StringLengthSD(); + + @Override + public int getTMCount() { + return 92; + } + + @Override + public int getHMCount() { + return 8; + } + + @Override + public Map getTMHMCompatibility() { + Map compat = new TreeMap(); + for (int i = 1; i <= 493; i++) { + byte[] data = pokeNarc.files.get(i); + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[101]; + for (int j = 0; j < 13; j++) { + readByteIntoFlags(data, flags, j * 8 + 1, 0x1C + j); + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setTMHMCompatibility(Map compatData) { + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + byte[] data = pokeNarc.files.get(pkmn.number); + for (int j = 0; j < 13; j++) { + data[0x1C + j] = getByteFromFlags(flags, j * 8 + 1); + } + } + } + + @Override + public boolean hasMoveTutors() { + return romEntry.romType != Type_DP; + } + + @Override + public List getMoveTutorMoves() { + if (!hasMoveTutors()) { + return new ArrayList(); + } + int baseOffset = romEntry.getInt("MoveTutorMovesOffset"); + int amount = romEntry.getInt("MoveTutorCount"); + int bytesPer = romEntry.getInt("MoveTutorBytesCount"); + List mtMoves = new ArrayList(); + try { + byte[] mtFile = readOverlay(romEntry + .getInt("MoveTutorMovesOvlNumber")); + for (int i = 0; i < amount; i++) { + mtMoves.add(readWord(mtFile, baseOffset + i * bytesPer)); + } + } catch (IOException e) { + } + return mtMoves; + } + + @Override + public void setMoveTutorMoves(List moves) { + if (!hasMoveTutors()) { + return; + } + int baseOffset = romEntry.getInt("MoveTutorMovesOffset"); + int amount = romEntry.getInt("MoveTutorCount"); + int bytesPer = romEntry.getInt("MoveTutorBytesCount"); + if (moves.size() != amount) { + return; + } + try { + byte[] mtFile = readOverlay(romEntry + .getInt("MoveTutorMovesOvlNumber")); + for (int i = 0; i < amount; i++) { + writeWord(mtFile, baseOffset + i * bytesPer, moves.get(i)); + } + writeOverlay(romEntry.getInt("MoveTutorMovesOvlNumber"), mtFile); + } catch (IOException e) { + } + } + + @Override + public Map getMoveTutorCompatibility() { + if (!hasMoveTutors()) { + return new TreeMap(); + } + Map compat = new TreeMap(); + int amount = romEntry.getInt("MoveTutorCount"); + int baseOffset = romEntry.getInt("MoveTutorCompatOffset"); + int bytesPer = romEntry.getInt("MoveTutorCompatBytesCount"); + try { + byte[] mtcFile; + if (romEntry.romType == Type_HGSS) { + mtcFile = readFile(romEntry.getString("MoveTutorCompat")); + } else { + mtcFile = readOverlay(romEntry + .getInt("MoveTutorCompatOvlNumber")); + } + for (int i = 1; i <= 493; i++) { + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[amount + 1]; + for (int j = 0; j < bytesPer; j++) { + readByteIntoFlags(mtcFile, flags, j * 8 + 1, baseOffset + + (i - 1) * bytesPer + j); + } + compat.put(pkmn, flags); + } + } catch (IOException e) { + } + return compat; + } + + @Override + public void setMoveTutorCompatibility(Map compatData) { + if (!hasMoveTutors()) { + return; + } + int amount = romEntry.getInt("MoveTutorCount"); + int baseOffset = romEntry.getInt("MoveTutorCompatOffset"); + int bytesPer = romEntry.getInt("MoveTutorCompatBytesCount"); + try { + byte[] mtcFile; + if (romEntry.romType == Type_HGSS) { + mtcFile = readFile(romEntry.getString("MoveTutorCompat")); + } else { + mtcFile = readOverlay(romEntry + .getInt("MoveTutorCompatOvlNumber")); + } + for (Map.Entry compatEntry : compatData + .entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + for (int j = 0; j < bytesPer; j++) { + int offsHere = baseOffset + (pkmn.number - 1) * bytesPer + + j; + if (j * 8 + 8 <= amount) { + // entirely new byte + mtcFile[offsHere] = getByteFromFlags(flags, j * 8 + 1); + } else if (j * 8 < amount) { + // need some of the original byte + int newByte = getByteFromFlags(flags, j * 8 + 1) & 0xFF; + int oldByteParts = (mtcFile[offsHere] >>> (8 - amount + j * 8)) << (8 - amount + j * 8); + mtcFile[offsHere] = (byte) (newByte | oldByteParts); + } + // else do nothing to the byte + } + } + if (romEntry.romType == Type_HGSS) { + writeFile(romEntry.getString("MoveTutorCompat"), mtcFile); + } else { + writeOverlay(romEntry.getInt("MoveTutorCompatOvlNumber"), + mtcFile); + } + } catch (IOException e) { + } + } + + private int find(byte[] data, String hexString) { + if (hexString.length() % 2 != 0) { + return -3; // error + } + byte[] searchFor = new byte[hexString.length() / 2]; + for (int i = 0; i < searchFor.length; i++) { + searchFor[i] = (byte) Integer.parseInt( + hexString.substring(i * 2, i * 2 + 2), 16); + } + List found = RomFunctions.search(data, searchFor); + if (found.size() == 0) { + return -1; // not found + } else if (found.size() > 1) { + return -2; // not unique + } else { + return found.get(0); + } + } + + private boolean lastStringsCompressed = false; + + private List getStrings(int index) { + PokeTextData pt = new PokeTextData(msgNarc.files.get(index)); + pt.decrypt(); + lastStringsCompressed = pt.compressFlag; + return new ArrayList(pt.strlist); + } + + private void setStrings(int index, List newStrings) { + setStrings(index, newStrings, false); + } + + private void setStrings(int index, List newStrings, + boolean compressed) { + byte[] rawUnencrypted = TextToPoke.MakeFile(newStrings, compressed); + + // make new encrypted name set + PokeTextData encrypt = new PokeTextData(rawUnencrypted); + encrypt.SetKey(0xD00E); + encrypt.encrypt(); + + // rewrite + msgNarc.files.set(index, encrypt.get()); + } + + @Override + public String getROMName() { + return "Pokemon " + romEntry.name; + } + + @Override + public String getROMCode() { + return romEntry.romCode; + } + + @Override + public String getSupportLevel() { + return romEntry.staticPokemonSupport ? "Complete" : "No Static Pokemon"; + } + + @Override + public boolean hasTimeBasedEncounters() { + // dppt technically do but we ignore them completely + return romEntry.romType == Type_HGSS; + } + + @Override + public boolean canChangeStaticPokemon() { + return romEntry.staticPokemonSupport; + } + + @Override + public boolean canChangeStarters() { + return true; + } + + @Override + public List getEvolutions() { + // Read NARC + List evos = new ArrayList(); + List evosForThisPoke = new ArrayList(); + try { + NARCContents evoNARC = readNARC(romEntry + .getString("PokemonEvolutions")); + for (int i = 1; i <= 493; i++) { + evosForThisPoke.clear(); + byte[] evoEntry = evoNARC.files.get(i); + for (int evo = 0; evo < 7; evo++) { + int method = readWord(evoEntry, evo * 6); + int species = readWord(evoEntry, evo * 6 + 4); + if (method >= 1 && method <= 26 && species >= 1) { + EvolutionType et = EvolutionType.fromIndex(4, method); + int extraInfo = readWord(evoEntry, evo * 6 + 2); + Evolution evol = new Evolution(i, species, true, et, + extraInfo); + if (!evos.contains(evol)) { + evos.add(evol); + evosForThisPoke.add(evol); + } + } + } + // split evos don't carry stats + if (evosForThisPoke.size() > 1) { + for (Evolution e : evosForThisPoke) { + e.carryStats = false; + } + } + } + } catch (IOException e) { + // can't do anything + } + return evos; + } + + @Override + public void removeTradeEvolutions(boolean changeMoveEvos) { + // Read NARC + try { + NARCContents evoNARC = readNARC(romEntry + .getString("PokemonEvolutions")); + Map> movesets = this.getMovesLearnt(); + log("--Removing Trade Evolutions--"); + for (int i = 1; i <= 493; i++) { + byte[] evoEntry = evoNARC.files.get(i); + for (int evo = 0; evo < 7; evo++) { + int evoType = readWord(evoEntry, evo * 6); + int evolvingTo = readWord(evoEntry, evo * 6 + 4); + // new 160 other impossible evolutions: + if (romEntry.romType == Type_HGSS) { + // beauty milotic + if (evoType == 15) { + // Replace w/ level 35 + writeWord(evoEntry, evo * 6, 4); + writeWord(evoEntry, evo * 6 + 2, 35); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 35); + } + // mt.coronet (magnezone/probopass) + if (evoType == 24) { + // Replace w/ level 40 + writeWord(evoEntry, evo * 6, 4); + writeWord(evoEntry, evo * 6 + 2, 40); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 40); + } + // moss rock (leafeon) + if (evoType == 25) { + // Replace w/ leaf stone + writeWord(evoEntry, evo * 6, 7); + writeWord(evoEntry, evo * 6 + 2, 85); + logEvoChangeStone(pokes[i].name, + pokes[evolvingTo].name, itemNames.get(85)); + } + // icy rock (glaceon) + if (evoType == 26) { + // Replace w/ dawn stone + writeWord(evoEntry, evo * 6, 7); + writeWord(evoEntry, evo * 6 + 2, 109); + logEvoChangeStone(pokes[i].name, + pokes[evolvingTo].name, itemNames.get(109)); + } + } + if (changeMoveEvos && evoType == 20) { + // read move + int move = readWord(evoEntry, evo * 6 + 2); + int levelLearntAt = 1; + for (MoveLearnt ml : movesets.get(pokes[i])) { + if (ml.move == move) { + levelLearntAt = ml.level; + break; + } + } + if (levelLearntAt == 1) { + // override for piloswine + levelLearntAt = 45; + } + // change to pure level evo + writeWord(evoEntry, evo * 6, 4); + writeWord(evoEntry, evo * 6 + 2, levelLearntAt); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, levelLearntAt); + } + // Pure Trade + if (evoType == 5) { + // Replace w/ level 37 + writeWord(evoEntry, evo * 6, 4); + writeWord(evoEntry, evo * 6 + 2, 37); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 37); + } + // Trade w/ Item + if (evoType == 6) { + // Get the current item & evolution + int item = readWord(evoEntry, evo * 6 + 2); + if (i == 79) { + // Slowpoke is awkward - he already has a level evo + // So we can't do Level up w/ Held Item for him + // Put Water Stone instead + writeWord(evoEntry, evo * 6, 7); + writeWord(evoEntry, evo * 6 + 2, 84); + logEvoChangeStone(pokes[i].name, + pokes[evolvingTo].name, itemNames.get(84)); + } else { + logEvoChangeLevelWithItem(pokes[i].name, + pokes[evolvingTo].name, itemNames.get(item)); + // Replace, for this entry, w/ + // Level up w/ Held Item at Day + writeWord(evoEntry, evo * 6, 18); + // Now look for a free slot to put + // Level up w/ Held Item at Night + for (int evo2 = evo + 1; evo2 < 7; evo2++) { + if (readWord(evoEntry, evo2 * 6) == 0) { + // Bingo, blank entry + writeWord(evoEntry, evo2 * 6, 19); + writeWord(evoEntry, evo2 * 6 + 2, item); + writeWord(evoEntry, evo2 * 6 + 4, + evolvingTo); + break; + } + } + } + } + } + } + writeNARC(romEntry.getString("PokemonEvolutions"), evoNARC); + logBlankLine(); + } catch (IOException e) { + // can't do anything + } + + } + + @Override + public List getTrainerNames() { + List tnames = new ArrayList( + getStrings(romEntry.getInt("TrainerNamesTextOffset"))); + tnames.remove(0); // blank one + for (int i = 0; i < tnames.size(); i++) { + if (tnames.get(i).contains("\\and")) { + tnames.set(i, tnames.get(i).replace("\\and", "&")); + } + } + return tnames; + } + + @Override + public int maxTrainerNameLength() { + return 10;// based off the english ROMs fixed + } + + @Override + public void setTrainerNames(List trainerNames) { + List oldTNames = getStrings(romEntry + .getInt("TrainerNamesTextOffset")); + List newTNames = new ArrayList(trainerNames); + for (int i = 0; i < newTNames.size(); i++) { + if (newTNames.get(i).contains("&")) { + newTNames.set(i, newTNames.get(i).replace("&", "\\and")); + } + } + newTNames.add(0, oldTNames.get(0)); // the 0-entry, preserve it + + // rewrite, only compressed if they were compressed before + setStrings(romEntry.getInt("TrainerNamesTextOffset"), newTNames, + lastStringsCompressed); + + } + + @Override + public TrainerNameMode trainerNameMode() { + return TrainerNameMode.MAX_LENGTH; + } + + @Override + public List getTCNameLengthsByTrainer() { + // not needed + return new ArrayList(); + } + + @Override + public List getTrainerClassNames() { + return getStrings(romEntry.getInt("TrainerClassesTextOffset")); + } + + @Override + public void setTrainerClassNames(List trainerClassNames) { + setStrings(romEntry.getInt("TrainerClassesTextOffset"), + trainerClassNames); + } + + @Override + public int maxTrainerClassNameLength() { + return 12;// based off the english ROMs + } + + @Override + public boolean fixedTrainerClassNamesLength() { + return false; + } + + @Override + public String getDefaultExtension() { + return "nds"; + } + + @Override + public int abilitiesPerPokemon() { + return 2; + } + + @Override + public int highestAbilityIndex() { + return 123; + } + + @Override + public int internalStringLength(String string) { + return string.length(); + } + + @Override + public void applySignature() { + // For now, do nothing. + + } + + @Override + public ItemList getAllowedItems() { + return allowedItems; + } + + @Override + public String[] getItemNames() { + return itemNames.toArray(new String[0]); + } + + @Override + public String abilityName(int number) { + return abilityNames.get(number); + } + + private List getFieldItems() { + List fieldItems = new ArrayList(); + // normal items + int scriptFile = romEntry.getInt("ItemBallsScriptOffset"); + byte[] itemScripts = scriptNarc.files.get(scriptFile); + int offset = 0; + int skipTableOffset = 0; + int[] skipTable = romEntry.arrayEntries.get("ItemBallsSkip"); + int setVar = romEntry.romType == Type_HGSS ? 0x29 : 0x28; + while (true) { + int part1 = readWord(itemScripts, offset); + if (part1 == 0xFD13) { + // done + break; + } + int offsetInFile = readRelativePointer(itemScripts, offset); + offset += 4; + if (skipTableOffset < skipTable.length + && (skipTable[skipTableOffset] == (offset / 4) - 1)) { + skipTableOffset++; + continue; + } + int command = readWord(itemScripts, offsetInFile); + int variable = readWord(itemScripts, offsetInFile + 2); + if (command == setVar && variable == 0x8008) { + int item = readWord(itemScripts, offsetInFile + 4); + fieldItems.add(item); + } + + } + + // hidden items + int hiTableOffset = romEntry.getInt("HiddenItemTableOffset"); + int hiTableLimit = romEntry.getInt("HiddenItemCount"); + for (int i = 0; i < hiTableLimit; i++) { + int item = readWord(arm9, hiTableOffset + i * 8); + fieldItems.add(item); + } + + return fieldItems; + } + + private void setFieldItems(List fieldItems) { + Iterator iterItems = fieldItems.iterator(); + + // normal items + int scriptFile = romEntry.getInt("ItemBallsScriptOffset"); + byte[] itemScripts = scriptNarc.files.get(scriptFile); + int offset = 0; + int skipTableOffset = 0; + int[] skipTable = romEntry.arrayEntries.get("ItemBallsSkip"); + int setVar = romEntry.romType == Type_HGSS ? 0x29 : 0x28; + while (true) { + int part1 = readWord(itemScripts, offset); + if (part1 == 0xFD13) { + // done + break; + } + int offsetInFile = readRelativePointer(itemScripts, offset); + offset += 4; + if (skipTableOffset < skipTable.length + && (skipTable[skipTableOffset] == (offset / 4) - 1)) { + skipTableOffset++; + continue; + } + int command = readWord(itemScripts, offsetInFile); + int variable = readWord(itemScripts, offsetInFile + 2); + if (command == setVar && variable == 0x8008) { + int item = iterItems.next(); + writeWord(itemScripts, offsetInFile + 4, item); + } + } + + // hidden items + int hiTableOffset = romEntry.getInt("HiddenItemTableOffset"); + int hiTableLimit = romEntry.getInt("HiddenItemCount"); + for (int i = 0; i < hiTableLimit; i++) { + int item = iterItems.next(); + writeWord(arm9, hiTableOffset + i * 8, item); + } + } + + @Override + public List getRequiredFieldTMs() { + if (romEntry.romType == Type_DP) { + return Arrays.asList(new Integer[] { 2, 3, 5, 9, 12, 19, 23, 28, + 34, 39, 41, 43, 46, 47, 49, 50, 62, 69, 79, 80, 82, 84, 85, + 87 }); + } else if (romEntry.romType == Type_Plat) { + // same as DP just we have to keep the weather TMs + return Arrays.asList(new Integer[] { 2, 3, 5, 7, 9, 11, 12, 18, 19, + 23, 28, 34, 37, 39, 41, 43, 46, 47, 49, 50, 62, 69, 79, 80, + 82, 84, 85, 87 }); + } + return new ArrayList(); + } + + @Override + public List getCurrentFieldTMs() { + List fieldItems = this.getFieldItems(); + List fieldTMs = new ArrayList(); + + for (int item : fieldItems) { + if (allowedItems.isTM(item)) { + fieldTMs.add(item - 327); + } + } + + return fieldTMs; + } + + @Override + public void setFieldTMs(List fieldTMs) { + List fieldItems = this.getFieldItems(); + int fiLength = fieldItems.size(); + Iterator iterTMs = fieldTMs.iterator(); + + for (int i = 0; i < fiLength; i++) { + int oldItem = fieldItems.get(i); + if (allowedItems.isTM(oldItem)) { + int newItem = iterTMs.next() + 327; + fieldItems.set(i, newItem); + } + } + + this.setFieldItems(fieldItems); + } + + @Override + public List getRegularFieldItems() { + List fieldItems = this.getFieldItems(); + List fieldRegItems = new ArrayList(); + + for (int item : fieldItems) { + if (allowedItems.isAllowed(item) && !(allowedItems.isTM(item))) { + fieldRegItems.add(item); + } + } + + return fieldRegItems; + } + + @Override + public void setRegularFieldItems(List items) { + List fieldItems = this.getFieldItems(); + int fiLength = fieldItems.size(); + Iterator iterNewItems = items.iterator(); + + for (int i = 0; i < fiLength; i++) { + int oldItem = fieldItems.get(i); + if (!(allowedItems.isTM(oldItem)) + && allowedItems.isAllowed(oldItem)) { + int newItem = iterNewItems.next(); + fieldItems.set(i, newItem); + } + } + + this.setFieldItems(fieldItems); + } + + @Override + public List getIngameTrades() { + List trades = new ArrayList(); + try { + NARCContents tradeNARC = this.readNARC(romEntry + .getString("InGameTrades")); + int[] spTrades = new int[0]; + if (romEntry.arrayEntries.containsKey("StaticPokemonTrades")) { + spTrades = romEntry.arrayEntries.get("StaticPokemonTrades"); + } + List tradeStrings = getStrings(romEntry + .getInt("IngameTradesTextOffset")); + int tradeCount = tradeNARC.files.size(); + for (int i = 0; i < tradeCount; i++) { + boolean isSP = false; + for (int j = 0; j < spTrades.length; j++) { + if (spTrades[j] == i) { + isSP = true; + break; + } + } + if (isSP) { + continue; + } + byte[] tfile = tradeNARC.files.get(i); + IngameTrade trade = new IngameTrade(); + trade.nickname = tradeStrings.get(i); + trade.givenPokemon = pokes[readLong(tfile, 0)]; + trade.ivs = new int[6]; + for (int iv = 0; iv < 6; iv++) { + trade.ivs[iv] = readLong(tfile, 4 + iv * 4); + } + trade.otId = readWord(tfile, 0x20); + trade.otName = tradeStrings.get(i + tradeCount); + trade.item = readLong(tfile, 0x3C); + trade.requestedPokemon = pokes[readLong(tfile, 0x4C)]; + trades.add(trade); + } + } catch (IOException ex) { + } + return trades; + } + + @Override + public void setIngameTrades(List trades) { + int tradeOffset = 0; + try { + NARCContents tradeNARC = this.readNARC(romEntry + .getString("InGameTrades")); + int[] spTrades = new int[0]; + if (romEntry.arrayEntries.containsKey("StaticPokemonTrades")) { + spTrades = romEntry.arrayEntries.get("StaticPokemonTrades"); + } + List tradeStrings = getStrings(romEntry + .getInt("IngameTradesTextOffset")); + int tradeCount = tradeNARC.files.size(); + for (int i = 0; i < tradeCount; i++) { + boolean isSP = false; + for (int j = 0; j < spTrades.length; j++) { + if (spTrades[j] == i) { + isSP = true; + break; + } + } + if (isSP) { + continue; + } + byte[] tfile = tradeNARC.files.get(i); + IngameTrade trade = trades.get(tradeOffset++); + tradeStrings.set(i, trade.nickname); + tradeStrings.set(i + tradeCount, trade.otName); + writeLong(tfile, 0, trade.givenPokemon.number); + for (int iv = 0; iv < 6; iv++) { + writeLong(tfile, 4 + iv * 4, trade.ivs[iv]); + } + writeWord(tfile, 0x20, trade.otId); + writeLong(tfile, 0x3C, trade.item); + writeLong(tfile, 0x4C, trade.requestedPokemon.number); + if (tfile.length > 0x50) { + writeLong(tfile, 0x50, 0); // disable gender + } + } + this.writeNARC(romEntry.getString("InGameTrades"), tradeNARC); + this.setStrings(romEntry.getInt("IngameTradesTextOffset"), + tradeStrings); + } catch (IOException ex) { + } + } + + @Override + public boolean hasDVs() { + return false; + } + + @Override + public int generationOfPokemon() { + return 4; + } + + @Override + public void removeEvosForPokemonPool() { + // slightly more complicated than gen2/3 + // we have to update a "baby table" too + List pokemonIncluded = this.mainPokemonList; + List evolsIncluded = new ArrayList(); + try { + NARCContents evoNARC = readNARC(romEntry + .getString("PokemonEvolutions")); + byte[] babyPokes = readFile(romEntry.getString("BabyPokemon")); + for (int i = 1; i <= 493; i++) { + boolean included = pokemonIncluded.contains(pokes[i]); + byte[] evoEntry = evoNARC.files.get(i); + for (int evo = 0; evo < 7; evo++) { + int method = readWord(evoEntry, evo * 6); + int species = readWord(evoEntry, evo * 6 + 4); + if (method >= 1 && method <= 26 && species >= 1) { + Pokemon evolvingInto = pokes[species]; + if (!included + || !pokemonIncluded.contains(evolvingInto)) { + // remove this evolution + writeWord(evoEntry, evo * 6, 0); + writeWord(evoEntry, evo * 6 + 2, 0); + writeWord(evoEntry, evo * 6 + 4, 0); + } else { + EvolutionType et = EvolutionType.fromIndex(4, method); + int extraInfo = readWord(evoEntry, evo * 6 + 2); + Evolution evol = new Evolution(i, species, true, et, + extraInfo); + evolsIncluded.add(evol); + } + } + } + } + // baby pokemon + for (int i = 1; i <= 493; i++) { + int oldBaby = i; + while (true) { + int currentBaby = oldBaby; + for (Evolution evol : evolsIncluded) { + if (evol.to == oldBaby) { + currentBaby = evol.from; + break; + } + } + if (currentBaby == oldBaby) { + break; + } + oldBaby = currentBaby; + } + writeWord(babyPokes, i * 2, oldBaby); + } + // finish up + writeNARC(romEntry.getString("PokemonEvolutions"), evoNARC); + writeFile(romEntry.getString("BabyPokemon"), babyPokes); + } catch (IOException e) { + // can't do anything + } + } + + @Override + public boolean supportsFourStartingMoves() { + return true; + } +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/Gen5RomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/Gen5RomHandler.java new file mode 100755 index 000000000..877d36727 --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/Gen5RomHandler.java @@ -0,0 +1,2705 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- Gen5RomHandler.java - randomizer handler for B/W/B2/W2. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.ByteArrayInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.TreeMap; + +import pptxt.PPTxtHandler; + +import com.dabomstew.pkrandom.FileFunctions; +import com.dabomstew.pkrandom.RandomSource; +import com.dabomstew.pkrandom.RomFunctions; +import com.dabomstew.pkrandom.pokemon.Encounter; +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.EvolutionType; +import com.dabomstew.pkrandom.pokemon.ExpCurve; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.ItemList; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.TrainerPokemon; +import com.dabomstew.pkrandom.pokemon.Type; + +import dsdecmp.HexInputStream; +import dsdecmp.JavaDSDecmp; + +public class Gen5RomHandler extends AbstractDSRomHandler { + + // Statics + private static final Type[] typeTable = constructTypeTable(); + + private static Type[] constructTypeTable() { + Type[] table = new Type[256]; + table[0x00] = Type.NORMAL; + table[0x01] = Type.FIGHTING; + table[0x02] = Type.FLYING; + table[0x03] = Type.POISON; + table[0x04] = Type.GROUND; + table[0x05] = Type.ROCK; + table[0x06] = Type.BUG; + table[0x07] = Type.GHOST; + table[0x08] = Type.STEEL; + table[0x09] = Type.FIRE; + table[0x0A] = Type.WATER; + table[0x0B] = Type.GRASS; + table[0x0C] = Type.ELECTRIC; + table[0x0D] = Type.PSYCHIC; + table[0x0E] = Type.ICE; + table[0x0F] = Type.DRAGON; + table[0x10] = Type.DARK; + return table; + } + + private static byte typeToByte(Type type) { + if (type == null) { + return 0x00; // normal? + } + switch (type) { + case NORMAL: + return 0x00; + case FIGHTING: + return 0x01; + case FLYING: + return 0x02; + case POISON: + return 0x03; + case GROUND: + return 0x04; + case ROCK: + return 0x05; + case BUG: + return 0x06; + case GHOST: + return 0x07; + case FIRE: + return 0x09; + case WATER: + return 0x0A; + case GRASS: + return 0x0B; + case ELECTRIC: + return 0x0C; + case PSYCHIC: + return 0x0D; + case ICE: + return 0x0E; + case DRAGON: + return 0x0F; + case STEEL: + return 0x08; + case DARK: + return 0x10; + default: + return 0; // normal by default + } + } + + private static class OffsetWithinEntry { + private int entry; + private int offset; + } + + private static class RomEntry { + private String name; + private String romCode; + private int romType; + private boolean staticPokemonSupport = false, + copyStaticPokemon = false; + private Map strings = new HashMap(); + private Map numbers = new HashMap(); + private Map arrayEntries = new HashMap(); + private Map offsetArrayEntries = new HashMap(); + private List staticPokemon = new ArrayList(); + + private int getInt(String key) { + if (!numbers.containsKey(key)) { + numbers.put(key, 0); + } + return numbers.get(key); + } + + private String getString(String key) { + if (!strings.containsKey(key)) { + strings.put(key, ""); + } + return strings.get(key); + } + } + + private static List roms; + private static ItemList allowedItems; + + static { + loadROMInfo(); + setupAllowedItems(); + } + + private static void loadROMInfo() { + roms = new ArrayList(); + RomEntry current = null; + try { + Scanner sc = new Scanner( + FileFunctions.openConfig("gen5_offsets.ini"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine().trim(); + if (q.contains("//")) { + q = q.substring(0, q.indexOf("//")).trim(); + } + if (!q.isEmpty()) { + if (q.startsWith("[") && q.endsWith("]")) { + // New rom + current = new RomEntry(); + current.name = q.substring(1, q.length() - 1); + roms.add(current); + } else { + String[] r = q.split("=", 2); + if (r.length == 1) { + System.err.println("invalid entry " + q); + continue; + } + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + r[1] = r[1].trim(); + if (r[0].equals("Game")) { + current.romCode = r[1]; + } else if (r[0].equals("Type")) { + if (r[1].equalsIgnoreCase("BW2")) { + current.romType = Type_BW2; + } else { + current.romType = Type_BW; + } + } else if (r[0].equals("CopyFrom")) { + for (RomEntry otherEntry : roms) { + if (r[1].equalsIgnoreCase(otherEntry.romCode)) { + // copy from here + current.arrayEntries + .putAll(otherEntry.arrayEntries); + current.numbers.putAll(otherEntry.numbers); + current.strings.putAll(otherEntry.strings); + current.offsetArrayEntries + .putAll(otherEntry.offsetArrayEntries); + if (current.copyStaticPokemon) { + current.staticPokemon + .addAll(otherEntry.staticPokemon); + current.staticPokemonSupport = true; + } else { + current.staticPokemonSupport = false; + } + } + } + } else if (r[0].equals("StaticPokemon[]")) { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + int[] offs = new int[offsets.length]; + int[] files = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + String[] parts = off.split("\\:"); + files[c] = parseRIInt(parts[0]); + offs[c++] = parseRIInt(parts[1]); + } + StaticPokemon sp = new StaticPokemon(); + sp.files = files; + sp.offsets = offs; + current.staticPokemon.add(sp); + } else { + String[] parts = r[1].split("\\:"); + int files = parseRIInt(parts[0]); + int offs = parseRIInt(parts[1]); + StaticPokemon sp = new StaticPokemon(); + sp.files = new int[] { files }; + sp.offsets = new int[] { offs }; + } + } else if (r[0].equals("StaticPokemonSupport")) { + int spsupport = parseRIInt(r[1]); + current.staticPokemonSupport = (spsupport > 0); + } else if (r[0].equals("CopyStaticPokemon")) { + int csp = parseRIInt(r[1]); + current.copyStaticPokemon = (csp > 0); + } else if (r[0].startsWith("StarterOffsets") + || r[0].equals("StaticPokemonFormValues")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + OffsetWithinEntry[] offs = new OffsetWithinEntry[offsets.length]; + int c = 0; + for (String off : offsets) { + String[] parts = off.split("\\:"); + OffsetWithinEntry owe = new OffsetWithinEntry(); + owe.entry = parseRIInt(parts[0]); + owe.offset = parseRIInt(parts[1]); + offs[c++] = owe; + } + current.offsetArrayEntries.put(r[0], offs); + } else { + if (r[1].startsWith("[") && r[1].endsWith("]")) { + String[] offsets = r[1].substring(1, + r[1].length() - 1).split(","); + if (offsets.length == 1 + && offsets[0].trim().isEmpty()) { + current.arrayEntries.put(r[0], new int[0]); + } else { + int[] offs = new int[offsets.length]; + int c = 0; + for (String off : offsets) { + offs[c++] = parseRIInt(off); + } + current.arrayEntries.put(r[0], offs); + } + } else if (r[0].endsWith("Offset") + || r[0].endsWith("Count") + || r[0].endsWith("Number")) { + int offs = parseRIInt(r[1]); + current.numbers.put(r[0], offs); + } else { + current.strings.put(r[0], r[1]); + } + } + } + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + + } + + private static int parseRIInt(String off) { + int radix = 10; + off = off.trim().toLowerCase(); + if (off.startsWith("0x") || off.startsWith("&h")) { + radix = 16; + off = off.substring(2); + } + try { + return Integer.parseInt(off, radix); + } catch (NumberFormatException ex) { + System.err.println("invalid base " + radix + "number " + off); + return 0; + } + } + + private static void setupAllowedItems() { + allowedItems = new ItemList(638); + // Key items + version exclusives + allowedItems.banRange(428, 109); + allowedItems.banRange(621, 18); + allowedItems.banSingles(574, 578, 579, 616, 617); + // Unknown blank items or version exclusives + allowedItems.banRange(113, 3); + allowedItems.banRange(120, 14); + // TMs & HMs - tms cant be held in gen5 + allowedItems.tmRange(328, 92); + allowedItems.tmRange(618, 3); + allowedItems.banRange(328, 100); + allowedItems.banRange(618, 3); + // Battle Launcher exclusives + allowedItems.banRange(592, 24); + } + + // This ROM + private Pokemon[] pokes; + private List pokemonList; + private Move[] moves; + private RomEntry romEntry; + private byte[] arm9; + private List abilityNames; + private List itemNames; + + private static final int Type_BW = 0; + private static final int Type_BW2 = 1; + + private NARCContents pokeNarc, moveNarc, stringsNarc, storyTextNarc, + scriptNarc; + + @Override + protected boolean detectNDSRom(String ndsCode) { + for (RomEntry re : roms) { + if (ndsCode.equals(re.romCode)) { + this.romEntry = re; + return true; // match + } + } + return false; + } + + @Override + protected void loadedROM() { + try { + arm9 = readARM9(); + } catch (IOException e) { + arm9 = new byte[0]; + } + try { + stringsNarc = readNARC(romEntry.getString("TextStrings")); + storyTextNarc = readNARC(romEntry.getString("TextStory")); + } catch (IOException e) { + stringsNarc = null; + storyTextNarc = null; + } + + try { + scriptNarc = readNARC(romEntry.getString("Scripts")); + } catch (IOException e) { + scriptNarc = null; + } + loadPokemonStats(); + pokemonList = Arrays.asList(pokes); + loadMoves(); + + abilityNames = getStrings(false, + romEntry.getInt("AbilityNamesTextOffset")); + itemNames = getStrings(false, romEntry.getInt("ItemNamesTextOffset")); + } + + private void loadPokemonStats() { + try { + pokeNarc = this.readNARC(romEntry.getString("PokemonStats")); + String[] pokeNames = readPokemonNames(); + pokes = new Pokemon[650]; + for (int i = 1; i <= 649; i++) { + pokes[i] = new Pokemon(); + pokes[i].number = i; + loadBasicPokeStats(pokes[i], pokeNarc.files.get(i)); + // Name? + pokes[i].name = pokeNames[i]; + } + } catch (IOException e) { + // uh-oh? + e.printStackTrace(); + } + + } + + private void loadMoves() { + try { + moveNarc = this.readNARC(romEntry.getString("MoveData")); + moves = new Move[560]; + List moveNames = getStrings(false, + romEntry.getInt("MoveNamesTextOffset")); + for (int i = 1; i <= 559; i++) { + byte[] moveData = moveNarc.files.get(i); + moves[i] = new Move(); + moves[i].name = moveNames.get(i); + moves[i].number = i; + moves[i].hitratio = (moveData[4] & 0xFF); + moves[i].power = moveData[3] & 0xFF; + moves[i].pp = moveData[5] & 0xFF; + moves[i].type = typeTable[moveData[0] & 0xFF]; + moves[i].effectIndex = moveData[2] & 0xFF; + } + } catch (IOException e) { + // change this later + e.printStackTrace(); + } + + } + + private void loadBasicPokeStats(Pokemon pkmn, byte[] stats) { + pkmn.hp = stats[0] & 0xFF; + pkmn.attack = stats[1] & 0xFF; + pkmn.defense = stats[2] & 0xFF; + pkmn.speed = stats[3] & 0xFF; + pkmn.spatk = stats[4] & 0xFF; + pkmn.spdef = stats[5] & 0xFF; + // Type + pkmn.primaryType = typeTable[stats[6] & 0xFF]; + pkmn.secondaryType = typeTable[stats[7] & 0xFF]; + // Only one type? + if (pkmn.secondaryType == pkmn.primaryType) { + pkmn.secondaryType = null; + } + pkmn.catchRate = stats[8] & 0xFF; + pkmn.growthCurve = ExpCurve.fromByte(stats[21]); + // Abilities for debugging later + pkmn.ability1 = stats[24] & 0xFF; + pkmn.ability2 = stats[25] & 0xFF; + pkmn.ability3 = stats[26] & 0xFF; + + // Held Items? + int item1 = readWord(stats, 12); + int item2 = readWord(stats, 14); + + if (item1 == item2) { + // guaranteed + pkmn.guaranteedHeldItem = item1; + pkmn.commonHeldItem = 0; + pkmn.rareHeldItem = 0; + pkmn.darkGrassHeldItem = 0; + } else { + pkmn.guaranteedHeldItem = 0; + pkmn.commonHeldItem = item1; + pkmn.rareHeldItem = item2; + pkmn.darkGrassHeldItem = readWord(stats, 16); + } + } + + private String[] readPokemonNames() { + String[] pokeNames = new String[650]; + List nameList = getStrings(false, + romEntry.getInt("PokemonNamesTextOffset")); + for (int i = 1; i <= 649; i++) { + pokeNames[i] = nameList.get(i); + } + return pokeNames; + } + + @Override + protected void savingROM() { + savePokemonStats(); + saveMoves(); + try { + writeARM9(arm9); + } catch (IOException e) { + } + try { + writeNARC(romEntry.getString("TextStrings"), stringsNarc); + writeNARC(romEntry.getString("TextStory"), storyTextNarc); + } catch (IOException e) { + } + + try { + writeNARC(romEntry.getString("Scripts"), scriptNarc); + } catch (IOException e) { + } + } + + private void saveMoves() { + for (int i = 1; i <= 559; i++) { + byte[] data = moveNarc.files.get(i); + data[3] = (byte) moves[i].power; + data[0] = typeToByte(moves[i].type); + int hitratio = (int) Math.round(moves[i].hitratio); + if (hitratio < 0) { + hitratio = 0; + } + if (hitratio > 101) { + hitratio = 100; + } + data[4] = (byte) hitratio; + data[5] = (byte) moves[i].pp; + } + + try { + this.writeNARC(romEntry.getString("MoveData"), moveNarc); + } catch (IOException e) { + // // change this later + e.printStackTrace(); + } + + } + + private void savePokemonStats() { + List nameList = getStrings(false, + romEntry.getInt("PokemonNamesTextOffset")); + for (int i = 1; i <= 649; i++) { + saveBasicPokeStats(pokes[i], pokeNarc.files.get(i)); + nameList.set(i, pokes[i].name); + } + setStrings(false, romEntry.getInt("PokemonNamesTextOffset"), nameList); + try { + this.writeNARC(romEntry.getString("PokemonStats"), pokeNarc); + } catch (IOException e) { + // uh-oh? + e.printStackTrace(); + } + + } + + private void saveBasicPokeStats(Pokemon pkmn, byte[] stats) { + stats[0] = (byte) pkmn.hp; + stats[1] = (byte) pkmn.attack; + stats[2] = (byte) pkmn.defense; + stats[3] = (byte) pkmn.speed; + stats[4] = (byte) pkmn.spatk; + stats[5] = (byte) pkmn.spdef; + stats[6] = typeToByte(pkmn.primaryType); + if (pkmn.secondaryType == null) { + stats[7] = stats[6]; + } else { + stats[7] = typeToByte(pkmn.secondaryType); + } + stats[8] = (byte) pkmn.catchRate; + stats[21] = pkmn.growthCurve.toByte(); + + stats[24] = (byte) pkmn.ability1; + stats[25] = (byte) pkmn.ability2; + stats[26] = (byte) pkmn.ability3; + + // Held items + if (pkmn.guaranteedHeldItem > 0) { + writeWord(stats, 12, pkmn.guaranteedHeldItem); + writeWord(stats, 14, pkmn.guaranteedHeldItem); + writeWord(stats, 16, 0); + } else { + writeWord(stats, 12, pkmn.commonHeldItem); + writeWord(stats, 14, pkmn.rareHeldItem); + writeWord(stats, 16, pkmn.darkGrassHeldItem); + } + } + + @Override + public boolean isInGame(Pokemon pkmn) { + return isInGame(pkmn.number); + } + + @Override + public boolean isInGame(int pokemonNumber) { + return pokemonNumber >= 1 && pokemonNumber <= 649; + } + + @Override + public List getPokemon() { + return pokemonList; + } + + @Override + public List getStarters() { + NARCContents scriptNARC = scriptNarc; + List starters = new ArrayList(); + for (int i = 0; i < 3; i++) { + OffsetWithinEntry[] thisStarter = romEntry.offsetArrayEntries + .get("StarterOffsets" + (i + 1)); + starters.add(pokes[readWord( + scriptNARC.files.get(thisStarter[0].entry), + thisStarter[0].offset)]); + } + return starters; + } + + @Override + public boolean setStarters(List newStarters) { + if (newStarters.size() != 3) { + return false; + } + for (Pokemon pkmn : newStarters) { + if (!isInGame(pkmn)) { + return false; + } + } + + // Fix up starter offsets + try { + NARCContents scriptNARC = scriptNarc; + for (int i = 0; i < 3; i++) { + int starter = newStarters.get(i).number; + OffsetWithinEntry[] thisStarter = romEntry.offsetArrayEntries + .get("StarterOffsets" + (i + 1)); + for (OffsetWithinEntry entry : thisStarter) { + writeWord(scriptNARC.files.get(entry.entry), entry.offset, + starter); + } + } + // GIVE ME BACK MY PURRLOIN + if (romEntry.romType == Type_BW2) { + byte[] newScript = new byte[] { 0x28, 0x00, (byte) 0xA1, 0x40, + 0x04, 0x00, (byte) 0xDE, 0x00, 0x00, 0x00, (byte) 0xFD, + 0x01, 0x05, 0x00 }; + if (romEntry.romCode.charAt(3) == 'J') { + newScript[0x6] -= 4; + } + byte[] oldFile = scriptNARC.files.get(romEntry + .getInt("PokedexGivenFileOffset")); + byte[] newFile = new byte[oldFile.length + newScript.length]; + int offset = find(oldFile, "2800A1400400"); + if (offset > 0) { + System.arraycopy(oldFile, 0, newFile, 0, oldFile.length); + System.arraycopy(newScript, 0, newFile, oldFile.length, + newScript.length); + newFile[offset++] = 0x1E; + newFile[offset++] = 0x0; + writeRelativePointer(newFile, offset, oldFile.length); + scriptNARC.files.set( + romEntry.getInt("PokedexGivenFileOffset"), newFile); + } + } else { + byte[] newScript = new byte[] { 0x24, 0x00, (byte) 0xA7, 0x02, + (byte) 0xE7, 0x00, 0x00, 0x00, (byte) 0xDE, 0x00, 0x00, + 0x00, (byte) 0xF8, 0x01, 0x05, 0x00 }; + if (romEntry.romCode.charAt(3) == 'J') { + newScript[0x4] -= 4; + newScript[0x8] -= 4; + } + byte[] oldFile = scriptNARC.files.get(romEntry + .getInt("PokedexGivenFileOffset")); + byte[] newFile = new byte[oldFile.length + newScript.length]; + int offset = find(oldFile, "2400A702"); + if (offset > 0) { + System.arraycopy(oldFile, 0, newFile, 0, oldFile.length); + System.arraycopy(newScript, 0, newFile, oldFile.length, + newScript.length); + newFile[offset++] = 0x04; + newFile[offset++] = 0x0; + writeRelativePointer(newFile, offset, oldFile.length); + scriptNARC.files.set( + romEntry.getInt("PokedexGivenFileOffset"), newFile); + } + } + + // Starter sprites + NARCContents starterNARC = this.readNARC(romEntry + .getString("StarterGraphics")); + NARCContents pokespritesNARC = this.readNARC(romEntry + .getString("PokemonGraphics")); + replaceStarterFiles(starterNARC, pokespritesNARC, 0, + newStarters.get(0).number); + replaceStarterFiles(starterNARC, pokespritesNARC, 1, + newStarters.get(1).number); + replaceStarterFiles(starterNARC, pokespritesNARC, 2, + newStarters.get(2).number); + writeNARC(romEntry.getString("StarterGraphics"), starterNARC); + } catch (IOException ex) { + return false; + } catch (InterruptedException e) { + return false; + } + // Fix text depending on version + if (romEntry.romType == Type_BW) { + List yourHouseStrings = getStrings(true, + romEntry.getInt("StarterLocationTextOffset")); + for (int i = 0; i < 3; i++) { + yourHouseStrings.set(18 - i, "\\xF000\\xBD02\\x0000The " + + newStarters.get(i).primaryType.camelCase() + + "-type Pok\\x00E9mon\\xFFFE\\xF000\\xBD02\\x0000" + + newStarters.get(i).name); + } + // Update what the friends say + yourHouseStrings + .set(26, + "Cheren: Hey, how come you get to pick\\xFFFEout my Pok\\x00E9mon?" + + "\\xF000\\xBE01\\x0000\\xFFFEOh, never mind. I wanted this one\\xFFFEfrom the start, anyway." + + "\\xF000\\xBE01\\x0000"); + yourHouseStrings + .set(53, + "It's decided. You'll be my opponent...\\xFFFEin our first Pok\\x00E9mon battle!" + + "\\xF000\\xBE01\\x0000\\xFFFELet's see what you can do, \\xFFFEmy Pok\\x00E9mon!" + + "\\xF000\\xBE01\\x0000"); + + // rewrite + setStrings(true, romEntry.getInt("StarterLocationTextOffset"), + yourHouseStrings); + } else { + List starterTownStrings = getStrings(true, + romEntry.getInt("StarterLocationTextOffset")); + for (int i = 0; i < 3; i++) { + starterTownStrings.set(37 - i, "\\xF000\\xBD02\\x0000The " + + newStarters.get(i).primaryType.camelCase() + + "-type Pok\\x00E9mon\\xFFFE\\xF000\\xBD02\\x0000" + + newStarters.get(i).name); + } + // Update what the rival says + starterTownStrings + .set(60, + "\\xF000\\x0100\\x0001\\x0001: Let's see how good\\xFFFEa Trainer you are!" + + "\\xF000\\xBE01\\x0000\\xFFFEI'll use my Pok\\x00E9mon" + + "\\xFFFEthat I raised from an Egg!\\xF000\\xBE01\\x0000"); + + // rewrite + setStrings(true, romEntry.getInt("StarterLocationTextOffset"), + starterTownStrings); + } + return true; + } + + @Override + public List getStarterHeldItems() { + // do nothing + return new ArrayList(); + } + + @Override + public void setStarterHeldItems(List items) { + // do nothing + } + + private void replaceStarterFiles(NARCContents starterNARC, + NARCContents pokespritesNARC, int starterIndex, int pokeNumber) + throws IOException, InterruptedException { + starterNARC.files.set(starterIndex * 2, + pokespritesNARC.files.get(pokeNumber * 20 + 18)); + // Get the picture... + byte[] compressedPic = pokespritesNARC.files.get(pokeNumber * 20); + // Decompress it with JavaDSDecmp + int[] ucp = JavaDSDecmp.Decompress(new HexInputStream( + new ByteArrayInputStream(compressedPic))); + byte[] uncompressedPic = convIntArrToByteArr(ucp); + starterNARC.files.set(12 + starterIndex, uncompressedPic); + } + + private byte[] convIntArrToByteArr(int[] arg) { + byte[] out = new byte[arg.length]; + for (int i = 0; i < arg.length; i++) { + out[i] = (byte) arg[i]; + } + return out; + } + + @Override + public void shufflePokemonStats() { + for (int i = 1; i <= 649; i++) { + pokes[i].shuffleStats(); + } + + } + + @Override + public List getMoves() { + return Arrays.asList(moves); + } + + @Override + public List getEncounters(boolean useTimeOfDay) { + try { + NARCContents encounterNARC = readNARC(romEntry + .getString("WildPokemon")); + List encounters = new ArrayList(); + int idx = -1; + for (byte[] entry : encounterNARC.files) { + idx++; + if (entry.length > 232 && useTimeOfDay) { + for (int i = 0; i < 4; i++) { + processEncounterEntry(encounters, entry, i * 232); + } + } else { + processEncounterEntry(encounters, entry, 0); + } + for (EncounterSet es : encounters) { + if (es.rate == -1) { + es.rate = idx; + } + } + } + return encounters; + } catch (IOException e) { + // whuh-oh + e.printStackTrace(); + return new ArrayList(); + } + } + + private void processEncounterEntry(List encounters, + byte[] entry, int startOffset) { + int[] amounts = new int[] { 12, 12, 12, 5, 5, 5, 5 }; + + int offset = 8; + for (int i = 0; i < 7; i++) { + int rate = entry[startOffset + i] & 0xFF; + if (rate != 0) { + List encs = readEncounters(entry, startOffset + + offset, amounts[i]); + EncounterSet area = new EncounterSet(); + area.rate = -1; + area.encounters = encs; + encounters.add(area); + } + offset += amounts[i] * 4; + } + + } + + private List readEncounters(byte[] data, int offset, int number) { + List encs = new ArrayList(); + for (int i = 0; i < number; i++) { + Encounter enc1 = new Encounter(); + enc1.pokemon = pokes[((data[offset + i * 4] & 0xFF) + ((data[offset + + 1 + i * 4] & 0x03) << 8))]; + enc1.level = data[offset + 2 + i * 4] & 0xFF; + enc1.maxLevel = data[offset + 3 + i * 4] & 0xFF; + encs.add(enc1); + } + return encs; + } + + /* @formatter:off */ + @SuppressWarnings("unused") + private static final int[][] habitatListEntries = new int[][] { + { 104, 105 }, // Route 4 + { 124 }, // Route 15 + { 134 }, // Route 21 + { 84, 85, 86 }, // Clay Tunnel + { 23, 24, 25, 26 }, // Twist Mountain + { 97 }, // Village Bridge + { 27, 28, 29, 30 }, // Dragonspiral Tower + { 81, 82, 83 }, // Relic Passage + { 106 }, // Route 5* + { 125 }, // Route 16* + { 98 }, // Marvelous Bridge + { 123 }, // Abundant Shrine + { 132 }, // Undella Town + { 107 }, // Route 6 + { 43 }, // Undella Bay + { 102, 103 }, // Wellspring Cave + { 95 }, // Nature Preserve + { 127 }, // Route 18 + { 32, 33, 34, 35, 36 }, // Giant Chasm + { 111 }, // Route 7 + { 31, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 }, // Victory Road + { 12, 13, 14, 15, 16, 17, 18, 19 }, // Relic Castle + { 0 }, // Striation City + { 128 }, // Route 19 + { 3 }, // Aspertia City + { 116 }, // Route 8* + { 44, 45 }, // Floccesy Ranch + { 61, 62, 63, 64, 65, 66, 67, 68, 69, 70 }, // Strange House + { 129 }, // Route 20 + { 4 }, // Virbank City + { 37, 38, 39, 40, 41 }, // Castelia Sewers + { 118 }, // Route 9 + { 46, 47 }, // Virbank Complex + { 42 }, // P2 Laboratory + { 1 }, // Castelia City + { 8, 9 }, // Pinwheel Forest + { 5 }, // Humilau City + { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 }, // Reversal Mountain + { 6, 7 }, // Dreamyard + { 112, 113, 114, 115 }, // Celestial Tower + { 130 }, // Route 22 + { 10, 11 }, // Desert Resort + { 119 }, // Route 11 + { 133 }, // Route 17 + { 99 }, // Route 1 + { 131 }, // Route 23 + { 2 }, // Icirrus City* + { 120 }, // Route 12 + { 100 }, // Route 2 + { 108, 109 }, // Mistralton Cave + { 121 }, // Route 13 + { 101 }, // Route 3 + { 117 }, // Moor of Icirrus* + { 96 }, // Driftveil Drawbridge + { 93, 94 }, // Seaside Cave + { 126 }, // Lostlorn Forest + { 122 }, // Route 14 + { 20, 21, 22 }, // Chargestone Cave + }; + + private static final int[] wildFileToAreaMap = new int[] { + 2, + 4, + 8, + 59, + 61, + 63, + 19, 19, + 20, 20, + 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, + 24, 24, 24, + 25, 25, 25, 25, + 26, 26, 26, 26, + 76, + 27, 27, 27, 27, 27, + 70, 70, 70, 70, 70, + 29, + 35, + 71, 71, + 72, 72, + 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, + 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76, + 77, 77, 77, + 79, 79, 79, 79, 79, 79, 79, 79, 79, + 78, 78, + -1, // Nature Preserve (not on map) + 55, + 57, + 58, + 37, + 38, + 39, + 30, 30, + 40, 40, + 41, + 42, + 31, 31, 31, + 43, + 32, 32, 32, 32, + 44, + 33, + 45, + 46, + 47, + 48, + 49, + 34, + 50, + 51, + 36, + 53, + 66, + 67, + 69, + 75, + 12, + 52, + 68, + }; + /* @formatter:on */ + + @Override + public void setEncounters(boolean useTimeOfDay, + List encountersList) { + try { + NARCContents encounterNARC = readNARC(romEntry + .getString("WildPokemon")); + Iterator encounters = encountersList.iterator(); + for (byte[] entry : encounterNARC.files) { + writeEncounterEntry(encounters, entry, 0); + if (entry.length > 232) { + if (useTimeOfDay) { + for (int i = 1; i < 4; i++) { + writeEncounterEntry(encounters, entry, i * 232); + } + } else { + // copy for other 3 seasons + System.arraycopy(entry, 0, entry, 232, 232); + System.arraycopy(entry, 0, entry, 464, 232); + System.arraycopy(entry, 0, entry, 696, 232); + } + } + } + + // Save + writeNARC(romEntry.getString("WildPokemon"), encounterNARC); + + // Habitat List / Area Data? + if (romEntry.romType == Type_BW2) { + // disabled: habitat list changes cause a crash if too many + // entries for now. + + // NARCContents habitatNARC = readNARC(romEntry + // .getString("HabitatList")); + // for (int i = 0; i < habitatNARC.files.size(); i++) { + // byte[] oldEntry = habitatNARC.files.get(i); + // int[] encounterFiles = habitatListEntries[i]; + // Map pokemonHere = new TreeMap(); + // for (int encFile : encounterFiles) { + // byte[] encEntry = encounterNARC.files.get(encFile); + // if (encEntry.length > 232) { + // for (int s = 0; s < 4; s++) { + // addHabitats(encEntry, s * 232, pokemonHere, s); + // } + // } else { + // for (int s = 0; s < 4; s++) { + // addHabitats(encEntry, 0, pokemonHere, s); + // } + // } + // } + // // Make the new file + // byte[] habitatEntry = new byte[10 + pokemonHere.size() * 28]; + // System.arraycopy(oldEntry, 0, habitatEntry, 0, 10); + // habitatEntry[8] = (byte) pokemonHere.size(); + // // 28-byte entries for each pokemon + // int num = -1; + // for (Pokemon pkmn : pokemonHere.keySet()) { + // num++; + // writeWord(habitatEntry, 10 + num * 28, pkmn.number); + // byte[] slots = pokemonHere.get(pkmn); + // System.arraycopy(slots, 0, habitatEntry, 12 + num * 28, + // 12); + // } + // // Save + // habitatNARC.files.set(i, habitatEntry); + // } + // // Save habitat + // this.writeNARC(romEntry.getString("HabitatList"), + // habitatNARC); + + // Area Data + NARCContents areaNARC = this.readNARC(romEntry + .getString("PokemonAreaData")); + List newFiles = new ArrayList(); + for (int i = 0; i < 649; i++) { + byte[] nf = new byte[345]; + nf[0] = 1; + newFiles.add(nf); + } + // Get data now + for (int i = 0; i < encounterNARC.files.size(); i++) { + byte[] encEntry = encounterNARC.files.get(i); + if (encEntry.length > 232) { + for (int s = 0; s < 4; s++) { + parseAreaData(encEntry, s * 232, newFiles, s, i); + } + } else { + for (int s = 0; s < 4; s++) { + parseAreaData(encEntry, 0, newFiles, s, i); + } + } + } + // Now update unobtainables & save + for (int i = 0; i < 649; i++) { + byte[] file = newFiles.get(i); + for (int s = 0; s < 4; s++) { + boolean unobtainable = true; + for (int e = 0; e < 85; e++) { + if (file[s * 86 + e + 2] != 0) { + unobtainable = false; + break; + } + } + if (unobtainable) { + file[s * 86 + 1] = 1; + } + } + areaNARC.files.set(i, file); + } + // Save + this.writeNARC(romEntry.getString("PokemonAreaData"), areaNARC); + } + } catch (IOException e) { + // whuh-oh + e.printStackTrace(); + } + + } + + private void parseAreaData(byte[] entry, int startOffset, + List areaData, int season, int fileNumber) { + int[] amounts = new int[] { 12, 12, 12, 5, 5, 5, 5 }; + + int offset = 8; + for (int i = 0; i < 7; i++) { + int rate = entry[startOffset + i] & 0xFF; + if (rate != 0) { + for (int e = 0; e < amounts[i]; e++) { + Pokemon pkmn = pokes[((entry[startOffset + offset + e * 4] & 0xFF) + ((entry[startOffset + + offset + 1 + e * 4] & 0x03) << 8))]; + byte[] pokeFile = areaData.get(pkmn.number - 1); + int areaIndex = wildFileToAreaMap[fileNumber]; + // Route 4? + if (areaIndex == 40) { + if ((fileNumber == 104 && romEntry.romCode.charAt(2) == 'D') + || (fileNumber == 105 && romEntry.romCode + .charAt(2) == 'E')) { + areaIndex = -1; // wrong version + } + } + // Victory Road? + if (areaIndex == 76) { + if (romEntry.romCode.charAt(2) == 'D') { + // White 2 + if (fileNumber == 71 || fileNumber == 73) { + areaIndex = -1; // wrong version + } + } else { + // Black 2 + if (fileNumber == 78 || fileNumber == 79) { + areaIndex = -1; // wrong version + } + } + } + // Reversal Mountain? + if (areaIndex == 73) { + if (romEntry.romCode.charAt(2) == 'D') { + // White 2 + if (fileNumber >= 49 && fileNumber <= 54) { + areaIndex = -1; // wrong version + } + } else { + // Black 2 + if (fileNumber >= 55 && fileNumber <= 60) { + areaIndex = -1; // wrong version + } + } + } + // Skip stuff that isn't on the map or is wrong version + if (areaIndex != -1) { + pokeFile[season * 86 + 2 + areaIndex] |= (1 << i); + } + } + } + offset += amounts[i] * 4; + } + } + + @SuppressWarnings("unused") + private void addHabitats(byte[] entry, int startOffset, + Map pokemonHere, int season) { + int[] amounts = new int[] { 12, 12, 12, 5, 5, 5, 5 }; + int[] type = new int[] { 0, 0, 0, 1, 1, 2, 2 }; + + int offset = 8; + for (int i = 0; i < 7; i++) { + int rate = entry[startOffset + i] & 0xFF; + if (rate != 0) { + for (int e = 0; e < amounts[i]; e++) { + Pokemon pkmn = pokes[((entry[startOffset + offset + e * 4] & 0xFF) + ((entry[startOffset + + offset + 1 + e * 4] & 0x03) << 8))]; + if (pokemonHere.containsKey(pkmn)) { + pokemonHere.get(pkmn)[type[i] + season * 3] = 1; + } else { + byte[] locs = new byte[12]; + locs[type[i] + season * 3] = 1; + pokemonHere.put(pkmn, locs); + } + } + } + offset += amounts[i] * 4; + } + } + + private void writeEncounterEntry(Iterator encounters, + byte[] entry, int startOffset) { + int[] amounts = new int[] { 12, 12, 12, 5, 5, 5, 5 }; + + int offset = 8; + for (int i = 0; i < 7; i++) { + int rate = entry[startOffset + i] & 0xFF; + if (rate != 0) { + EncounterSet area = encounters.next(); + for (int j = 0; j < amounts[i]; j++) { + Encounter enc = area.encounters.get(j); + writeWord(entry, startOffset + offset + j * 4, + enc.pokemon.number); + entry[startOffset + offset + j * 4 + 2] = (byte) enc.level; + entry[startOffset + offset + j * 4 + 3] = (byte) enc.maxLevel; + } + } + offset += amounts[i] * 4; + } + } + + @Override + public List getTrainers() { + List allTrainers = new ArrayList(); + try { + NARCContents trainers = this.readNARC(romEntry + .getString("TrainerData")); + NARCContents trpokes = this.readNARC(romEntry + .getString("TrainerPokemon")); + int trainernum = trainers.files.size(); + List tclasses = this.getTrainerClassNames(); + List tnames = this.getTrainerNames(); + for (int i = 1; i < trainernum; i++) { + byte[] trainer = trainers.files.get(i); + byte[] trpoke = trpokes.files.get(i); + Trainer tr = new Trainer(); + tr.poketype = trainer[0] & 0xFF; + tr.offset = trainer[1] & 0xFF; + tr.trainerclass = trainer[1] & 0xFF; + int numPokes = trainer[3] & 0xFF; + int pokeOffs = 0; + tr.fullDisplayName = tclasses.get(tr.trainerclass) + " " + + tnames.get(i - 1); + // printBA(trpoke); + for (int poke = 0; poke < numPokes; poke++) { + // Structure is + // AI SB LV LV SP SP FRM FRM + // (HI HI) + // (M1 M1 M2 M2 M3 M3 M4 M4) + // where SB = 0 0 Ab Ab 0 0 Fm Ml + // Ab Ab = ability number, 0 for random + // Fm = 1 for forced female + // Ml = 1 for forced male + // There's also a trainer flag to force gender, but + // this allows fixed teams with mixed genders. + + int ailevel = trpoke[pokeOffs] & 0xFF; + // int secondbyte = trpoke[pokeOffs + 1] & 0xFF; + int level = readWord(trpoke, pokeOffs + 2); + int species = readWord(trpoke, pokeOffs + 4); + // int formnum = readWord(trpoke, pokeOffs + 6); + TrainerPokemon tpk = new TrainerPokemon(); + tpk.level = level; + tpk.pokemon = pokes[species]; + tpk.AILevel = ailevel; + tpk.ability = trpoke[pokeOffs + 1] & 0xFF; + pokeOffs += 8; + if (tr.poketype >= 2) { + int heldItem = readWord(trpoke, pokeOffs); + tpk.heldItem = heldItem; + pokeOffs += 2; + } + if (tr.poketype % 2 == 1) { + int attack1 = readWord(trpoke, pokeOffs); + int attack2 = readWord(trpoke, pokeOffs + 2); + int attack3 = readWord(trpoke, pokeOffs + 4); + int attack4 = readWord(trpoke, pokeOffs + 6); + tpk.move1 = attack1; + tpk.move2 = attack2; + tpk.move3 = attack3; + tpk.move4 = attack4; + pokeOffs += 8; + } + tr.pokemon.add(tpk); + } + allTrainers.add(tr); + } + if (romEntry.romType == Type_BW) { + tagTrainersBW(allTrainers); + } else { + if (!romEntry.getString("DriftveilPokemon").isEmpty()) { + NARCContents driftveil = this.readNARC(romEntry + .getString("DriftveilPokemon")); + for (int trno = 0; trno < 2; trno++) { + Trainer tr = new Trainer(); + tr.poketype = 3; + tr.offset = 0; + for (int poke = 0; poke < 3; poke++) { + byte[] pkmndata = driftveil.files.get(trno * 3 + + poke + 1); + TrainerPokemon tpk = new TrainerPokemon(); + tpk.level = 25; + tpk.pokemon = pokes[readWord(pkmndata, 0)]; + tpk.AILevel = 255; + tpk.heldItem = readWord(pkmndata, 12); + tpk.move1 = readWord(pkmndata, 2); + tpk.move2 = readWord(pkmndata, 2); + tpk.move3 = readWord(pkmndata, 2); + tpk.move4 = readWord(pkmndata, 2); + tr.pokemon.add(tpk); + } + allTrainers.add(tr); + } + } + tagTrainersBW2(allTrainers); + } + } catch (IOException ex) { + // change this later + ex.printStackTrace(); + } + return allTrainers; + } + + private void tagTrainersBW(List trs) { + // We use different Gym IDs to cheat the system for the 3 n00bs + // Chili, Cress, and Cilan + // Cilan can be GYM1, then Chili is GYM9 and Cress GYM10 + // Also their *trainers* are GYM11 lol + + // Gym Trainers + tag(trs, "GYM11", 0x09, 0x0A); + tag(trs, "GYM2", 0x56, 0x57, 0x58); + tag(trs, "GYM3", 0xC4, 0xC6, 0xC7, 0xC8); + tag(trs, "GYM4", 0x42, 0x43, 0x44, 0x45); + tag(trs, "GYM5", 0xC9, 0xCA, 0xCB, 0x5F, 0xA8); + tag(trs, "GYM6", 0x7D, 0x7F, 0x80, 0x46, 0x47); + tag(trs, "GYM7", 0xD7, 0xD8, 0xD9, 0xD4, 0xD5, 0xD6); + tag(trs, "GYM8", 0x109, 0x10A, 0x10F, 0x10E, 0x110, 0x10B, 0x113, 0x112); + + // Gym Leaders + tag(trs, 0x0C, "GYM1"); // Cilan + tag(trs, 0x0B, "GYM9"); // Chili + tag(trs, 0x0D, "GYM10"); // Cress + tag(trs, 0x15, "GYM2"); // Lenora + tag(trs, 0x16, "GYM3"); // Burgh + tag(trs, 0x17, "GYM4"); // Elesa + tag(trs, 0x18, "GYM5"); // Clay + tag(trs, 0x19, "GYM6"); // Skyla + tag(trs, 0x83, "GYM7"); // Brycen + tag(trs, 0x84, "GYM8"); // Iris or Drayden + tag(trs, 0x85, "GYM8"); // Iris or Drayden + + // Elite 4 + tag(trs, 0xE4, "ELITE1"); // Shauntal + tag(trs, 0xE6, "ELITE2"); // Grimsley + tag(trs, 0xE7, "ELITE3"); // Caitlin + tag(trs, 0xE5, "ELITE4"); // Marshal + + // Elite 4 R2 + tag(trs, 0x233, "ELITE1"); // Shauntal + tag(trs, 0x235, "ELITE2"); // Grimsley + tag(trs, 0x236, "ELITE3"); // Caitlin + tag(trs, 0x234, "ELITE4"); // Marshal + tag(trs, 0x197, "CHAMPION"); // Alder + + // Ubers? + tag(trs, 0x21E, "UBER"); // Game Freak Guy + tag(trs, 0x237, "UBER"); // Cynthia + tag(trs, 0xE8, "UBER"); // Ghetsis + tag(trs, 0x24A, "UBER"); // N-White + tag(trs, 0x24B, "UBER"); // N-Black + + // Rival - Cheren + tagRivalBW(trs, "RIVAL1", 0x35); + tagRivalBW(trs, "RIVAL2", 0x11F); + tagRivalBW(trs, "RIVAL3", 0x38); // used for 3rd battle AND tag battle + tagRivalBW(trs, "RIVAL4", 0x193); + tagRivalBW(trs, "RIVAL5", 0x5A); // 5th battle & 2nd tag battle + tagRivalBW(trs, "RIVAL6", 0x21B); + tagRivalBW(trs, "RIVAL7", 0x24C); + tagRivalBW(trs, "RIVAL8", 0x24F); + + // Rival - Bianca + tagRivalBW(trs, "FRIEND1", 0x3B); + tagRivalBW(trs, "FRIEND2", 0x1F2); + tagRivalBW(trs, "FRIEND3", 0x1FB); + tagRivalBW(trs, "FRIEND4", 0x1EB); + tagRivalBW(trs, "FRIEND5", 0x1EE); + tagRivalBW(trs, "FRIEND6", 0x252); + } + + private void tagTrainersBW2(List trs) { + // Use GYM9/10/11 for the retired Chili/Cress/Cilan. + // Lenora doesn't have a team, or she'd be 12. + // Likewise for Brycen + + // Some trainers have TWO teams because of Challenge Mode + // I believe this is limited to Gym Leaders, E4, Champ... + // The "Challenge Mode" teams have levels at similar to regular, + // but have the normal boost applied too. + + // Gym Trainers + tag(trs, "GYM1", 0xab, 0xac); + tag(trs, "GYM2", 0xb2, 0xb3); + tag(trs, "GYM3", 0x2de, 0x2df, 0x2e0, 0x2e1); + // GYM4: old gym site included to give the city a theme + tag(trs, "GYM4", 0x26d, 0x94, 0xcf, 0xd0, 0xd1); // 0x94 might be 0x324 + tag(trs, "GYM5", 0x13f, 0x140, 0x141, 0x142, 0x143, 0x144, 0x145); + tag(trs, "GYM6", 0x95, 0x96, 0x97, 0x98, 0x14c); + tag(trs, "GYM7", 0x17d, 0x17e, 0x17f, 0x180, 0x181); + tag(trs, "GYM8", 0x15e, 0x15f, 0x160, 0x161, 0x162, 0x163); + + // Gym Leaders + // Order: Normal, Challenge Mode + // All the challenge mode teams are near the end of the ROM + // which makes things a bit easier. + tag(trs, "GYM1", 0x9c, 0x2fc); // Cheren + tag(trs, "GYM2", 0x9d, 0x2fd); // Roxie + tag(trs, "GYM3", 0x9a, 0x2fe); // Burgh + tag(trs, "GYM4", 0x99, 0x2ff); // Elesa + tag(trs, "GYM5", 0x9e, 0x300); // Clay + tag(trs, "GYM6", 0x9b, 0x301); // Skyla + tag(trs, "GYM7", 0x9f, 0x302); // Drayden + tag(trs, "GYM8", 0xa0, 0x303); // Marlon + + // Elite 4 / Champion + // Order: Normal, Challenge Mode, Rematch, Rematch Challenge Mode + tag(trs, "ELITE1", 0x26, 0x304, 0x8f, 0x309); + tag(trs, "ELITE2", 0x28, 0x305, 0x91, 0x30a); + tag(trs, "ELITE3", 0x29, 0x307, 0x92, 0x30c); + tag(trs, "ELITE4", 0x27, 0x306, 0x90, 0x30b); + tag(trs, "CHAMPION", 0x155, 0x308, 0x218, 0x30d); + + // Rival - Hugh + tagRivalBW(trs, "RIVAL1", 0xa1); // Start + tagRivalBW(trs, "RIVAL2", 0xa6); // Floccessy Ranch + tagRivalBW(trs, "RIVAL3", 0x24c); // Tag Battles in the sewers + tagRivalBW(trs, "RIVAL4", 0x170); // Tag Battle on the Plasma Frigate + tagRivalBW(trs, "RIVAL5", 0x17a); // Undella Town 1st visit + tagRivalBW(trs, "RIVAL6", 0x2bd); // Lacunosa Town Tag Battle + tagRivalBW(trs, "RIVAL7", 0x31a); // 2nd Plasma Frigate Tag Battle + tagRivalBW(trs, "RIVAL8", 0x2ac); // Victory Road + tagRivalBW(trs, "RIVAL9", 0x2b5); // Undella Town Post-E4 + tagRivalBW(trs, "RIVAL10", 0x2b8); // Driftveil Post-Undella-Battle + + // Tag Battle with Opposite Gender Hero + tagRivalBW(trs, "FRIEND1", 0x168); + tagRivalBW(trs, "FRIEND1", 0x16b); + + // Tag/PWT Battles with Cheren + tag(trs, "GYM1", 0x173, 0x278, 0x32E); + + // The Restaurant Brothers + tag(trs, "GYM9", 0x1f0); // Cilan + tag(trs, "GYM10", 0x1ee); // Chili + tag(trs, "GYM11", 0x1ef); // Cress + + // Themed Trainers + tag(trs, "THEMED:ZINZOLIN", 0x2c0, 0x248, 0x15b); + tag(trs, "THEMED:COLRESS", 0x166, 0x158, 0x32d, 0x32f); + tag(trs, "THEMED:SHADOW1", 0x247, 0x15c, 0x2af); + tag(trs, "THEMED:SHADOW2", 0x1f2, 0x2b0); + tag(trs, "THEMED:SHADOW3", 0x1f3, 0x2b1); + + // Uber-Trainers + // There are *fourteen* ubers of 17 allowed (incl. the champion) + // It's a rather stacked game... + tag(trs, 0x246, "UBER"); // Alder + tag(trs, 0x1c8, "UBER"); // Cynthia + tag(trs, 0xca, "UBER"); // Benga/BlackTower + tag(trs, 0xc9, "UBER"); // Benga/WhiteTreehollow + tag(trs, 0x5, "UBER"); // N/Zekrom + tag(trs, 0x6, "UBER"); // N/Reshiram + tag(trs, 0x30e, "UBER"); // N/Spring + tag(trs, 0x30f, "UBER"); // N/Summer + tag(trs, 0x310, "UBER"); // N/Autumn + tag(trs, 0x311, "UBER"); // N/Winter + tag(trs, 0x159, "UBER"); // Ghetsis + tag(trs, 0x8c, "UBER"); // Game Freak Guy + tag(trs, 0x24f, "UBER"); // Game Freak Leftovers Guy + + } + + private void tagRivalBW(List allTrainers, String tag, int offset) { + allTrainers.get(offset - 1).tag = tag + "-0"; + allTrainers.get(offset).tag = tag + "-1"; + allTrainers.get(offset + 1).tag = tag + "-2"; + + } + + private void tag(List allTrainers, int number, String tag) { + if (allTrainers.size() > (number - 1)) { + allTrainers.get(number - 1).tag = tag; + } + } + + private void tag(List allTrainers, String tag, int... numbers) { + for (int num : numbers) { + if (allTrainers.size() > (num - 1)) { + allTrainers.get(num - 1).tag = tag; + } + } + } + + @Override + public void setTrainers(List trainerData) { + Iterator allTrainers = trainerData.iterator(); + try { + NARCContents trainers = this.readNARC(romEntry + .getString("TrainerData")); + NARCContents trpokes = new NARCContents(); + // empty entry + trpokes.files.add(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); + int trainernum = trainers.files.size(); + for (int i = 1; i < trainernum; i++) { + byte[] trainer = trainers.files.get(i); + Trainer tr = allTrainers.next(); + tr.poketype = 0; // write as type 0 for no item/moves + trainer[0] = (byte) tr.poketype; + int numPokes = tr.pokemon.size(); + trainer[3] = (byte) numPokes; + + int bytesNeeded = 8 * numPokes; + if (tr.poketype % 2 == 1) { + bytesNeeded += 8 * numPokes; + } + if (tr.poketype >= 2) { + bytesNeeded += 2 * numPokes; + } + byte[] trpoke = new byte[bytesNeeded]; + int pokeOffs = 0; + Iterator tpokes = tr.pokemon.iterator(); + for (int poke = 0; poke < numPokes; poke++) { + TrainerPokemon tpk = tpokes.next(); + trpoke[pokeOffs] = (byte) tpk.AILevel; + // no gender or ability info, so no byte 1 + writeWord(trpoke, pokeOffs + 2, tpk.level); + writeWord(trpoke, pokeOffs + 4, tpk.pokemon.number); + // no form info, so no byte 6/7 + pokeOffs += 8; + if (tr.poketype >= 2) { + writeWord(trpoke, pokeOffs, tpk.heldItem); + pokeOffs += 2; + } + if (tr.poketype % 2 == 1) { + writeWord(trpoke, pokeOffs, tpk.move1); + writeWord(trpoke, pokeOffs + 2, tpk.move2); + writeWord(trpoke, pokeOffs + 4, tpk.move3); + writeWord(trpoke, pokeOffs + 6, tpk.move4); + pokeOffs += 8; + } + } + trpokes.files.add(trpoke); + } + this.writeNARC(romEntry.getString("TrainerData"), trainers); + this.writeNARC(romEntry.getString("TrainerPokemon"), trpokes); + // Deal with PWT + if (romEntry.romType == Type_BW2 + && !romEntry.getString("DriftveilPokemon").isEmpty()) { + NARCContents driftveil = this.readNARC(romEntry + .getString("DriftveilPokemon")); + Map> movesets = this.getMovesLearnt(); + for (int trno = 0; trno < 2; trno++) { + Trainer tr = allTrainers.next(); + Iterator tpks = tr.pokemon.iterator(); + for (int poke = 0; poke < 3; poke++) { + byte[] pkmndata = driftveil.files.get(trno * 3 + poke + + 1); + TrainerPokemon tpk = tpks.next(); + // pokemon and held item + writeWord(pkmndata, 0, tpk.pokemon.number); + writeWord(pkmndata, 12, tpk.heldItem); + // pick 4 moves, based on moveset@25 + int[] moves = new int[4]; + int moveCount = 0; + List set = movesets.get(tpk.pokemon); + for (int i = 0; i < set.size(); i++) { + MoveLearnt ml = set.get(i); + if (ml.level > 25) { + break; + } + // unconditional learn? + if (moveCount < 4) { + moves[moveCount++] = ml.move; + } else { + // already knows? + boolean doTeach = true; + for (int j = 0; j < 4; j++) { + if (moves[j] == ml.move) { + doTeach = false; + break; + } + } + if (doTeach) { + // shift up + for (int j = 0; j < 3; j++) { + moves[j] = moves[j + 1]; + } + moves[3] = ml.move; + } + } + } + // write the moveset we calculated + for (int i = 0; i < 4; i++) { + writeWord(pkmndata, 2 + i * 2, moves[i]); + } + } + } + this.writeNARC(romEntry.getString("DriftveilPokemon"), + driftveil); + } + } catch (IOException ex) { + // change this later + ex.printStackTrace(); + } + } + + @Override + public Map> getMovesLearnt() { + Map> movesets = new TreeMap>(); + try { + NARCContents movesLearnt = this.readNARC(romEntry + .getString("PokemonMovesets")); + for (int i = 1; i <= 649; i++) { + Pokemon pkmn = pokes[i]; + byte[] movedata = movesLearnt.files.get(i); + int moveDataLoc = 0; + List learnt = new ArrayList(); + while (readWord(movedata, moveDataLoc) != 0xFFFF + || readWord(movedata, moveDataLoc + 2) != 0xFFFF) { + int move = readWord(movedata, moveDataLoc); + int level = readWord(movedata, moveDataLoc + 2); + MoveLearnt ml = new MoveLearnt(); + ml.level = level; + ml.move = move; + learnt.add(ml); + moveDataLoc += 4; + } + movesets.put(pkmn, learnt); + } + } catch (IOException e) { + // change this later + e.printStackTrace(); + } + return movesets; + } + + @Override + public void setMovesLearnt(Map> movesets) { + // Backup of movesets for later + Map> oldSets = this.getMovesLearnt(); + try { + NARCContents movesLearnt = readNARC(romEntry + .getString("PokemonMovesets")); + for (int i = 1; i <= 649; i++) { + Pokemon pkmn = pokes[i]; + List learnt = movesets.get(pkmn); + int sizeNeeded = learnt.size() * 4 + 4; + byte[] moveset = new byte[sizeNeeded]; + int j = 0; + for (; j < learnt.size(); j++) { + MoveLearnt ml = learnt.get(j); + writeWord(moveset, j * 4, ml.move); + writeWord(moveset, j * 4 + 2, ml.level); + } + writeWord(moveset, j * 4, 0xFFFF); + writeWord(moveset, j * 4 + 2, 0xFFFF); + movesLearnt.files.set(i, moveset); + } + // Save + this.writeNARC(romEntry.getString("PokemonMovesets"), movesLearnt); + } catch (IOException e) { + // change this later + e.printStackTrace(); + } + + } + + private static class StaticPokemon { + private int[] files; + private int[] offsets; + + public Pokemon getPokemon(Gen5RomHandler parent, NARCContents scriptNARC) { + return parent.pokes[parent.readWord(scriptNARC.files.get(files[0]), + offsets[0])]; + } + + public void setPokemon(Gen5RomHandler parent, NARCContents scriptNARC, + Pokemon pkmn) { + int value = pkmn.number; + for (int i = 0; i < offsets.length; i++) { + byte[] file = scriptNARC.files.get(files[i]); + parent.writeWord(file, offsets[i], value); + } + } + } + + @Override + public boolean canChangeStaticPokemon() { + return romEntry.staticPokemonSupport; + } + + @Override + public List getStaticPokemon() { + List sp = new ArrayList(); + if (!romEntry.staticPokemonSupport) { + return sp; + } + NARCContents scriptNARC = scriptNarc; + for (StaticPokemon statP : romEntry.staticPokemon) { + sp.add(statP.getPokemon(this, scriptNARC)); + } + return sp; + } + + @Override + public boolean setStaticPokemon(List staticPokemon) { + if (!romEntry.staticPokemonSupport) { + return false; + } + if (staticPokemon.size() != romEntry.staticPokemon.size()) { + return false; + } + Iterator statics = staticPokemon.iterator(); + NARCContents scriptNARC = scriptNarc; + for (StaticPokemon statP : romEntry.staticPokemon) { + statP.setPokemon(this, scriptNARC, statics.next()); + } + if (romEntry.offsetArrayEntries.containsKey("StaticPokemonFormValues")) { + OffsetWithinEntry[] formValues = romEntry.offsetArrayEntries + .get("StaticPokemonFormValues"); + for (OffsetWithinEntry owe : formValues) { + writeWord(scriptNARC.files.get(owe.entry), owe.offset, 0); + } + } + + return true; + } + + @Override + public boolean hasHiddenHollowPokemon() { + return romEntry.romType == Type_BW2; + } + + @Override + public void randomizeHiddenHollowPokemon() { + if (romEntry.romType != Type_BW2) { + return; + } + int[] allowedUnovaPokemon = new int[] { 505, 507, 510, 511, 513, 515, + 519, 523, 525, 527, 529, 531, 533, 535, 538, 539, 542, 545, + 546, 548, 550, 553, 556, 558, 559, 561, 564, 569, 572, 575, + 578, 580, 583, 587, 588, 594, 596, 601, 605, 607, 610, 613, + 616, 618, 619, 621, 622, 624, 626, 628, 630, 631, 632, }; + int randomSize = 493 + allowedUnovaPokemon.length; + try { + NARCContents hhNARC = this.readNARC(romEntry + .getString("HiddenHollows")); + for (byte[] hhEntry : hhNARC.files) { + for (int version = 0; version < 2; version++) { + for (int rarityslot = 0; rarityslot < 3; rarityslot++) { + for (int group = 0; group < 4; group++) { + int pokeChoice = RandomSource.nextInt(randomSize) + 1; + if (pokeChoice > 493) { + pokeChoice = allowedUnovaPokemon[pokeChoice - 494]; + } + writeWord(hhEntry, version * 78 + rarityslot * 26 + + group * 2, pokeChoice); + int genderRatio = RandomSource.nextInt(101); + hhEntry[version * 78 + rarityslot * 26 + 16 + group] = (byte) genderRatio; + hhEntry[version * 78 + rarityslot * 26 + 20 + group] = 0; // forme + } + } + } + // the rest of the file is items + } + this.writeNARC(romEntry.getString("HiddenHollows"), hhNARC); + } catch (IOException e) { + } + } + + @Override + public List getTMMoves() { + String tmDataPrefix = "87038803"; + int offset = find(arm9, tmDataPrefix); + if (offset > 0) { + offset += 4; // because it was a prefix + List tms = new ArrayList(); + for (int i = 0; i < 92; i++) { + tms.add(readWord(arm9, offset + i * 2)); + } + // Skip past first 92 TMs and 6 HMs + offset += 196; + for (int i = 0; i < 3; i++) { + tms.add(readWord(arm9, offset + i * 2)); + } + return tms; + } else { + return null; + } + } + + @Override + public List getHMMoves() { + String tmDataPrefix = "87038803"; + int offset = find(arm9, tmDataPrefix); + if (offset > 0) { + offset += 4; // because it was a prefix + offset += 184; // TM data + List hms = new ArrayList(); + for (int i = 0; i < 6; i++) { + hms.add(readWord(arm9, offset + i * 2)); + } + return hms; + } else { + return null; + } + } + + @Override + public void setTMMoves(List moveIndexes) { + String tmDataPrefix = "87038803"; + int offset = find(arm9, tmDataPrefix); + if (offset > 0) { + offset += 4; // because it was a prefix + for (int i = 0; i < 92; i++) { + writeWord(arm9, offset + i * 2, moveIndexes.get(i)); + } + // Skip past those 92 TMs and 6 HMs + offset += 196; + for (int i = 0; i < 3; i++) { + writeWord(arm9, offset + i * 2, moveIndexes.get(i + 92)); + } + + // Update TM item descriptions + List itemDescriptions = getStrings(false, + romEntry.getInt("ItemDescriptionsTextOffset")); + List moveDescriptions = getStrings(false, + romEntry.getInt("MoveDescriptionsTextOffset")); + // TM01 is item 328 and so on + for (int i = 0; i < 92; i++) { + itemDescriptions.set(i + 328, + moveDescriptions.get(moveIndexes.get(i))); + } + // TM93-95 are 618-620 + for (int i = 0; i < 3; i++) { + itemDescriptions.set(i + 618, + moveDescriptions.get(moveIndexes.get(i + 92))); + } + // Save the new item descriptions + setStrings(false, romEntry.getInt("ItemDescriptionsTextOffset"), + itemDescriptions); + // Palettes + String baseOfPalettes; + if (romEntry.romType == Type_BW) { + baseOfPalettes = "E903EA03020003000400050006000700"; + } else { + baseOfPalettes = "FD03FE03020003000400050006000700"; + } + int offsPals = find(arm9, baseOfPalettes); + if (offsPals > 0) { + // Write pals + for (int i = 0; i < 92; i++) { + int itmNum = 328 + i; + Move m = this.moves[moveIndexes.get(i)]; + int pal = this.typeTMPaletteNumber(m.type); + writeWord(arm9, offsPals + itmNum * 4 + 2, pal); + } + for (int i = 0; i < 3; i++) { + int itmNum = 618 + i; + Move m = this.moves[moveIndexes.get(i + 92)]; + int pal = this.typeTMPaletteNumber(m.type); + writeWord(arm9, offsPals + itmNum * 4 + 2, pal); + } + } + } else { + } + } + + private static RomFunctions.StringSizeDeterminer ssd = new RomFunctions.StringSizeDeterminer() { + + @Override + public int lengthFor(String encodedText) { + int offs = 0; + int len = encodedText.length(); + while (encodedText.indexOf("\\x", offs) != -1) { + len -= 5; + offs = encodedText.indexOf("\\x", offs) + 1; + } + return len; + } + }; + + @Override + public int getTMCount() { + return 95; + } + + @Override + public int getHMCount() { + return 6; + } + + @Override + public Map getTMHMCompatibility() { + Map compat = new TreeMap(); + for (int i = 1; i <= 649; i++) { + byte[] data = pokeNarc.files.get(i); + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[102]; + for (int j = 0; j < 13; j++) { + readByteIntoFlags(data, flags, j * 8 + 1, 0x28 + j); + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setTMHMCompatibility(Map compatData) { + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + byte[] data = pokeNarc.files.get(pkmn.number); + for (int j = 0; j < 13; j++) { + data[0x28 + j] = getByteFromFlags(flags, j * 8 + 1); + } + } + } + + @Override + public boolean hasMoveTutors() { + return romEntry.romType == Type_BW2; + } + + @Override + public List getMoveTutorMoves() { + if (!hasMoveTutors()) { + return new ArrayList(); + } + int baseOffset = romEntry.getInt("MoveTutorDataOffset"); + int amount = 60; + int bytesPer = 12; + List mtMoves = new ArrayList(); + try { + byte[] mtFile = readOverlay(romEntry.getInt("MoveTutorOvlNumber")); + for (int i = 0; i < amount; i++) { + mtMoves.add(readWord(mtFile, baseOffset + i * bytesPer)); + } + } catch (IOException e) { + } + return mtMoves; + } + + @Override + public void setMoveTutorMoves(List moves) { + if (!hasMoveTutors()) { + return; + } + int baseOffset = romEntry.getInt("MoveTutorDataOffset"); + int amount = 60; + int bytesPer = 12; + if (moves.size() != amount) { + return; + } + try { + byte[] mtFile = readOverlay(romEntry.getInt("MoveTutorOvlNumber")); + for (int i = 0; i < amount; i++) { + writeWord(mtFile, baseOffset + i * bytesPer, moves.get(i)); + } + writeOverlay(romEntry.getInt("MoveTutorOvlNumber"), mtFile); + } catch (IOException e) { + } + } + + @Override + public Map getMoveTutorCompatibility() { + if (!hasMoveTutors()) { + return new TreeMap(); + } + Map compat = new TreeMap(); + int[] countsPersonalOrder = new int[] { 15, 17, 13, 15 }; + int[] countsMoveOrder = new int[] { 13, 15, 15, 17 }; + int[] personalToMoveOrder = new int[] { 1, 3, 0, 2 }; + for (int i = 1; i <= 649; i++) { + byte[] data = pokeNarc.files.get(i); + Pokemon pkmn = pokes[i]; + boolean[] flags = new boolean[61]; + for (int mt = 0; mt < 4; mt++) { + boolean[] mtflags = new boolean[countsPersonalOrder[mt] + 1]; + for (int j = 0; j < 4; j++) { + readByteIntoFlags(data, mtflags, j * 8 + 1, 0x3C + mt * 4 + + j); + } + int offsetOfThisData = 0; + for (int cmoIndex = 0; cmoIndex < personalToMoveOrder[mt]; cmoIndex++) { + offsetOfThisData += countsMoveOrder[cmoIndex]; + } + System.arraycopy(mtflags, 1, flags, offsetOfThisData + 1, + countsPersonalOrder[mt]); + } + compat.put(pkmn, flags); + } + return compat; + } + + @Override + public void setMoveTutorCompatibility(Map compatData) { + if (!hasMoveTutors()) { + return; + } + // BW2 move tutor flags aren't using the same order as the move tutor + // move data. + // We unscramble them from move data order to personal.narc flag order. + int[] countsPersonalOrder = new int[] { 15, 17, 13, 15 }; + int[] countsMoveOrder = new int[] { 13, 15, 15, 17 }; + int[] personalToMoveOrder = new int[] { 1, 3, 0, 2 }; + for (Map.Entry compatEntry : compatData.entrySet()) { + Pokemon pkmn = compatEntry.getKey(); + boolean[] flags = compatEntry.getValue(); + byte[] data = pokeNarc.files.get(pkmn.number); + for (int mt = 0; mt < 4; mt++) { + int offsetOfThisData = 0; + for (int cmoIndex = 0; cmoIndex < personalToMoveOrder[mt]; cmoIndex++) { + offsetOfThisData += countsMoveOrder[cmoIndex]; + } + boolean[] mtflags = new boolean[countsPersonalOrder[mt] + 1]; + System.arraycopy(flags, offsetOfThisData + 1, mtflags, 1, + countsPersonalOrder[mt]); + for (int j = 0; j < 4; j++) { + data[0x3C + mt * 4 + j] = getByteFromFlags(mtflags, + j * 8 + 1); + } + } + } + } + + private int find(byte[] data, String hexString) { + if (hexString.length() % 2 != 0) { + return -3; // error + } + byte[] searchFor = new byte[hexString.length() / 2]; + for (int i = 0; i < searchFor.length; i++) { + searchFor[i] = (byte) Integer.parseInt( + hexString.substring(i * 2, i * 2 + 2), 16); + } + List found = RomFunctions.search(data, searchFor); + if (found.size() == 0) { + return -1; // not found + } else if (found.size() > 1) { + return -2; // not unique + } else { + return found.get(0); + } + } + + private List getStrings(boolean isStoryText, int index) { + NARCContents baseNARC = isStoryText ? storyTextNarc : stringsNarc; + byte[] rawFile = baseNARC.files.get(index); + return new ArrayList(PPTxtHandler.readTexts(rawFile)); + } + + private void setStrings(boolean isStoryText, int index, List strings) { + NARCContents baseNARC = isStoryText ? storyTextNarc : stringsNarc; + byte[] oldRawFile = baseNARC.files.get(index); + byte[] newRawFile = PPTxtHandler.saveEntry(oldRawFile, strings); + baseNARC.files.set(index, newRawFile); + } + + @Override + public String getROMName() { + return "Pokemon " + romEntry.name; + } + + @Override + public String getROMCode() { + return romEntry.romCode; + } + + @Override + public String getSupportLevel() { + return romEntry.staticPokemonSupport ? "Complete" : "No Static Pokemon"; + } + + @Override + public boolean hasTimeBasedEncounters() { + return true; // All BW/BW2 do [seasons] + } + + @Override + public List getEvolutions() { + // Read NARC + List evos = new ArrayList(); + List evosForThisPoke = new ArrayList(); + try { + NARCContents evoNARC = readNARC(romEntry + .getString("PokemonEvolutions")); + for (int i = 1; i <= 649; i++) { + evosForThisPoke.clear(); + byte[] evoEntry = evoNARC.files.get(i); + for (int evo = 0; evo < 7; evo++) { + int method = readWord(evoEntry, evo * 6); + int species = readWord(evoEntry, evo * 6 + 4); + if (method >= 1 && method <= 27 && species >= 1) { + EvolutionType et = EvolutionType.fromIndex(5, method); + int extraInfo = readWord(evoEntry, evo * 6 + 2); + Evolution evol = new Evolution(i, species, true, et, + extraInfo); + if (!evos.contains(evol)) { + evos.add(evol); + evosForThisPoke.add(evol); + } + } + } + // split evos don't carry stats + if (evosForThisPoke.size() > 1) { + for (Evolution e : evosForThisPoke) { + e.carryStats = false; + } + } + } + logBlankLine(); + } catch (IOException e) { + // can't do anything + } + return evos; + } + + @Override + public void removeTradeEvolutions(boolean changeMoveEvos) { + // Read NARC + try { + NARCContents evoNARC = readNARC(romEntry + .getString("PokemonEvolutions")); + Map> movesets = this.getMovesLearnt(); + log("--Removing Trade Evolutions--"); + for (int i = 1; i <= 649; i++) { + byte[] evoEntry = evoNARC.files.get(i); + for (int evo = 0; evo < 7; evo++) { + int evoType = readWord(evoEntry, evo * 6); + int evolvingTo = readWord(evoEntry, evo * 6 + 4); + if (changeMoveEvos && evoType == 21) { + // read move + int move = readWord(evoEntry, evo * 6 + 2); + int levelLearntAt = 1; + for (MoveLearnt ml : movesets.get(pokes[i])) { + if (ml.move == move) { + levelLearntAt = ml.level; + break; + } + } + if (levelLearntAt == 1) { + // override for piloswine + levelLearntAt = 45; + } + // change to pure level evo + writeWord(evoEntry, evo * 6, 4); + writeWord(evoEntry, evo * 6 + 2, levelLearntAt); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, levelLearntAt); + } + if (evoType == 5) { + // Replace w/ level 37 + writeWord(evoEntry, evo * 6, 4); + writeWord(evoEntry, evo * 6 + 2, 37); + logEvoChangeLevel(pokes[i].name, + pokes[evolvingTo].name, 37); + } else if (evoType == 6) { + // Get the current item & evolution + int item = readWord(evoEntry, evo * 6 + 2); + + if (i == 79) { + // Slowpoke is awkward - he already has a level evo + // So we can't do Level up w/ Held Item for him + // Put Water Stone instead + writeWord(evoEntry, evo * 6, 8); + writeWord(evoEntry, evo * 6 + 2, 84); + logEvoChangeStone(pokes[i].name, + pokes[evolvingTo].name, itemNames.get(84)); + } else { + logEvoChangeLevelWithItem(pokes[i].name, + pokes[evolvingTo].name, itemNames.get(item)); + // Replace, for this entry, w/ + // Level up w/ Held Item at Day + writeWord(evoEntry, evo * 6, 19); + // Now look for a free slot to put + // Level up w/ Held Item at Night + for (int evo2 = evo + 1; evo2 < 7; evo2++) { + if (readWord(evoEntry, evo2 * 6) == 0) { + // Bingo, blank entry + writeWord(evoEntry, evo2 * 6, 20); + writeWord(evoEntry, evo2 * 6 + 2, item); + writeWord(evoEntry, evo2 * 6 + 4, + evolvingTo); + break; + } + } + } + } else if (evoType == 7) { + // This is the karrablast <-> shelmet trade + // Replace it with Level up w/ Other Species in Party + // (22) + // Based on what species we're currently dealing with + writeWord(evoEntry, evo * 6, 22); + writeWord(evoEntry, evo * 6 + 2, (i == 588 ? 616 : 588)); + logEvoChangeLevelWithPkmn(pokes[i].name, + pokes[evolvingTo].name, pokes[(i == 588 ? 616 + : 588)].name); + } + } + } + writeNARC(romEntry.getString("PokemonEvolutions"), evoNARC); + logBlankLine(); + } catch (IOException e) { + // can't do anything + } + + } + + @Override + public List getTrainerNames() { + List tnames = getStrings(false, + romEntry.getInt("TrainerNamesTextOffset")); + tnames.remove(0); // blank one + // Tack the mugshot names on the end + List mnames = getStrings(false, + romEntry.getInt("TrainerMugshotsTextOffset")); + for (String mname : mnames) { + if (!mname.isEmpty() + && (mname.charAt(0) >= 'A' && mname.charAt(0) <= 'Z')) { + tnames.add(mname); + } + } + return tnames; + } + + @Override + public int maxTrainerNameLength() { + return 10;// based off the english ROMs + } + + @Override + public void setTrainerNames(List trainerNames) { + List tnames = getStrings(false, + romEntry.getInt("TrainerNamesTextOffset")); + // Grab the mugshot names off the back of the list of trainer names + // we got back + List mnames = getStrings(false, + romEntry.getInt("TrainerMugshotsTextOffset")); + int trNamesSize = trainerNames.size(); + for (int i = mnames.size() - 1; i >= 0; i--) { + String origMName = mnames.get(i); + if (!origMName.isEmpty() + && (origMName.charAt(0) >= 'A' && origMName.charAt(0) <= 'Z')) { + // Grab replacement + String replacement = trainerNames.remove(--trNamesSize); + mnames.set(i, replacement); + } + } + // Save back mugshot names + setStrings(false, romEntry.getInt("TrainerMugshotsTextOffset"), mnames); + + // Now save the rest of trainer names + List newTNames = new ArrayList(trainerNames); + newTNames.add(0, tnames.get(0)); // the 0-entry, preserve it + setStrings(false, romEntry.getInt("TrainerNamesTextOffset"), newTNames); + + } + + @Override + public TrainerNameMode trainerNameMode() { + return TrainerNameMode.MAX_LENGTH; + } + + @Override + public List getTCNameLengthsByTrainer() { + // not needed + return new ArrayList(); + } + + @Override + public List getTrainerClassNames() { + return getStrings(false, romEntry.getInt("TrainerClassesTextOffset")); + } + + @Override + public void setTrainerClassNames(List trainerClassNames) { + setStrings(false, romEntry.getInt("TrainerClassesTextOffset"), + trainerClassNames); + } + + @Override + public int maxTrainerClassNameLength() { + return 12;// based off the english ROMs + } + + @Override + public boolean fixedTrainerClassNamesLength() { + return false; + } + + @Override + public String getDefaultExtension() { + return "nds"; + } + + @Override + public int abilitiesPerPokemon() { + return 3; + } + + @Override + public int highestAbilityIndex() { + return 164; + } + + @Override + public int internalStringLength(String string) { + return ssd.lengthFor(string); + } + + @Override + public void applySignature() { + // For now, do nothing. + + } + + @Override + public ItemList getAllowedItems() { + return allowedItems; + } + + @Override + public String[] getItemNames() { + return itemNames.toArray(new String[0]); + } + + @Override + public String abilityName(int number) { + return abilityNames.get(number); + } + + private List getFieldItems() { + List fieldItems = new ArrayList(); + // normal items + int scriptFileNormal = romEntry.getInt("ItemBallsScriptOffset"); + int scriptFileHidden = romEntry.getInt("HiddenItemsScriptOffset"); + int[] skipTable = romEntry.arrayEntries.get("ItemBallsSkip"); + int[] skipTableH = romEntry.arrayEntries.get("HiddenItemsSkip"); + int setVarNormal = 0x28; + int setVarHidden = 0x2A; + + byte[] itemScripts = scriptNarc.files.get(scriptFileNormal); + int offset = 0; + int skipTableOffset = 0; + while (true) { + int part1 = readWord(itemScripts, offset); + if (part1 == 0xFD13) { + // done + break; + } + int offsetInFile = readRelativePointer(itemScripts, offset); + offset += 4; + if (offsetInFile > itemScripts.length) { + break; + } + if (skipTableOffset < skipTable.length + && (skipTable[skipTableOffset] == (offset / 4) - 1)) { + skipTableOffset++; + continue; + } + int command = readWord(itemScripts, offsetInFile + 2); + int variable = readWord(itemScripts, offsetInFile + 4); + if (command == setVarNormal && variable == 0x800C) { + int item = readWord(itemScripts, offsetInFile + 6); + fieldItems.add(item); + } + + } + + // hidden items + byte[] hitemScripts = scriptNarc.files.get(scriptFileHidden); + offset = 0; + skipTableOffset = 0; + while (true) { + int part1 = readWord(hitemScripts, offset); + if (part1 == 0xFD13) { + // done + break; + } + int offsetInFile = readRelativePointer(hitemScripts, offset); + if (offsetInFile > hitemScripts.length) { + break; + } + offset += 4; + if (skipTableOffset < skipTable.length + && (skipTableH[skipTableOffset] == (offset / 4) - 1)) { + skipTableOffset++; + continue; + } + int command = readWord(hitemScripts, offsetInFile + 2); + int variable = readWord(hitemScripts, offsetInFile + 4); + if (command == setVarHidden && variable == 0x8000) { + int item = readWord(hitemScripts, offsetInFile + 6); + fieldItems.add(item); + } + + } + + return fieldItems; + } + + private void setFieldItems(List fieldItems) { + Iterator iterItems = fieldItems.iterator(); + + // normal items + int scriptFileNormal = romEntry.getInt("ItemBallsScriptOffset"); + int scriptFileHidden = romEntry.getInt("HiddenItemsScriptOffset"); + int[] skipTable = romEntry.arrayEntries.get("ItemBallsSkip"); + int[] skipTableH = romEntry.arrayEntries.get("HiddenItemsSkip"); + int setVarNormal = 0x28; + int setVarHidden = 0x2A; + + byte[] itemScripts = scriptNarc.files.get(scriptFileNormal); + int offset = 0; + int skipTableOffset = 0; + while (true) { + int part1 = readWord(itemScripts, offset); + if (part1 == 0xFD13) { + // done + break; + } + int offsetInFile = readRelativePointer(itemScripts, offset); + offset += 4; + if (offsetInFile > itemScripts.length) { + break; + } + if (skipTableOffset < skipTable.length + && (skipTable[skipTableOffset] == (offset / 4) - 1)) { + skipTableOffset++; + continue; + } + int command = readWord(itemScripts, offsetInFile + 2); + int variable = readWord(itemScripts, offsetInFile + 4); + if (command == setVarNormal && variable == 0x800C) { + int item = iterItems.next(); + writeWord(itemScripts, offsetInFile + 6, item); + } + + } + + // hidden items + byte[] hitemScripts = scriptNarc.files.get(scriptFileHidden); + offset = 0; + skipTableOffset = 0; + while (true) { + int part1 = readWord(hitemScripts, offset); + if (part1 == 0xFD13) { + // done + break; + } + int offsetInFile = readRelativePointer(hitemScripts, offset); + offset += 4; + if (offsetInFile > hitemScripts.length) { + break; + } + if (skipTableOffset < skipTable.length + && (skipTableH[skipTableOffset] == (offset / 4) - 1)) { + skipTableOffset++; + continue; + } + int command = readWord(hitemScripts, offsetInFile + 2); + int variable = readWord(hitemScripts, offsetInFile + 4); + if (command == setVarHidden && variable == 0x8000) { + int item = iterItems.next(); + writeWord(hitemScripts, offsetInFile + 6, item); + } + + } + } + + private int tmFromIndex(int index) { + if (index >= 328 && index <= 419) { + return index - 327; + } else { + return (index + 93) - 618; + } + } + + private int indexFromTM(int tm) { + if (tm >= 1 && tm <= 92) { + return tm + 327; + } else { + return tm + 525; + } + } + + @Override + public List getCurrentFieldTMs() { + List fieldItems = this.getFieldItems(); + List fieldTMs = new ArrayList(); + + for (int item : fieldItems) { + if (allowedItems.isTM(item)) { + fieldTMs.add(tmFromIndex(item)); + } + } + + return fieldTMs; + } + + @Override + public void setFieldTMs(List fieldTMs) { + List fieldItems = this.getFieldItems(); + int fiLength = fieldItems.size(); + Iterator iterTMs = fieldTMs.iterator(); + + for (int i = 0; i < fiLength; i++) { + int oldItem = fieldItems.get(i); + if (allowedItems.isTM(oldItem)) { + int newItem = indexFromTM(iterTMs.next()); + fieldItems.set(i, newItem); + } + } + + this.setFieldItems(fieldItems); + } + + @Override + public List getRegularFieldItems() { + List fieldItems = this.getFieldItems(); + List fieldRegItems = new ArrayList(); + + for (int item : fieldItems) { + if (allowedItems.isAllowed(item) && !(allowedItems.isTM(item))) { + fieldRegItems.add(item); + } + } + + return fieldRegItems; + } + + @Override + public void setRegularFieldItems(List items) { + List fieldItems = this.getFieldItems(); + int fiLength = fieldItems.size(); + Iterator iterNewItems = items.iterator(); + + for (int i = 0; i < fiLength; i++) { + int oldItem = fieldItems.get(i); + if (!(allowedItems.isTM(oldItem)) + && allowedItems.isAllowed(oldItem)) { + int newItem = iterNewItems.next(); + fieldItems.set(i, newItem); + } + } + + this.setFieldItems(fieldItems); + } + + @Override + public List getRequiredFieldTMs() { + if (romEntry.romType == Type_BW) { + return Arrays.asList(new Integer[] { 2, 3, 5, 6, 9, 12, 13, 19, 22, + 24, 26, 29, 30, 35, 36, 39, 41, 46, 47, 50, 52, 53, 55, 58, + 61, 63, 65, 66, 71, 80, 81, 84, 85, 86, 90, 91, 92, 93 }); + } else { + return Arrays + .asList(new Integer[] { 1, 2, 3, 5, 6, 12, 13, 19, 22, 26, + 28, 29, 30, 36, 39, 41, 46, 47, 50, 52, 53, 56, 58, + 61, 63, 65, 66, 67, 69, 71, 80, 81, 84, 85, 86, 90, + 91, 92, 93 }); + } + } + + @Override + public List getIngameTrades() { + List trades = new ArrayList(); + try { + NARCContents tradeNARC = this.readNARC(romEntry + .getString("InGameTrades")); + List tradeStrings = getStrings(false, + romEntry.getInt("IngameTradesTextOffset")); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + int tableSize = tradeNARC.files.size(); + + for (int entry = 0; entry < tableSize; entry++) { + if (unusedOffset < unused.length + && unused[unusedOffset] == entry) { + unusedOffset++; + continue; + } + IngameTrade trade = new IngameTrade(); + byte[] tfile = tradeNARC.files.get(entry); + trade.nickname = tradeStrings.get(entry * 2); + trade.givenPokemon = pokes[readLong(tfile, 4)]; + trade.ivs = new int[6]; + for (int iv = 0; iv < 6; iv++) { + trade.ivs[iv] = readLong(tfile, 0x10 + iv * 4); + } + trade.otId = readWord(tfile, 0x34); + trade.item = readLong(tfile, 0x4C); + trade.otName = tradeStrings.get(entry * 2 + 1); + trade.requestedPokemon = pokes[readLong(tfile, 0x5C)]; + trades.add(trade); + } + } catch (Exception ex) { + } + + return trades; + + } + + @Override + public void setIngameTrades(List trades) { + // info + int tradeOffset = 0; + try { + NARCContents tradeNARC = this.readNARC(romEntry + .getString("InGameTrades")); + List tradeStrings = getStrings(false, + romEntry.getInt("IngameTradesTextOffset")); + int tradeCount = tradeNARC.files.size(); + int[] unused = romEntry.arrayEntries.get("TradesUnused"); + int unusedOffset = 0; + for (int i = 0; i < tradeCount; i++) { + if (unusedOffset < unused.length && unused[unusedOffset] == i) { + unusedOffset++; + continue; + } + byte[] tfile = tradeNARC.files.get(i); + IngameTrade trade = trades.get(tradeOffset++); + tradeStrings.set(i * 2, trade.nickname); + tradeStrings.set(i * 2 + 1, trade.otName); + writeLong(tfile, 4, trade.givenPokemon.number); + writeLong(tfile, 8, 0); // disable forme + for (int iv = 0; iv < 6; iv++) { + writeLong(tfile, 0x10 + iv * 4, trade.ivs[iv]); + } + writeLong(tfile, 0x2C, 0xFF); // random nature + writeWord(tfile, 0x34, trade.otId); + writeLong(tfile, 0x4C, trade.item); + writeLong(tfile, 0x5C, trade.requestedPokemon.number); + } + this.writeNARC(romEntry.getString("InGameTrades"), tradeNARC); + this.setStrings(false, romEntry.getInt("IngameTradesTextOffset"), + tradeStrings); + } catch (IOException ex) { + } + } + + @Override + public boolean hasDVs() { + return false; + } + + @Override + public int generationOfPokemon() { + return 5; + } + + @Override + public void removeEvosForPokemonPool() { + // slightly more complicated than gen2/3 + // we have to update a "baby table" too + List pokemonIncluded = this.mainPokemonList; + List evolsIncluded = new ArrayList(); + try { + NARCContents evoNARC = readNARC(romEntry + .getString("PokemonEvolutions")); + NARCContents babyNARC = readNARC(romEntry.getString("BabyPokemon")); + for (int i = 1; i <= 649; i++) { + boolean included = pokemonIncluded.contains(pokes[i]); + byte[] evoEntry = evoNARC.files.get(i); + for (int evo = 0; evo < 7; evo++) { + int method = readWord(evoEntry, evo * 6); + int species = readWord(evoEntry, evo * 6 + 4); + if (method >= 1 && method <= 27 && species >= 1) { + Pokemon evolvingInto = pokes[species]; + if (!included + || !pokemonIncluded.contains(evolvingInto)) { + // remove this evolution + writeWord(evoEntry, evo * 6, 0); + writeWord(evoEntry, evo * 6 + 2, 0); + writeWord(evoEntry, evo * 6 + 4, 0); + } else { + EvolutionType et = EvolutionType.fromIndex(5, + method); + int extraInfo = readWord(evoEntry, evo * 6 + 2); + Evolution evol = new Evolution(i, species, true, + et, extraInfo); + evolsIncluded.add(evol); + } + } + } + } + // baby pokemon + for (int i = 1; i <= 493; i++) { + int oldBaby = i; + while (true) { + int currentBaby = oldBaby; + for (Evolution evol : evolsIncluded) { + if (evol.to == oldBaby) { + currentBaby = evol.from; + break; + } + } + if (currentBaby == oldBaby) { + break; + } + oldBaby = currentBaby; + } + writeWord(babyNARC.files.get(i), 0, oldBaby); + } + // finish up + writeNARC(romEntry.getString("PokemonEvolutions"), evoNARC); + writeNARC(romEntry.getString("BabyPokemon"), babyNARC); + } catch (IOException e) { + // can't do anything + } + } + + @Override + public boolean supportsFourStartingMoves() { + return true; + } +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/NARCContents.java b/src/com/dabomstew/pkrandom/romhandlers/NARCContents.java new file mode 100755 index 000000000..80505dcae --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/NARCContents.java @@ -0,0 +1,36 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- NARCContents.java - represents the contents of a NARC archive. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.ArrayList; +import java.util.List; + +public class NARCContents { + + public List filenames = new ArrayList(); + public List files = new ArrayList(); + + public boolean hasFilenames = false; + +} diff --git a/src/com/dabomstew/pkrandom/romhandlers/RomHandler.java b/src/com/dabomstew/pkrandom/romhandlers/RomHandler.java new file mode 100755 index 000000000..01ebcc968 --- /dev/null +++ b/src/com/dabomstew/pkrandom/romhandlers/RomHandler.java @@ -0,0 +1,392 @@ +package com.dabomstew.pkrandom.romhandlers; + +/*----------------------------------------------------------------------------*/ +/*-- RomHandler.java - defines the functionality that each randomization --*/ +/*-- handler must implement. --*/ +/*-- --*/ +/*-- Part of "Universal Pokemon Randomizer" by Dabomstew --*/ +/*-- Pokemon and any associated names and the like are --*/ +/*-- trademark and (C) Nintendo 1996-2012. --*/ +/*-- --*/ +/*-- The custom code written here is licensed under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.List; +import java.util.Map; + +import com.dabomstew.pkrandom.pokemon.EncounterSet; +import com.dabomstew.pkrandom.pokemon.Evolution; +import com.dabomstew.pkrandom.pokemon.GenRestrictions; +import com.dabomstew.pkrandom.pokemon.IngameTrade; +import com.dabomstew.pkrandom.pokemon.ItemList; +import com.dabomstew.pkrandom.pokemon.Move; +import com.dabomstew.pkrandom.pokemon.MoveLearnt; +import com.dabomstew.pkrandom.pokemon.Pokemon; +import com.dabomstew.pkrandom.pokemon.Trainer; +import com.dabomstew.pkrandom.pokemon.Type; + +public interface RomHandler { + + // Check whether this ROM is for this handler or not + + public boolean detectRom(String filename); + + // Basic load/save to filenames + + public boolean loadRom(String filename); + + public boolean saveRom(String filename); + + public String loadedFilename(); + + // Functionality + public boolean isInGame(Pokemon pkmn); + + public boolean isInGame(int pokemonNumber); + + // Get the evolution pairs that apply in this game + public List getEvolutions(); + + // Get a List of Pokemon objects in this game. + // 0 = null 1-whatever = the Pokemon. + public List getPokemon(); + + // Setup Gen Restrictions. + public void setPokemonPool(GenRestrictions restrictions); + + public void removeEvosForPokemonPool(); + + // Randomizer: Starters + // Get starters, they should be ordered with Pokemon + // following the one it is SE against. + // E.g. Grass, Fire, Water or Fire, Water, Grass etc. + public List getStarters(); + + // Change the starter data in the ROM. + // Optionally also change the starter used by the rival in + // the level 5 battle, if there is one. + public boolean setStarters(List newStarters); + + // Tells whether this ROM has the ability to have starters changed. + // Was for before CUE's compressors were found and arm9 was untouchable. + public boolean canChangeStarters(); + + // Randomizer: Pokemon stats + + // Run the stats shuffler on each Pokemon. + public void shufflePokemonStats(); + + // Randomise stats following evolutions for proportions or not (see + // tooltips) + public void randomizePokemonStats(boolean evolutionSanity); + + // Give a random Pokemon who's in this game + public Pokemon randomPokemon(); + + // Give a random non-legendary Pokemon who's in this game + // Business rules for who's legendary are in Pokemon class + public Pokemon randomNonLegendaryPokemon(); + + // Give a random legendary Pokemon who's in this game + // Business rules for who's legendary are in Pokemon class + public Pokemon randomLegendaryPokemon(); + + // Give a random Pokemon who has 2 evolution stages + // Should make a good starter Pokemon + public Pokemon random2EvosPokemon(); + + // Randomizer: moves + + // Update all moves to gen5 definitions as much as possible + // e.g. change typing, power, accuracy, but don't try to + // stuff around with effects. + public void updateMovesToGen5(); + + // same for gen6 + public void updateMovesToGen6(); + + // stuff for printing move changes + public void initMoveUpdates(); + + public void printMoveUpdates(); + + // return all the moves valid in this game. + public List getMoves(); + + // Randomizer: types + + // return a random type valid in this game. + // straightforward except for gen1 where dark&steel are excluded. + public Type randomType(); + + // randomise Pokemon types, with a switch on whether evolutions + // should follow the same types or not. + // some evolutions dont anyway, e.g. Eeveelutions, Hitmons + public void randomizePokemonTypes(boolean evolutionSanity); + + // Randomizer: wild pokemon + public List getEncounters(boolean useTimeOfDay); + + public void setEncounters(boolean useTimeOfDay, + List encounters); + + public void randomEncounters(boolean useTimeOfDay, boolean catchEmAll, + boolean typeThemed, boolean usePowerLevels, boolean noLegendaries); + + public void area1to1Encounters(boolean useTimeOfDay, boolean catchEmAll, + boolean typeThemed, boolean usePowerLevels, boolean noLegendaries); + + public void game1to1Encounters(boolean useTimeOfDay, + boolean usePowerLevels, boolean noLegendaries); + + public boolean hasTimeBasedEncounters(); + + public List bannedForWildEncounters(); + + // Randomizer: trainer pokemon + public List getTrainers(); + + public void setTrainers(List trainerData); + + public void randomizeTrainerPokes(boolean rivalCarriesStarter, + boolean usePowerLevels, boolean noLegendaries, + boolean noEarlyWonderGuard); + + public void typeThemeTrainerPokes(boolean rivalCarriesStarter, + boolean usePowerLevels, boolean weightByFrequency, + boolean noLegendaries, boolean noEarlyWonderGuard); + + public boolean typeInGame(Type type); + + // Randomizer: moves learnt + + public Map> getMovesLearnt(); + + public void setMovesLearnt(Map> movesets); + + public void randomizeMovesLearnt(boolean typeThemed, boolean noBroken, + boolean forceFourStartingMoves); + + public void metronomeOnlyMode(); + + public boolean supportsFourStartingMoves(); + + // Randomizer: static pokemon (except starters) + + public List getStaticPokemon(); + + public boolean setStaticPokemon(List staticPokemon); + + public void randomizeStaticPokemon(boolean legendForLegend); + + public boolean canChangeStaticPokemon(); + + public List bannedForStaticPokemon(); + + // Randomizer: TMs/HMs + + public List getTMMoves(); + + public List getHMMoves(); + + public void setTMMoves(List moveIndexes); + + public void randomizeTMMoves(boolean noBroken); + + public int getTMCount(); + + public int getHMCount(); + + /** + * Get TM/HM compatibility data from this rom. The result should contain a + * boolean array for each Pokemon indexed as such: + * + * 0: blank (false) / 1 - (getTMCount()) : TM compatibility / + * (getTMCount()+1) - (getTMCount()+getHMCount()) - HM compatibility + * + * @return + */ + + public Map getTMHMCompatibility(); + + public void setTMHMCompatibility(Map compatData); + + public void randomizeTMHMCompatibility(boolean preferSameType); + + // Randomizer: move tutors + + public boolean hasMoveTutors(); + + public List getMoveTutorMoves(); + + public void setMoveTutorMoves(List moves); + + public void randomizeMoveTutorMoves(boolean noBroken); + + public Map getMoveTutorCompatibility(); + + public void setMoveTutorCompatibility(Map compatData); + + public void randomizeMoveTutorCompatibility(boolean preferSameType); + + // Randomizer: trainer names + + public List getTrainerNames(); + + public void setTrainerNames(List trainerNames); + + public enum TrainerNameMode { + SAME_LENGTH, MAX_LENGTH, MAX_LENGTH_WITH_CLASS + }; + + public TrainerNameMode trainerNameMode(); + + // Returns this with or without the class + public int maxTrainerNameLength(); + + // Only needed if above mode is "MAX LENGTH WITH CLASS" + public List getTCNameLengthsByTrainer(); + + public void randomizeTrainerNames(byte[] presetNames); + + // Randomizer: trainer class names + + public List getTrainerClassNames(); + + public void setTrainerClassNames(List trainerClassNames); + + public boolean fixedTrainerClassNamesLength(); + + public int maxTrainerClassNameLength(); + + public void randomizeTrainerClassNames(byte[] presetNames); + + // Randomizer: pokemon abilities + public int abilitiesPerPokemon(); + + public int highestAbilityIndex(); + + public String abilityName(int number); + + public void randomizeAbilities(boolean allowWonderGuard); + + // Hidden hollows (BW2 only) + + public boolean hasHiddenHollowPokemon(); + + public void randomizeHiddenHollowPokemon(); + + // Items + + public ItemList getAllowedItems(); + + public void randomizeWildHeldItems(); + + public String[] getItemNames(); + + public List getStarterHeldItems(); + + public void setStarterHeldItems(List items); + + public void randomizeStarterHeldItems(); + + // Field Items + + // TMs on the field + + public List getRequiredFieldTMs(); + + public List getCurrentFieldTMs(); + + public void setFieldTMs(List fieldTMs); + + // Everything else + + public List getRegularFieldItems(); + + public void setRegularFieldItems(List items); + + // Randomizer methods + + public void shuffleFieldItems(); + + public void randomizeFieldItems(); + + // Trades + + public List getIngameTrades(); + + public void setIngameTrades(List trades); + + public void randomizeIngameTrades(boolean randomizeRequest, + byte[] presetNicknames, boolean randomNickname, + byte[] presetTrainerNames, boolean randomOT, boolean randomStats, + boolean randomItem); + + public boolean hasDVs(); + + public int maxTradeNicknameLength(); + + public int maxTradeOTNameLength(); + + // Evos + + public void removeTradeEvolutions(boolean changeMoveEvos); + + // stats stuff + public void minimumCatchRate(int rateNonLegendary, int rateLegendary); + + public void standardizeEXPCurves(); + + // Misc + + public void applyCamelCaseNames(); + + public boolean isYellow(); + + public String getROMName(); + + public String getROMCode(); + + public String getSupportLevel(); + + public void fixTypeEffectiveness(); + + public void patchForNationalDex(); + + public String getDefaultExtension(); + + public int internalStringLength(String string); + + public int codeTweaksAvailable(); + + public List getGameBreakingMoves(); + + public void applySignature(); + + public boolean isROMHack(); + + public int generationOfPokemon(); + + // code tweaks + + public void applyBWEXPPatch(); + + public void applyXAccNerfPatch(); + + public void applyCritRatePatch(); + +} diff --git a/src/cuecompressors/BLZCoder.java b/src/cuecompressors/BLZCoder.java new file mode 100755 index 000000000..51487f53c --- /dev/null +++ b/src/cuecompressors/BLZCoder.java @@ -0,0 +1,576 @@ +package cuecompressors; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +/*----------------------------------------------------------------------------*/ +/*-- blz.c - Bottom LZ coding for Nintendo GBA/DS --*/ +/*-- Copyright (C) 2011 CUE --*/ +/*-- --*/ +/*-- Ported to Java by Dabomstew under the terms of the GPL: --*/ +/*-- --*/ +/*-- This program is free software: you can redistribute it and/or modify --*/ +/*-- it under the terms of the GNU General Public License as published by --*/ +/*-- the Free Software Foundation, either version 3 of the License, or --*/ +/*-- (at your option) any later version. --*/ +/*-- --*/ +/*-- This program is distributed in the hope that it will be useful, --*/ +/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/ +/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/ +/*-- GNU General Public License for more details. --*/ +/*-- --*/ +/*-- You should have received a copy of the GNU General Public License --*/ +/*-- along with this program. If not, see . --*/ +/*----------------------------------------------------------------------------*/ + +public class BLZCoder { + + /** + * @param args + */ + public static void main(String[] args) { + new BLZCoder(args); + } + + private static final int CMD_DECODE = 0; + private static final int CMD_ENCODE = 1; + + private static final int BLZ_NORMAL = 0; + private static final int BLZ_BEST = 1; + + private static final int BLZ_SHIFT = 1; + private static final int BLZ_MASK = 0x80; + + private static final int BLZ_THRESHOLD = 2; + private static final int BLZ_N = 0x1002; + private static final int BLZ_F = 0x12; + + private static final int RAW_MINIM = 0; + private static final int RAW_MAXIM = 0x00FFFFFF; + + private static final int BLZ_MINIM = 4; + private static final int BLZ_MAXIM = 0x01400000; + + private boolean arm9; + + public BLZCoder(String[] args) { + + int cmd, mode = 0, arg; + + Title(); + + if (args == null) { + // going to be used internally + return; + } + + if (args.length < 1) { + Usage(); + } + + if (args[0].equalsIgnoreCase("-d")) { + cmd = CMD_DECODE; + } else if (args[0].equalsIgnoreCase("-en") + || args[0].equalsIgnoreCase("-en9")) { + cmd = CMD_ENCODE; + mode = BLZ_NORMAL; + } else if (args[0].equalsIgnoreCase("-eo") + || args[0].equalsIgnoreCase("-eo9")) { + cmd = CMD_ENCODE; + mode = BLZ_BEST; + } else { + EXIT("Command not supported\n"); + return; + } + + if (args.length < 2) { + EXIT("Filename not specified\n"); + } + + switch (cmd) { + case CMD_DECODE: + for (arg = 1; arg < args.length; arg++) + BLZ_Decode(args[arg]); + break; + case CMD_ENCODE: + arm9 = (args[0].length() > 3 && args[0].charAt(3) == '9'); + for (arg = 1; arg < args.length; arg++) + BLZ_Encode(args[arg], mode); + break; + } + + System.out.print("\nDone\n"); + } + + private void Usage() { + EXIT("Usage: BLZ command filename [filename [...]]\n" + "\n" + + "command:\n" + " -d ....... decode 'filename'\n" + + " -en[9] ... encode 'filename', normal mode\n" + + " -eo[9] ... encode 'filename', optimal mode (LZ-CUE)\n" + + "\n" + + "* '9' compress an ARM9 file with 0x4000 bytes decoded\n" + + "* multiple filenames and wildcards are permitted\n" + + "* the original file is overwritten with the new file\n" + + "* this codification is used in the DS overlay files\n"); + + } + + private void Title() { + System.out.print("\n"); + System.out.print("BLZ - (c) CUE 2011\n"); + System.out.print("Bottom LZ coding for Nintendo GBA/DS\n"); + System.out.print("\n"); + } + + public void EXIT(String text) { + System.out.print(text); + System.exit(0); + } + + private void Save(String filename, int[] buffer, int length) { + try { + FileOutputStream fos = new FileOutputStream(filename); + byte[] write = new byte[length]; + for (int i = 0; i < length; i++) { + write[i] = (byte) buffer[i]; + } + fos.write(write); + fos.close(); + } catch (IOException e) { + EXIT("\nFile write error\n"); + } + + } + + private void BLZ_Decode(String filename) { + try { + System.out.printf("- decoding '%s'", filename); + long startTime = System.currentTimeMillis(); + FileInputStream fis = new FileInputStream(filename); + byte[] buf = new byte[fis.available()]; + fis.read(buf); + fis.close(); + BLZResult result = BLZ_Decode(buf); + if (result != null) + Save(filename, result.buffer, result.length); + System.out.print(" - done, time=" + + (System.currentTimeMillis() - startTime) + "ms"); + System.out.print("\n"); + } catch (IOException e) { + EXIT("\nFile read error\n"); + } + } + + public byte[] BLZ_DecodePub(byte[] data, String reference) { + System.out.printf("- decoding '%s' (memory)", reference); + long startTime = System.currentTimeMillis(); + BLZResult result = BLZ_Decode(data); + System.out.print(" - done, time=" + + (System.currentTimeMillis() - startTime) + "ms"); + System.out.print("\n"); + if (result != null) { + byte[] retbuf = new byte[result.length]; + for (int i = 0; i < result.length; i++) { + retbuf[i] = (byte) result.buffer[i]; + } + result = null; + return retbuf; + } else { + return null; + } + } + + private BLZResult BLZ_Decode(byte[] data) { + int[] pak_buffer, raw_buffer; + int pak, raw, pak_end, raw_end; + int pak_len, raw_len, len, pos, inc_len, hdr_len, enc_len, dec_len; + int flags = 0, mask; + + pak_buffer = prepareData(data); + pak_len = pak_buffer.length - 3; + + inc_len = readUnsigned(pak_buffer, pak_len - 4); + if (inc_len < 1) { + System.out.printf(", WARNING: not coded file!"); + enc_len = 0; + dec_len = pak_len; + pak_len = 0; + raw_len = dec_len; + } else { + if (pak_len < 8) { + EXIT("\nFile has a bad header\n"); + return null; + } + hdr_len = pak_buffer[pak_len - 5]; + if (hdr_len < 8 || hdr_len > 0xB) { + EXIT("\nBad header length\n"); + return null; + } + if (pak_len <= hdr_len) { + EXIT("\nBad length\n"); + return null; + } + enc_len = readUnsigned(pak_buffer, pak_len - 8) & 0x00FFFFFF; + dec_len = pak_len - enc_len; + pak_len = enc_len - hdr_len; + raw_len = dec_len + enc_len + inc_len; + if (raw_len > RAW_MAXIM) { + EXIT("\nBad decoded length\n"); + return null; + } + } + + raw_buffer = new int[raw_len]; + + pak = 0; + raw = 0; + pak_end = dec_len + pak_len; + raw_end = raw_len; + + for (len = 0; len < dec_len; len++) { + raw_buffer[raw++] = pak_buffer[pak++]; + } + + BLZ_Invert(pak_buffer, dec_len, pak_len); + + mask = 0; + + while (raw < raw_end) { + if ((mask = (mask >>> BLZ_SHIFT)) == 0) { + if (pak == pak_end) { + break; + } + flags = pak_buffer[pak++]; + mask = BLZ_MASK; + } + + if ((flags & mask) == 0) { + if (pak == pak_end) { + break; + } + raw_buffer[raw++] = pak_buffer[pak++]; + } else { + if ((pak + 1) >= pak_end) { + break; + } + pos = pak_buffer[pak++] << 8; + pos |= pak_buffer[pak++]; + len = (pos >>> 12) + BLZ_THRESHOLD + 1; + if (raw + len > raw_end) { + System.out.print(", WARNING: wrong decoded length!"); + len = raw_end - raw; + } + pos = (pos & 0xFFF) + 3; + while ((len--) > 0) { + int charHere = raw_buffer[raw - pos]; + raw_buffer[raw++] = charHere; + } + } + } + + BLZ_Invert(raw_buffer, dec_len, raw_len - dec_len); + + raw_len = raw; + + if (raw != raw_end) { + System.out.print(", WARNING: unexpected end of encoded file!"); + } + + return new BLZResult(raw_buffer, raw_len); + + } + + private int[] prepareData(byte[] data) { + int fs = data.length; + int[] fb = new int[fs + 3]; + for (int i = 0; i < fs; i++) { + fb[i] = data[i] & 0xFF; + } + return fb; + } + + private int readUnsigned(int[] buffer, int offset) { + return buffer[offset] | (buffer[offset + 1] << 8) + | (buffer[offset + 2] << 16) + | ((buffer[offset + 3] & 0x7F) << 24); + } + + private void writeUnsigned(int[] buffer, int offset, int value) { + buffer[offset] = value & 0xFF; + buffer[offset + 1] = (value >> 8) & 0xFF; + buffer[offset + 2] = (value >> 16) & 0xFF; + buffer[offset + 3] = (value >> 24) & 0x7F; + } + + private int new_len; + + private void BLZ_Encode(String filename, int mode) { + try { + System.out.printf("- encoding '%s'", filename); + long startTime = System.currentTimeMillis(); + FileInputStream fis = new FileInputStream(filename); + byte[] buf = new byte[fis.available()]; + fis.read(buf); + fis.close(); + BLZResult result = BLZ_Encode(buf, mode); + if (result != null) + Save(filename, result.buffer, result.length); + System.out.print(" - done, time=" + + (System.currentTimeMillis() - startTime) + "ms"); + System.out.print("\n"); + } catch (IOException e) { + EXIT("\nFile read error\n"); + } + } + + public byte[] BLZ_EncodePub(byte[] data, boolean arm9, boolean best, + String reference) { + int mode = best ? BLZ_BEST : BLZ_NORMAL; + this.arm9 = arm9; + System.out.printf("- encoding '%s' (memory)", reference); + long startTime = System.currentTimeMillis(); + BLZResult result = BLZ_Encode(data, mode); + System.out.print(" - done, time=" + + (System.currentTimeMillis() - startTime) + "ms"); + System.out.print("\n"); + if (result != null) { + byte[] retbuf = new byte[result.length]; + for (int i = 0; i < result.length; i++) { + retbuf[i] = (byte) result.buffer[i]; + } + result = null; + return retbuf; + } else { + return null; + } + } + + private BLZResult BLZ_Encode(byte[] data, int mode) { + int[] raw_buffer, pak_buffer, new_buffer; + int raw_len, pak_len; + + new_len = 0; + + raw_buffer = prepareData(data); + raw_len = raw_buffer.length - 3; + + pak_buffer = null; + pak_len = BLZ_MAXIM + 1; + + new_buffer = BLZ_Code(raw_buffer, raw_len, mode); + + if (new_len < pak_len) { + pak_buffer = new_buffer; + pak_len = new_len; + } + return new BLZResult(pak_buffer, pak_len); + } + + private int[] BLZ_Code(int[] raw_buffer, int raw_len, int best) { + int[] pak_buffer, tmp; + int pak, raw, raw_end, flg = 0; + int pak_len, inc_len, hdr_len, enc_len, len; + int len_best, pos_best = 0, len_next, pos_next = 0, len_post, pos_post = 0; + int pak_tmp, raw_tmp, raw_new; + int mask; + + pak_tmp = 0; + raw_tmp = raw_len; + + pak_len = raw_len + ((raw_len + 7) / 8) + 11; + pak_buffer = new int[pak_len]; + + raw_new = raw_len; + + if (arm9) { + // We don't do any of the checks here + // Presume that we actually are using an arm9 + raw_new -= 0x4000; + } + + BLZ_Invert(raw_buffer, 0, raw_len); + + pak = 0; + raw = 0; + raw_end = raw_new; + + mask = 0; + while (raw < raw_end) { + if ((mask = (mask >>> BLZ_SHIFT)) == 0) { + pak_buffer[(flg = pak++)] = 0; + mask = BLZ_MASK; + } + + SearchPair sl1 = SEARCH(pos_best, raw_buffer, raw, raw_end); + len_best = sl1.l; + pos_best = sl1.p; + + // LZ-CUE optimization start + if (best == BLZ_BEST) { + if (len_best > BLZ_THRESHOLD) { + if (raw + len_best < raw_end) { + raw += len_best; + SearchPair sl2 = SEARCH(pos_next, raw_buffer, raw, + raw_end); + len_next = sl2.l; + pos_next = sl2.p; + raw -= (len_best - 1); + SearchPair sl3 = SEARCH(pos_post, raw_buffer, raw, + raw_end); + len_post = sl3.l; + pos_post = sl3.p; + raw--; + + if (len_next <= BLZ_THRESHOLD) { + len_next = 1; + } + if (len_post <= BLZ_THRESHOLD) { + len_post = 1; + } + if ((len_best + len_next) <= (1 + len_post)) { + len_best = 1; + } + } + } + } + // LZ-CUE optimization end + pak_buffer[flg] = (pak_buffer[flg] << 1); + if (len_best > BLZ_THRESHOLD) { + raw += len_best; + pak_buffer[flg] |= 1; + pak_buffer[pak++] = ((len_best - (BLZ_THRESHOLD + 1)) << 4) + | ((pos_best - 3) >>> 8); + pak_buffer[pak++] = (pos_best - 3) & 0xFF; + } else { + pak_buffer[pak++] = raw_buffer[raw++]; + } + + if (pak + raw_len - raw < pak_tmp + raw_tmp) { + pak_tmp = pak; + raw_tmp = raw_len - raw; + } + } + + while ((mask > 0) && (mask != 1)) { + mask = (mask >>> BLZ_SHIFT); + pak_buffer[flg] = pak_buffer[flg] << 1; + } + + pak_len = pak; + + BLZ_Invert(raw_buffer, 0, raw_len); + BLZ_Invert(pak_buffer, 0, pak_len); + + if (pak_tmp == 0 + || (raw_len + 4 < ((pak_tmp + raw_tmp + 3) & 0xFFFFFFFC) + 8)) { + pak = 0; + raw = 0; + raw_end = raw_len; + + while (raw < raw_end) { + pak_buffer[pak] = raw_buffer[raw]; + } + + while ((pak & 3) > 0) { + pak_buffer[pak++] = 0; + } + + pak_buffer[pak++] = 0; + pak_buffer[pak++] = 0; + pak_buffer[pak++] = 0; + pak_buffer[pak++] = 0; + } else { + tmp = new int[raw_tmp + pak_tmp + 11]; + for (len = 0; len < raw_tmp; len++) { + tmp[len] = raw_buffer[len]; + } + for (len = 0; len < pak_tmp; len++) { + tmp[raw_tmp + len] = pak_buffer[len + pak_len - pak_tmp]; + } + + pak = 0; + pak_buffer = tmp; + + pak = raw_tmp + pak_tmp; + + enc_len = pak_tmp; + hdr_len = 8; + inc_len = raw_len - pak_tmp - raw_tmp; + + while ((pak & 3) > 0) { + pak_buffer[pak++] = 0xFF; + hdr_len++; + } + + writeUnsigned(pak_buffer, pak, enc_len + hdr_len); + pak += 3; + pak_buffer[pak++] = hdr_len; + writeUnsigned(pak_buffer, pak, inc_len - hdr_len); + pak += 4; + + } + new_len = pak; + return pak_buffer; + } + + private static class SearchPair { + public int l; + public int p; + + public SearchPair(int l, int p) { + this.l = l; + this.p = p; + } + } + + private SearchPair SEARCH(int p, int[] raw_buffer, int raw, int raw_end) { + int l = BLZ_THRESHOLD; + int max = (raw >= BLZ_N) ? BLZ_N : raw; + for (int pos = 3; pos <= max; pos++) { + int len; + for (len = 0; len < BLZ_F; len++) { + if (raw + len == raw_end) { + break; + } + if (len >= pos) { + break; + } + if (raw_buffer[raw + len] != raw_buffer[raw + len - pos]) { + break; + } + } + + if (len > l) { + p = pos; + if ((l = len) == BLZ_F) { + break; + } + } + } + return new SearchPair(l, p); + } + + private class BLZResult { + public BLZResult(int[] raw_buffer, int raw_len) { + this.buffer = raw_buffer; + this.length = raw_len; + } + + int[] buffer; + int length; + } + + private void BLZ_Invert(int[] buffer, int offset, int length) { + int bottom, ch; + + bottom = offset + length - 1; + + while (offset < bottom) { + ch = buffer[offset]; + buffer[offset++] = buffer[bottom]; + buffer[bottom--] = ch; + } + } + +} diff --git a/src/dsdecmp/HexInputStream.java b/src/dsdecmp/HexInputStream.java new file mode 100755 index 000000000..d8cb77ace --- /dev/null +++ b/src/dsdecmp/HexInputStream.java @@ -0,0 +1,240 @@ +package dsdecmp; + +// Part of DSDecmp-Java +// License is below + +//Copyright (c) 2010 Nick Kraayenbrink +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +//all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Stack; + +public class HexInputStream { + + /** The InputStream this stream is based on. */ + private volatile InputStream dis; + /** The current position of this stream. */ + private volatile long currPos; + + /** Get the current position of this stream. */ + public long getPosition() { + return this.currPos; + } + + /** + * Sets the position of this stream. + * + * @param newPos + * The desired position of the stream. + * @throws IOException + * when the given position cannot be reached + */ + public void setPosition(long newPos) throws IOException { + this.skip(newPos - currPos); + } + + /** Convenience method for {@link #setPosition(long)}. */ + public void goTo(long pos) throws IOException { + this.setPosition(pos); + } + + /** The stack of saved positions for this stream. */ + private Stack positionStack; + + /** + * Creates a new HexInputStream, based off another InputStream. The + * 0-position of the new stream is the current position of the given stream. + * + * @param baseInputStream + * The InputStream to base the new HexInputStream on. + */ + public HexInputStream(InputStream baseInputStream) { + this.dis = baseInputStream; + this.currPos = 0; + this.positionStack = new Stack(); + } + + /** + * Creates a new HexInputStream for reading a file. + * + * @param String + * The name of the file to read. + * @throws FileNotFoundException + * If the given file does not exist. + */ + public HexInputStream(String filename) throws FileNotFoundException { + this.dis = new DataInputStream(new FileInputStream(new File(filename))); + this.currPos = 0; + this.positionStack = new Stack(); + } + + /** + * Returns an estimate of the number of bytes left to read until the EOF. + * See {@link InputStream#available} + */ + public int available() throws IOException { + return dis.available(); + } + + /** + * Read the next byte from a file. If the EOF has been reached, -1 is + * returned + */ + public int read() throws IOException { + int b = dis.read(); + if (b != -1) + currPos++; + return b; + } + + /** Read an array of bytes from this stream */ + public int[] readBytes(int length) throws IOException { + int[] data = new int[length]; + for (int i = 0; i < length; i++) + data[i] = readU8(); + return data; + } + + /** Read a byte from this stream */ + public int readU8() throws IOException { + int b = dis.read(); + currPos++; + return b; + } + + /** Read a BigEndian s16 from this stream */ + public short readS16() throws IOException { + short word = 0; + for (int i = 0; i < 2; i++) + word = (short) (word | (readU8() << (8 * i))); + return word; + } + + /** Read a LittleEndian s16 from this stream */ + public short readlS16() throws IOException { + short word = 0; + for (int i = 0; i < 2; i++) + word = (short) ((word << 8) | readU8()); + return word; + } + + /** Read a BigEndian u16 from this stream */ + public int readU16() throws IOException { + int word = 0; + for (int i = 0; i < 2; i++) + word = word | (readU8() << (8 * i)); + return word; + } + + /** Read a LittleEndian u16 from this stream */ + public int readlU16() throws IOException { + int word = 0; + for (int i = 0; i < 2; i++) + word = (word << 8) | readU8(); + return word; + } + + /** Read a BigEndian s32 from this stream (a signed int) */ + public int readS32() throws IOException { + int dword = 0; + for (int i = 0; i < 4; i++) + dword = dword | (readU8() << (8 * i)); + return dword; + } + + /** Read a LittleEndian s32 from this stream (a signed int) */ + public int readlS32() throws IOException { + int dword = 0; + for (int i = 0; i < 4; i++) + dword = (dword << 8) | readU8(); + return dword; + } + + /** Read a BigEndian u32 from this stream (an unsigned int) */ + public long readU32() throws IOException { + long dword = 0; + for (int i = 0; i < 4; i++) + dword = dword | (readU8() << (8 * i)); + + return dword; + } + + /** Read a LittleEndian u32 from this stream (an unsigned int) */ + public long readlU32() throws IOException { + long dword = 0; + for (int i = 0; i < 4; i++) + dword = (dword << 8) | readU8(); + return dword; + } + + /** Read a BigEndian s64 from this stream (a signed int) */ + public long readS64() throws IOException { + long qword = 0; + for (int i = 0; i < 8; i++) + qword = qword | (readU8() << (8 * i)); + return qword; + } + + /** Read a LittleEndian s64 from this stream (a signed int) */ + public long readlS64() throws IOException { + long qword = 0; + for (int i = 0; i < 8; i++) + qword = (qword << 8) | readU8(); + return qword; + } + + /** Close this stream */ + public void close() throws IOException { + dis.close(); + } + + /** Skip n bytes */ + public void skip(long n) throws IOException { + currPos += dis.skip(n); + } + + /** Reset this stream to its base position. */ + public void reset() throws IOException { + this.goTo(0); + } + + /** Save the current position on the local stack */ + public void savePosition() { + this.positionStack.push(this.currPos); + } + + /** + * Pop the last saved position from the local stack and restore that + * position + */ + public void loadPosition() throws IOException { + if (!this.positionStack.isEmpty()) { + long pos = this.positionStack.peek(); + this.positionStack.pop(); + this.goTo(pos); + } + } + +} diff --git a/src/dsdecmp/InvalidFileException.java b/src/dsdecmp/InvalidFileException.java new file mode 100755 index 000000000..7767ab658 --- /dev/null +++ b/src/dsdecmp/InvalidFileException.java @@ -0,0 +1,40 @@ +package dsdecmp; + +//Part of DSDecmp-Java +//License is below + +//Copyright (c) 2010 Nick Kraayenbrink +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +//all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +import java.io.IOException; + +public class InvalidFileException extends IOException { + + private static final long serialVersionUID = -8354901572139075536L; + + public InvalidFileException(String message) { + super(message); + } + + public InvalidFileException(String message, Exception innerException) { + super(message, innerException); + } + +} diff --git a/src/dsdecmp/JavaDSDecmp.java b/src/dsdecmp/JavaDSDecmp.java new file mode 100755 index 000000000..c821c5dc9 --- /dev/null +++ b/src/dsdecmp/JavaDSDecmp.java @@ -0,0 +1,167 @@ +package dsdecmp; + +//Part of DSDecmp-Java +//License is below + +//Copyright (c) 2010 Nick Kraayenbrink +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files (the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions: +// +//The above copyright notice and this permission notice shall be included in +//all copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +//THE SOFTWARE. + +import java.io.EOFException; +import java.io.IOException; + +public class JavaDSDecmp { + + public static int[] Decompress(HexInputStream his) throws IOException { + switch (his.readU8()) { + case 0x11: + return Decompress11LZ(his); + default: + return null; + } + } + + private static int getLength(HexInputStream his) throws IOException { + int length = 0; + for (int i = 0; i < 3; i++) + length = length | (his.readU8() << (i * 8)); + if (length == 0) // 0 length? then length is next 4 bytes + length = his.readlS32(); + return length; + } + + private static int[] Decompress11LZ(HexInputStream his) throws IOException { + int[] outData = new int[getLength(his)]; + + int curr_size = 0; + int flags; + boolean flag; + int b1, bt, b2, b3, len, disp, cdest; + + while (curr_size < outData.length) { + try { + flags = his.readU8(); + } catch (EOFException ex) { + break; + } + + for (int i = 0; i < 8 && curr_size < outData.length; i++) { + flag = (flags & (0x80 >> i)) > 0; + if (flag) { + try { + b1 = his.readU8(); + } catch (EOFException ex) { + throw new InvalidFileException("Incomplete data"); + } + + switch (b1 >> 4) { + case 0: + // ab cd ef + // => + // len = abc + 0x11 = bc + 0x11 + // disp = def + + len = b1 << 4; + try { + bt = his.readU8(); + } catch (EOFException ex) { + throw new InvalidFileException("Incomplete data"); + } + len |= bt >> 4; + len += 0x11; + + disp = (bt & 0x0F) << 8; + try { + b2 = his.readU8(); + } catch (EOFException ex) { + throw new InvalidFileException("Incomplete data"); + } + disp |= b2; + break; + + case 1: + // ab cd ef gh + // => + // len = bcde + 0x111 + // disp = fgh + // 10 04 92 3F => disp = 0x23F, len = 0x149 + 0x11 = + // 0x15A + + try { + bt = his.readU8(); + b2 = his.readU8(); + b3 = his.readU8(); + } catch (EOFException ex) { + throw new InvalidFileException("Incomplete data"); + } + + len = (b1 & 0xF) << 12; // len = b000 + len |= bt << 4; // len = bcd0 + len |= (b2 >> 4); // len = bcde + len += 0x111; // len = bcde + 0x111 + disp = (b2 & 0x0F) << 8; // disp = f + disp |= b3; // disp = fgh + break; + + default: + // ab cd + // => + // len = a + threshold = a + 1 + // disp = bcd + + len = (b1 >> 4) + 1; + + disp = (b1 & 0x0F) << 8; + try { + b2 = his.readU8(); + } catch (EOFException ex) { + throw new InvalidFileException("Incomplete data"); + } + disp |= b2; + break; + } + + if (disp > curr_size) + throw new InvalidFileException( + "Cannot go back more than already written"); + + cdest = curr_size; + + for (int j = 0; j < len && curr_size < outData.length; j++) + outData[curr_size++] = outData[cdest - disp - 1 + j]; + + if (curr_size > outData.length) + break; + } else { + try { + outData[curr_size++] = his.readU8(); + } catch (EOFException ex) { + break; + }// throw new Exception("Incomplete data"); } + + if (curr_size > outData.length) + break; + } + } + + } + return outData; + } + +} diff --git a/src/pptxt/PPTxtHandler.java b/src/pptxt/PPTxtHandler.java new file mode 100755 index 000000000..aecf51c1f --- /dev/null +++ b/src/pptxt/PPTxtHandler.java @@ -0,0 +1,414 @@ +package pptxt; + +/*----------------------------------------------------------------------------*/ +/*-- PPTxtHandler.java - handles generation 5 games text encoding --*/ +/*-- Code derived from "PPTXT", copyright (C) SCV? --*/ +/*-- Ported to Java and bugfixed/customized by Dabomstew --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.FileNotFoundException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.dabomstew.pkrandom.FileFunctions; + +public class PPTxtHandler { + + public static Map pokeToText = new HashMap(); + public static Map textToPoke = new HashMap(); + + public static Pattern pokeToTextPattern, textToPokePattern; + + static { + try { + Scanner sc = new Scanner( + FileFunctions.openConfig("Generation5.tbl"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine(); + if (!q.trim().isEmpty()) { + String[] r = q.split("=", 2); + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + pokeToText.put(Character.toString((char) Integer.parseInt( + r[0], 16)), + r[1].replace("\\", "\\\\").replace("$", "\\$")); + textToPoke.put(r[1], "\\\\x" + r[0]); + } + } + sc.close(); + pokeToTextPattern = makePattern(pokeToText.keySet()); + textToPokePattern = makePattern(textToPoke.keySet()); + } catch (FileNotFoundException e) { + } + } + + public static Pattern makePattern(Iterable tokens) { + String patternStr = "(" + + implode(tokens, "|").replace("\\", "\\\\") + .replace("[", "\\[").replace("]", "\\]") + .replace("(", "\\(").replace(")", "\\)") + ")"; + return Pattern.compile(patternStr); + } + + public static String implode(Iterable tokens, String sep) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (String token : tokens) { + if (!first) { + sb.append(sep); + } + sb.append(token); + first = false; + } + return sb.toString(); + } + + /** + * Decompress the words given into chars according to 9bits per char format + * Based off poketext's implementation of the same in gen4, but uses all 16 + * bits per word as opposed to 15 + * + * @param chars + * List of words, beginning with [F100] which is skipped. + * @return Decompressed list of integers corresponding to characters + */ + private static List decompress(List chars) { + List uncomp = new ArrayList(); + int j = 1; + int shift1 = 0; + int trans = 0; + while (true) { + int tmp = chars.get(j); + tmp = tmp >> shift1; + int tmp1 = tmp; + if (shift1 >= 0x10) { + shift1 -= 0x10; + if (shift1 > 0) { + tmp1 = (trans | ((chars.get(j) << (9 - shift1)) & 0x1FF)); + if ((tmp1 & 0xFF) == 0xFF) { + break; + } + if (tmp1 != 0x0 && tmp1 != 0x1) { + uncomp.add(tmp1); + } + } + } else { + tmp1 = ((chars.get(j) >> shift1) & 0x1FF); + if ((tmp1 & 0xFF) == 0xFF) { + break; + } + if (tmp1 != 0x0 && tmp1 != 0x1) { + uncomp.add(tmp1); + } + shift1 += 9; + if (shift1 < 0x10) { + trans = ((chars.get(j) >> shift1) & 0x1FF); + shift1 += 9; + } + j += 1; + } + } + return uncomp; + } + + private static List lastKeys; + private static List lastUnknowns; + + /** + * Take a byte-array corresponding to a NARC entry and build a list of + * strings against the gen5 text encryption. Decompresses as appropriate. + * + * @param ds + * The data from this msg.narc entry + * @return The list of strings + */ + + public static List readTexts(byte[] ds) { + int pos = 0; + int i = 0; + lastKeys = new ArrayList(); + lastUnknowns = new ArrayList(); + List strings = new ArrayList(); + int numSections, numEntries, tmpCharCount, tmpUnknown, tmpChar; + int tmpOffset; + int[] sizeSections = new int[] { 0, 0, 0 }; + int[] sectionOffset = new int[] { 0, 0, 0 }; + Map> tableOffsets = new HashMap>(); + Map> characterCount = new HashMap>(); + Map> unknown = new HashMap>(); + Map>> encText = new HashMap>>(); + Map>> decText = new HashMap>>(); + String string = ""; + int key; + + numSections = readWord(ds, 0); + numEntries = readWord(ds, 2); + sizeSections[0] = readLong(ds, 4); + // unk1 = readLong(ds, 8); + pos += 12; + if (numSections > i) { + for (int z = 0; z < numSections; z++) { + sectionOffset[z] = readLong(ds, pos); + pos += 4; + } + pos = sectionOffset[i]; + sizeSections[i] = readLong(ds, pos); + pos += 4; + tableOffsets.put(i, new ArrayList()); + characterCount.put(i, new ArrayList()); + unknown.put(i, new ArrayList()); + encText.put(i, new ArrayList>()); + decText.put(i, new ArrayList>()); + for (int j = 0; j < numEntries; j++) { + tmpOffset = readLong(ds, pos); + pos += 4; + tmpCharCount = readWord(ds, pos); + pos += 2; + tmpUnknown = readWord(ds, pos); + pos += 2; + tableOffsets.get(i).add(tmpOffset); + characterCount.get(i).add(tmpCharCount); + unknown.get(i).add(tmpUnknown); + lastUnknowns.add(tmpUnknown); + } + for (int j = 0; j < numEntries; j++) { + List tmpEncChars = new ArrayList(); + pos = sectionOffset[i] + tableOffsets.get(i).get(j); + for (int k = 0; k < characterCount.get(i).get(j); k++) { + tmpChar = readWord(ds, pos); + pos += 2; + tmpEncChars.add(tmpChar); + } + encText.get(i).add(tmpEncChars); + key = encText.get(i).get(j) + .get(characterCount.get(i).get(j) - 1) ^ 0xFFFF; + for (int k = characterCount.get(i).get(j) - 1; k >= 0; k--) { + encText.get(i) + .get(j) + .set(k, + (encText.get(i).get(j).get(k).intValue()) + ^ key); + if (k == 0) { + lastKeys.add(key); + } + key = ((key >>> 3) | (key << 13)) & 0xffff; + } + if (encText.get(i).get(j).get(0) == 0xF100) { + encText.get(i).set(j, decompress(encText.get(i).get(j))); + characterCount.get(i).set(j, encText.get(i).get(j).size()); + } + List chars = new ArrayList(); + string = ""; + for (int k = 0; k < characterCount.get(i).get(j); k++) { + if (encText.get(i).get(j).get(k) == 0xFFFF) { + chars.add("\\xFFFF"); + } else { + if (encText.get(i).get(j).get(k) > 20 + && encText.get(i).get(j).get(k) <= 0xFFF0 + && Character.UnicodeBlock.of(encText.get(i) + .get(j).get(k)) != null) { + chars.add("" + + ((char) encText.get(i).get(j).get(k) + .intValue())); + } else { + String num = String.format("%04X", encText.get(i) + .get(j).get(k)); + chars.add("\\x" + num); + } + string += chars.get(k); + } + } + strings.add(string); + decText.get(i).add(chars); + } + } + + // Parse strings against the table + for (int sn = 0; sn < strings.size(); sn++) { + strings.set(sn, + bulkReplace(strings.get(sn), pokeToTextPattern, pokeToText)); + } + return strings; + } + + private static String bulkReplace(String string, Pattern pattern, + Map replacements) { + Matcher matcher = pattern.matcher(string); + + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(sb, replacements.get(matcher.group(1))); + } + matcher.appendTail(sb); + + return sb.toString(); + } + + /** + * Write newStrings to the text datafile originalData, as language 0 (the + * only one in most releases BUT japanese). Return the resulting binary as a + * byte-array. Will never use the [F100] compression, even if the original + * file used it. + * + * @param originalData + * The original file, to copy stuff like unknowns. + * @param text + * The new data. + * @return The file to write back to the NARC. + */ + public static byte[] saveEntry(byte[] originalData, List text) { + + // Parse strings against the reverse table + for (int sn = 0; sn < text.size(); sn++) { + text.set(sn, + bulkReplace(text.get(sn), textToPokePattern, textToPoke)); + } + + // Make sure we have the original unknowns etc + readTexts(originalData); + + // Start getting stuff + int numSections, numEntries; + int[] sizeSections = new int[] { 0, 0, 0 }; + int[] sectionOffset = new int[] { 0, 0, 0 }; + int[] newsizeSections = new int[] { 0, 0, 0 }; + int[] newsectionOffset = new int[] { 0, 0, 0 }; + + // Data-Stream + byte[] ds = originalData; + int pos = 0; + + numSections = readWord(ds, 0); + numEntries = readWord(ds, 2); + sizeSections[0] = readLong(ds, 4); + // unk1 readLong(ds, 8); + pos += 12; + + if (text.size() < numEntries) { + System.err.println("Can't do anything due to too few lines"); + return originalData; + } else { + byte[] newEntry = makeSection(text, numEntries); + for (int z = 0; z < numSections; z++) { + sectionOffset[z] = readLong(ds, pos); + pos += 4; + } + for (int z = 0; z < numSections; z++) { + pos = sectionOffset[z]; + sizeSections[z] = readLong(ds, pos); + pos += 4; + } + newsizeSections[0] = newEntry.length; + + byte[] newData = new byte[ds.length - sizeSections[0] + + newsizeSections[0]]; + System.arraycopy(ds, 0, newData, 0, + Math.min(ds.length, newData.length)); + writeLong(newData, 4, newsizeSections[0]); + if (numSections == 2) { + newsectionOffset[1] = newsizeSections[0] + sectionOffset[0]; + writeLong(newData, 0x10, newsectionOffset[1]); + } + System.arraycopy(newEntry, 0, newData, sectionOffset[0], + newEntry.length); + if (numSections == 2) { + System.arraycopy(ds, sectionOffset[1], newData, + newsectionOffset[1], sizeSections[1]); + } + return newData; + } + } + + private static byte[] makeSection(List strings, int numEntries) { + List> data = new ArrayList>(); + int size = 0; + int offset = 4 + 8 * numEntries; + int charCount; + for (int i = 0; i < numEntries; i++) { + data.add(parseString(strings.get(i), i)); + size += (data.get(i).size() * 2); + } + if (size % 4 == 2) { + size += 2; + int tmpKey = lastKeys.get(numEntries - 1); + for (int i = 0; i < data.get(numEntries - 1).size(); i++) { + tmpKey = ((tmpKey << 3) | (tmpKey >> 13)) & 0xFFFF; + } + data.get(numEntries - 1).add(0xFFFF ^ tmpKey); + } + size += offset; + byte[] section = new byte[size]; + int pos = 0; + writeLong(section, pos, size); + pos += 4; + for (int i = 0; i < numEntries; i++) { + charCount = data.get(i).size(); + writeLong(section, pos, offset); + pos += 4; + writeWord(section, pos, charCount); + pos += 2; + writeWord(section, pos, lastUnknowns.get(i)); + pos += 2; + offset += (charCount * 2); + } + for (int i = 0; i < numEntries; i++) { + for (int word : data.get(i)) { + writeWord(section, pos, word); + pos += 2; + } + } + return section; + } + + private static List parseString(String string, int entry_id) { + List chars = new ArrayList(); + for (int i = 0; i < string.length(); i++) { + if (string.charAt(i) != '\\') { + chars.add((int) string.charAt(i)); + } else { + if (((i + 2) < string.length()) && string.charAt(i + 2) == '{') { + chars.add((int) string.charAt(i)); + } else { + chars.add(Integer.parseInt(string.substring(i + 2, i + 6), + 16)); + i += 5; + } + } + } + chars.add(0xFFFF); + int key = lastKeys.get(entry_id); + for (int i = 0; i < chars.size(); i++) { + chars.set(i, (chars.get(i) ^ key) & 0xFFFF); + key = ((key << 3) | (key >>> 13)) & 0xFFFF; + } + return chars; + } + + private static int readWord(byte[] data, int offset) { + return (data[offset] & 0xFF) + ((data[offset + 1] & 0xFF) << 8); + } + + private static int readLong(byte[] data, int offset) { + return (data[offset] & 0xFF) + ((data[offset + 1] & 0xFF) << 8) + + ((data[offset + 2] & 0xFF) << 16) + + ((data[offset + 3] & 0xFF) << 24); + } + + protected static void writeWord(byte[] data, int offset, int value) { + data[offset] = (byte) (value & 0xFF); + data[offset + 1] = (byte) ((value >> 8) & 0xFF); + } + + protected static void writeLong(byte[] data, int offset, int value) { + data[offset] = (byte) (value & 0xFF); + data[offset + 1] = (byte) ((value >> 8) & 0xFF); + data[offset + 2] = (byte) ((value >> 16) & 0xFF); + data[offset + 3] = (byte) ((value >> 24) & 0xFF); + } +} diff --git a/src/thenewpoketext/PokeTextData.java b/src/thenewpoketext/PokeTextData.java new file mode 100755 index 000000000..9bb3aad79 --- /dev/null +++ b/src/thenewpoketext/PokeTextData.java @@ -0,0 +1,205 @@ +package thenewpoketext; + +/*----------------------------------------------------------------------------*/ +/*-- PokeTextData.java - decodes gen4 games text into Unicode --*/ +/*-- Code derived from "thenewpoketext", copyright (C) loadingNOW --*/ +/*-- Ported to Java and bugfixed/customized by Dabomstew --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class PokeTextData { + private byte[] data; + public List ptrlist; + public List strlist; + public boolean compressFlag; + + public PokeTextData(byte[] data) { + this.data = Arrays.copyOf(data, data.length); + } + + public byte[] get() { + return data; + } + + private int read16(int ofs) { + return (data[ofs] & 0xFF) | ((data[ofs + 1] & 0xFF) << 8); + } + + private void write16(int d, int ofs) { + data[ofs] = (byte) (d & 0xFF); + data[ofs + 1] = (byte) ((d >> 8) & 0xFF); + } + + private int read32(int ofs) { + return (data[ofs] & 0xFF) | ((data[ofs + 1] & 0xFF) << 8) + | ((data[ofs + 2] & 0xFF) << 16) + | ((data[ofs + 3] & 0xFF) << 24); + } + + private void write32(int d, int ofs) { + data[ofs] = (byte) (d & 0xFF); + data[ofs + 1] = (byte) ((d >> 8) & 0xFF); + data[ofs + 2] = (byte) ((d >> 16) & 0xFF); + data[ofs + 3] = (byte) ((d >> 24) & 0xFF); + } + + public void decrypt() { + DecyptPtrs(read16(0), read16(2), 4); + this.ptrlist = CreatePtrList(read16(0), 4); + + this.strlist = new ArrayList(); + + int num = read16(0); + + for (int i = 0; i < num; i++) { + PointerEntry entry = this.ptrlist.get(i); + DecyptTxt(entry.getChars(), i + 1, entry.getPtr()); + this.strlist.add(MakeString(entry.getChars(), entry.getPtr())); + } + } + + public void encrypt() { + this.ptrlist = CreatePtrList(read16(0), 4); + int num = read16(0); + for (int i = 0; i < num; i++) { + PointerEntry entry = this.ptrlist.get(i); + DecyptTxt(entry.getChars(), i + 1, entry.getPtr()); + } + + DecyptPtrs(read16(0), read16(2), 4); + } + + private void DecyptPtrs(int count, int key, int sdidx) { + key = (key * 0x2FD) & 0xFFFF; + + for (int i = 0; i < count; i++) { + int key2 = (key * (i + 1) & 0xFFFF); + int realkey = key2 | (key2 << 16); + write32(read32(sdidx) ^ realkey, sdidx); + write32(read32(sdidx + 4) ^ realkey, sdidx + 4); + sdidx += 8; + } + + } + + private List CreatePtrList(int count, int sdidx) { + List ptrlist = new ArrayList(); + for (int i = 0; i < count; i++) { + ptrlist.add(new PointerEntry(read32(sdidx), read32(sdidx + 4))); + sdidx += 8; + } + return ptrlist; + } + + private void DecyptTxt(int count, int id, int idx) { + int key = (0x91BD3 * id) & 0xFFFF; + for (int i = 0; i < count; i++) { + write16(read16(idx) ^ key, idx); + key += 0x493D; + key = key & 0xFFFF; + idx += 2; + } + + } + + private String MakeString(int count, int idx) { + StringBuilder string = new StringBuilder(); + List chars = new ArrayList(); + List uncomp = new ArrayList(); + for (int i = 0; i < count; i++) { + chars.add(read16(idx)); + idx += 2; + } + + if (chars.get(0) == 0xF100) { + compressFlag = true; + int j = 1; + int shift1 = 0; + int trans = 0; + while (true) { + int tmp = chars.get(j); + tmp = tmp >> shift1; + int tmp1 = tmp; + if (shift1 >= 0xF) { + shift1 -= 0xF; + if (shift1 > 0) { + tmp1 = (trans | ((chars.get(j) << (9 - shift1)) & 0x1FF)); + if (tmp1 == 0x1FF) { + break; + } + uncomp.add(tmp1); + } + } else { + tmp1 = ((chars.get(j) >> shift1) & 0x1FF); + if (tmp1 == 0x1FF) { + break; + } + uncomp.add(tmp1); + shift1 += 9; + if (shift1 < 0xF) { + trans = ((chars.get(j) >> shift1) & 0x1FF); + shift1 += 9; + } + j += 1; + } + } + chars = uncomp; + } + int i = 0; + for (int c = 0; c < chars.size(); c++) { + int currChar = chars.get(i); + if (UnicodeParser.tb[currChar] != null) { + string.append(UnicodeParser.tb[currChar]); + } else { + if (currChar == 0xFFFE) { + i++; + string.append("\\v" + String.format("%04X", chars.get(i))); + i++; + int total = chars.get(i); + for (int z = 0; z < total; z++) { + i++; + string.append("\\z" + + String.format("%04X", chars.get(i))); + } + } else if (currChar == 0xFFFF) { + break; + } else { + string.append("\\x" + String.format("%04X", chars.get(i))); + } + } + i++; + } + return string.toString(); + } + + public void SetKey(int key) { + write16(key, 2); + } + + public int GetKey() { + return read16(2); + } + + public class PointerEntry { + + private int ptr; + private int chars; + + public PointerEntry(int ptr, int chars) { + this.ptr = ptr; + this.chars = chars; + } + + public int getPtr() { + return ptr; + } + + public int getChars() { + return chars; + } + } + +} diff --git a/src/thenewpoketext/TextToPoke.java b/src/thenewpoketext/TextToPoke.java new file mode 100755 index 000000000..88ecade28 --- /dev/null +++ b/src/thenewpoketext/TextToPoke.java @@ -0,0 +1,190 @@ +package thenewpoketext; + +/*----------------------------------------------------------------------------*/ +/*-- TextToPoke.java - encodes gen4 games text from Unicode --*/ +/*-- Code derived from "thenewpoketext", copyright (C) loadingNOW --*/ +/*-- Ported to Java and bugfixed/customized by Dabomstew --*/ +/*----------------------------------------------------------------------------*/ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class TextToPoke { + + public static byte[] MakeFile(List textarr, boolean compressed) { + int base = textarr.size() * 8 + 4; + List ptrtable = new ArrayList(); + List> rawdata = new ArrayList>(); + for (int i = 0; i < textarr.size(); i++) { + List data = ToCode(textarr.get(i), compressed); + int l = data.size(); + ptrtable.add(new PointerEntry(base, l)); + rawdata.add(data); + base += l * 2; + } + + List hdr = Arrays.asList(textarr.size(), 0); + + return join(wordListToBarr(hdr), pointerListToBarr(ptrtable), + listOfWordListToBarr(rawdata)); + } + + private static List ToCode(String text, boolean compressed) { + List data = new ArrayList(); + while (text.length() != 0) { + int i = Math.max(0, 6 - text.length()); + if (text.charAt(0) == '\\') { + if (text.charAt(1) == 'x') { + data.add(Integer.parseInt(text.substring(2, 6), 16)); + text = text.substring(6); + } else if (text.charAt(1) == 'v') { + data.add(0xFFFE); + data.add(Integer.parseInt(text.substring(2, 6), 16)); + text = text.substring(6); + } else if (text.charAt(1) == 'z') { + List var = new ArrayList(); + int w = 0; + while (text.length() != 0) { + if (text.charAt(0) == '\\' && text.charAt(1) == 'z') { + w++; + var.add(Integer.parseInt(text.substring(2, 6), 16)); + text = text.substring(6); + } else { + break; + } + } + data.add(w); + data.addAll(var); + } else if (text.charAt(1) == 'n') { + data.add(0xE000); + text = text.substring(2); + } else if (text.charAt(1) == 'r') { + data.add(0x25BC); + text = text.substring(2); + } else if (text.charAt(1) == 'f') { + data.add(0x25BD); + text = text.substring(2); + } else if (text.substring(1, 4).equals("and")) { + data.add(0x1C2); + text = text.substring(4); + } else { + System.out.printf("unknown escape: %s\n", + text.substring(1, 2)); + text = text.substring(2); + } + } else { + while (!(UnicodeParser.d.containsKey(text.substring(0, 6 - i)) || (i == 6))) { + i++; + } + if (i == 6) { + System.out.printf("Char not found %s(%d)", + text.substring(0, 1), text.charAt(0)); + text = text.substring(1); + } else { + data.add(UnicodeParser.d.get(text.substring(0, 6 - i))); + text = text.substring(6 - i); + } + } + } + if (compressed) { + if (data.size() % 5 != 0 || data.size() == 0) { + data.add(0x1FF); + } + byte[] bits = new byte[data.size() * 9]; + int bc = 0; + for (int i = 0; i < data.size(); i++) { + for (int j = 0; j < 9; j++) { + bits[bc++] = (byte) ((data.get(i) >> j) & 1); + } + } + int tmp_uint16 = 0; + data.clear(); + data.add(0xF100); + for (int i = 0; i < bits.length; i++) { + if (i % 15 == 0 && i != 0) { + data.add(tmp_uint16); + tmp_uint16 = 0; + } + tmp_uint16 |= (bits[i] << (i % 15)); + } + data.add(tmp_uint16); + } + data.add(0xFFFF); + return data; + } + + private static byte[] join(byte[]... args) { + int tlen = 0; + for (byte[] arr : args) { + tlen += arr.length; + } + byte[] barr = new byte[tlen]; + int offs = 0; + for (byte[] arr : args) { + System.arraycopy(arr, 0, barr, offs, arr.length); + offs += arr.length; + } + return barr; + } + + private static byte[] wordListToBarr(List list) { + byte[] barr = new byte[list.size() * 2]; + int l = list.size(); + for (int i = 0; i < l; i++) { + barr[i * 2] = (byte) (list.get(i) & 0xFF); + barr[i * 2 + 1] = (byte) ((list.get(i) >> 8) & 0xFF); + } + return barr; + } + + private static byte[] pointerListToBarr(List ptrList) { + byte[] data = new byte[ptrList.size() * 8]; + int l = ptrList.size(); + for (int i = 0; i < l; i++) { + int ofs = i * 8; + PointerEntry ent = ptrList.get(i); + data[ofs] = (byte) (ent.ptr & 0xFF); + data[ofs + 1] = (byte) ((ent.ptr >> 8) & 0xFF); + data[ofs + 2] = (byte) ((ent.ptr >> 16) & 0xFF); + data[ofs + 3] = (byte) ((ent.ptr >> 24) & 0xFF); + data[ofs + 4] = (byte) (ent.chars & 0xFF); + data[ofs + 5] = (byte) ((ent.chars >> 8) & 0xFF); + data[ofs + 6] = (byte) ((ent.chars >> 16) & 0xFF); + data[ofs + 7] = (byte) ((ent.chars >> 24) & 0xFF); + } + return data; + } + + private static byte[] listOfWordListToBarr(List> list) { + int tlen = 0; + for (List subList : list) { + tlen += subList.size() * 2; + } + byte[] barr = new byte[tlen]; + int offs = 0; + int l1 = list.size(); + for (int j = 0; j < l1; j++) { + List slist = list.get(j); + int l2 = slist.size(); + for (int i = 0; i < l2; i++) { + barr[offs] = (byte) (slist.get(i) & 0xFF); + barr[offs + 1] = (byte) ((slist.get(i) >> 8) & 0xFF); + offs += 2; + } + } + return barr; + } + + private static class PointerEntry { + + private int ptr; + private int chars; + + public PointerEntry(int ptr, int chars) { + this.ptr = ptr; + this.chars = chars; + } + } + +} diff --git a/src/thenewpoketext/UnicodeParser.java b/src/thenewpoketext/UnicodeParser.java new file mode 100755 index 000000000..5f1acc477 --- /dev/null +++ b/src/thenewpoketext/UnicodeParser.java @@ -0,0 +1,42 @@ +package thenewpoketext; + +/*----------------------------------------------------------------------------*/ +/*-- UnicodeParser.java - maintains the poke<->unicode text table --*/ +/*-- Code loosely derived from "thenewpoketext", copyright (C) loadingNOW --*/ +/*-- Ported to Java and customized by Dabomstew --*/ +/*----------------------------------------------------------------------------*/ + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +import com.dabomstew.pkrandom.FileFunctions; + +public class UnicodeParser { + + public static String[] tb = new String[65536]; + public static Map d = new HashMap(); + + static { + try { + Scanner sc = new Scanner( + FileFunctions.openConfig("Generation4.tbl"), "UTF-8"); + while (sc.hasNextLine()) { + String q = sc.nextLine(); + if (!q.trim().isEmpty()) { + String[] r = q.split("=", 2); + if (r[1].endsWith("\r\n")) { + r[1] = r[1].substring(0, r[1].length() - 2); + } + tb[Integer.parseInt(r[0], 16)] = r[1]; + d.put(r[1], Integer.parseInt(r[0], 16)); + } + } + sc.close(); + } catch (FileNotFoundException e) { + } + } + +}