use strict;
use Cwd;
use utf8;
use word_list;
use util;
use letter;
use errata;

#***********************************************************
# パッケージsorting
#***********************************************************
package sorting;

my $DAT_DIR = "../../zukan_data";
my $DST_DIR = "../../../../src/application/zukanlist/zkn_data";
my $IDX = "../../../../include/application/zukanlist/zkn_sort_akstnhmyrw_idx_%s.h";
my $ARC = "zukan_data.narc";
my $AIX = "zukan_data.naix";
my $WORD = "../../../../src/application/pms_input/pms_input_%s.res";
my $DUP = "../../../../src/system/pms_word_%s.res";
my $PWD = "../../../pms/pwdlist_%s.dat";
my $GEO = "../../../../src/application/wifi_earth/geo_sort_%s.res";

my %SORT = (
	"eng" => \&sort_logic_eng,
	"fra" => \&sort_logic_euro,
	"ger" => \&sort_logic_euro,
	"ita" => \&sort_logic_euro,
	"spa" => \&sort_logic_euro,
	"kor" => \&sort_logic_euro,
);
my (@TAG_REP, @PHASE1_REP, %PHASE1, %PHASE2, %HUNGEUL_KIND, @HUN_HEAD);

#===========================================================
# 初期処理。
#===========================================================
{
	my ($c, $cnt, $phase1, $phase2, @hun_head, $id);

	$phase2 = $letter::PHASE2_1 . $letter::PHASE2_HUNGEUL . $letter::PHASE2_2;
	# ハングル分類用。
	while (($c = substr($letter::PHASE2_HUNGEUL_HEAD, 0, 1, "")) ne "") {
		push @hun_head, $c;
	}
	@HUN_HEAD = ( @hun_head );
	push @hun_head, "";		# 番兵
	$id = 0;
	while (($c = substr($letter::PHASE2_HUNGEUL, 0, 1, "")) ne "") {
		if ($c eq $hun_head[$id + 1]) {
			$id++;
		}
		$HUNGEUL_KIND{$c} = $hun_head[$id];
	}

	for my $i (keys %letter::TAG) {
		push @TAG_REP, { reg => qr/\Q$i\E[^\]]*\]/, rep => "_$letter::TAG{$i}" };
	}
	for my $i (@letter::PHASE1_TABLE) {
		my ($str_rep);

		# 順序文字列の置換用文字。
		$str_rep = $i->{phase1};
		if ($str_rep ne "") {
			$str_rep = substr($str_rep, 0, 1);
		}
		while (($c = substr($i->{trans}, 0, 1, "")) ne "") {
			push @PHASE1_REP, { reg => qr/\Q$c/, rep => $i->{phase1}, str_rep => $str_rep };
		}
	}
	$phase1 = $phase2;
	$phase1 = "\U$phase1";
	for my $i (@PHASE1_REP) {
		$phase1 =~ s/$i->{reg}/$i->{str_rep}/g;
	}
	$cnt = 1;
	while (($c = substr($phase1, 0, 1, "")) ne "") {
		if (!exists($PHASE1{$c})) {
			$PHASE1{$c} = pack('U', $cnt++);
		}
	}
	$cnt = 1;
	while (($c = substr($phase2, 0, 1, "")) ne "") {
		$PHASE2{$c} = pack('U', $cnt++);
	}
}

#===========================================================
# ポケモン名ソート。
#===========================================================
sub pokeName {
	my ($name, $lang_mark) = @_;
	my (%each, $cnt, $sort_name, $cwd, $data_a, $data_b, $cnt2, %each2);
	my (@line);
	my @sort = (
		{ fname => "zkn_sort_aiueo.dat", data => [] },
		{ fname => "zkn_sort_a.dat",     data => [] },
		{ fname => "zkn_sort_ka.dat",    data => [] },
		{ fname => "zkn_sort_sa.dat",    data => [] },
		{ fname => "zkn_sort_ta.dat",    data => [] },
		{ fname => "zkn_sort_na.dat",    data => [] },
		{ fname => "zkn_sort_ha.dat",    data => [] },
		{ fname => "zkn_sort_ma.dat",    data => [] },
		{ fname => "zkn_sort_ra.dat",    data => [] },
		{ fname => "zkn_sort_yawa.dat",  data => [] }
	);
	my @index = (
		{ ename => "ZKN_AKSTNHMYRW_IDX_1" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_2" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_3" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_4" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_5" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_6" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_7" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_8" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_9" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_10" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_11" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_12" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_13" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_14" },
		{ ename => "ZKN_AKSTNHMYRW_IDX_END" }
	);

	$sort_name = $SORT{$lang_mark}($name);

	# 個別ソートのテーブルを構築。
	$cnt = 1;
	$cnt2 = 0;
	for my $i (@letter::POKE_EACH) {
		my ($c);

		while ($c = substr($i, 0, 1, "")) {
			$each{$c} = $sort[$cnt]{data};
			$each2{$c} = \$index[$cnt2++]{count};
		}
		$cnt++;
	}

	# ソート結果の分別。
	for my $i (@{$sort_name}) {
		my ($h);

		if ($i->{text} !~ /^(.)/) {
			die "ポケモン名の先頭文字が無い。\n";
		}
		$h = $1;
		if (!defined($HUNGEUL_KIND{$h})) {
			die "ポケモン名の先頭文字が想定外。\n";
		}
		push @{$each{$HUNGEUL_KIND{$h}}}, $i;
		${$each2{$HUNGEUL_KIND{$h}}}++;
		push @{$sort[0]{data}}, $i;
	}

	# ソート結果を保存。
	for my $i (@sort) {
		my ($res);

		for my $j (@{$i->{data}}) {
			$res .= pack("S", $j->{no});
		}

		if (!open(RES, ">$DAT_DIR/$i->{fname}")) {
			die "$i->{fname}が開けません。\n";
		}
		binmode(RES, "raw");
		print RES $res;
		close RES;
	}

	# アーカイブの作成と移動。
	$cwd = Cwd::getcwd();
	Cwd::chdir $DAT_DIR;
	`nnsarc -c -i -n zukan_data.narc -S zkn_arc_list.lst`;
	if ($? != 0) {
		die "nnsarc失敗\n";
	}
	Cwd::chdir $cwd;
	rename("$DAT_DIR/$ARC", "$DST_DIR/$lang_mark/$ARC");
	unlink("$DAT_DIR/$AIX");

	# 個別ソートのインデックス値を保存。
	$index[0]{num} = 0;
	for (my $i = 1; $i < @index; $i++) {
		$index[$i]{num} = $index[$i - 1]{num} + $index[$i - 1]{count};
	}

	$data_a = "#ifndef __ZKN_AKSTNHMYRW_HEADER__\n";
	$data_a .= "#define __ZKN_AKSTNHMYRW_HEADER__\n\n";
	for my $i (@index) {
		$data_a .= "#define $i->{ename}  ($i->{num})\n";
	}
	$data_a .= "\n";
	$data_a .= "#endif // __ZKN_AKSTNHMYRW_HEADER__\n";

	util::updateAndSave(sprintf($IDX, $lang_mark), $data_a, "encoding(shiftjis)");
}

#===========================================================
# 簡易会話ワードソート。
#===========================================================
sub simpleWord {
	my ($word_list, $lang_mark, $lang) = @_;
	my (@all, @cap, $add_to_list, @pwd_list, $all, $cnt, %cap_p);
	my %sort_list = ( each => [
		{ name => "ポケモン",       pwd => 0 },
		{ name => "ポケモン２",     pwd => 0 },
		{ name => "わざ",           pwd => 0 },
		{ name => "わざ２",         pwd => 0 },
		{ name => "ステータス",     pwd => 1 },
		{ name => "トレーナー",     pwd => 1 },
		{ name => "ひと",           pwd => 1 },
		{ name => "あいさつ",       pwd => 0 },
		{ name => "せいかつ",       pwd => 1 },
		{ name => "きもち",         pwd => 1 },
		{ name => "なんかいことば", pwd => 0 },
		{ name => "ユニオン",       pwd => 0 }
	], cap => [
		{ name => "─┐\n  │\n" }, { name => "│\n└─\n" }, { name => "┌─\n└─\n" },
		{ name => "─┐\n┌┘\n└─\n" }, { name => "┌─┐\n└─┘\n" },
		{ name => "├─┤\n└─┘\n" }, { name => "  │\n─┴─\n" }, { name => "○\n" },
		{ name => "─┬─\n─┴─\n" }, { name => "  ─\n─┬─\n─┴─\n" },
		{ name => "─┐\n─┤\n" }, { name => "┌─\n├─\n└─\n" }, { name => "┬┬\n┴┴\n" },
		{ name => "二\n○\n" }, { name => "その他\n" }
	] );

	# ワード全体を追加。
	for my $i (@{$word_list}) {
		for my $j (@{$i}) {
			push @all, { %{$j} };
		}
	}
	# 全ソート。
	$all = $SORT{$lang_mark}(\@all);

	# 個別ソートのテーブルを構築。
	$cnt = 0;
	for my $i (@HUN_HEAD) {
		$cap_p{$i} = $cap[$cnt] = [];
		$cnt++;
	}

	# 先頭文字で分類。
	for my $i (@{$all}) {
		my ($h);

		if ($i->{text} !~ /^(.)/) {
			die "簡易会話ワードの先頭文字が無い。\n";
		}
		$h = $1;
		if (defined($HUNGEUL_KIND{$h})) {
			push @{$cap_p{$HUNGEUL_KIND{$h}}}, $i;
		} else {
			push @{$cap[14]}, $i;
		}
	}
	$add_to_list = sub {
		my ($idx, $list, $prev) = @_;

		if (@{$list} != 1) {
			push @{$sort_list{cap}[$idx]{data}}, $list;
		} else {
			push @{$sort_list{cap}[$idx]{data}}, $prev;
		}
	};
	# ダブりを処理しながらリストに追加。
	for (my $i = 0; $i < @cap; $i++) {
		my ($list, $prev);

		$list = [ $cap[$i][0] ];
		$prev = $cap[$i][0];
		for my $j (@{$cap[$i]}[1 .. $#{$cap[$i]}]) {
			if ($j->{text} ne $prev->{text}) {
				$add_to_list->($i, $list, $prev);
				$list = [ $j ];
				$prev = $j;
			} else {
				push @{$list}, $j;
			}
		}
		$add_to_list->($i, $list, $prev);
	}

	# カテゴリ別にソート。
	sort_and_divide($sort_list{each}, 0, $word_list->[0], $lang_mark);
	sort_and_divide($sort_list{each}, 2, $word_list->[1], $lang_mark);
	for (my $i = 2; $i < 10; $i++) {
		my ($prev);

		$sort_list{each}[$i + 2]{data} = $SORT{$lang_mark}($word_list->[$i]);
		if ($sort_list{each}[$i + 2]{pwd}) {
			for my $j (@{$sort_list{each}[$i + 2]{data}}) {
				if ($j->{text} ne $prev || !defined($letter::SIMPLE_DUP{$lang}{$j->{text}})) {
					push @pwd_list, $j;
					$prev = $j->{text};
				}
			}
		}
	}

	# 出力処理。
	output_word(\%sort_list, $lang_mark, $lang);
	output_index(\%sort_list, $lang_mark, $lang);
	output_pwdlist(\@pwd_list, $lang_mark);
}

#===========================================================
# ジオネットの国名と地域名のソート処理。
#===========================================================
sub geonet {
	my ($geonet, $lang_mark) = @_;
	my (%sort_list, %except);
	my @EXCEPT_COUNTRY = (
		"country004", "country005", "country007", "country010", "country011",
		"country014", "country019", "country024", "country026", "country030",
		"country032", "country037", "country038", "country039", "country041",
		"country044", "country046", "country047", "country051", "country053",
		"country057", "country063", "country064", "country065", "country066",
		"country067", "country068", "country073", "country075", "country076",
		"country084", "country087", "country096", "country099", "country105",
		"country106", "country108", "country109", "country112", "country113",
		"country114", "country116", "country119", "country120", "country123",
		"country124", "country125", "country127", "country128", "country130",
		"country132", "country134", "country136", "country137", "country138",
		"country139", "country141", "country143", "country144", "country145",
		"country147", "country153", "country154", "country155", "country159",
		"country162", "country165", "country168", "country169", "country170",
		"country173", "country174", "country175", "country176", "country177",
		"country178", "country180", "country181", "country182", "country184",
		"country185", "country190", "country191", "country195", "country197",
		"country201", "country203", "country206", "country208", "country209",
		"country210", "country213", "country214", "country215", "country217",
		"country223", "country225", "country228", "country229", "country230",
		"country231", "country232", "country233"
	);

	for my $i (@{$geonet->{place}}) {
		my (%place);

		$place{name} = $i->{name};
		$place{country} = $i->{country};
		$place{data} = $SORT{$lang_mark}($i->{data});
		push @{$sort_list{place}}, \%place;
	}
	$sort_list{country} = $SORT{$lang_mark}($geonet->{country});

	for my $i (@EXCEPT_COUNTRY) {
		$except{$i} = 1;
	}
	# 出力処理。
	output_geonet(\%sort_list, \%except, $lang_mark);
}

#-----------------------------------------------------------
# private
#-----------------------------------------------------------

#===========================================================
# ソート処理(英語)。
#===========================================================
sub sort_logic_eng {
	my ($in) = @_;
	my ($sub, @out);

	$sub = sub {
		return $a->{text} cmp $b->{text} ||
			$word_list::NO{$a->{msg_id}} <=> $word_list::NO{$b->{msg_id}};
	};
	@out = sort $sub @{$in};
	return \@out;
}

#===========================================================
# ソート処理(欧州)。
#===========================================================
sub sort_logic_euro {
	my ($in) = @_;
	my ($sub, @list, @out);

	for my $i (@{$in}) {
		my ($text, $save, $c, $phase1, $phase2);

		$text = $i->{text};
		# まず、タグを_XXX_に置換。
		for my $j (@TAG_REP) {
			$text =~ s/$j->{reg}/$j->{rep}/g;
		}
		# 完全置換を実行。
		for my $j (@letter::REP_TABLE) {
			$text =~ s/$j->{reg}/$j->{rep}/g;
		}
		# この段階でタグが残っていたらダメ。
		if ($text =~ /_([^_]*)_/) {
			die "ソート対象に未解決のタグがある。\n";
		}
		# phase1比較用文字列作成。
		$save = $text;
		$text = "\U$text";
		for my $j (@PHASE1_REP) {
			$text =~ s/$j->{reg}/$j->{rep}/g;
		}
		while (($c = substr($text, 0, 1, "")) ne "") {
			if (!exists($PHASE1{$c})) {
				die "phase1に定義のない文字。\n";
			}
			$phase1 .= $PHASE1{$c};
		}
		# phase2比較用文字列作成。
		$text = $save;
		while (($c = substr($text, 0, 1, "")) ne "") {
			if (!exists($PHASE2{$c})) {
				die "phase2に定義のない文字。\n";
			}
			$phase2 .= $PHASE2{$c};
		}

		$i->{sort_info} = { phase1 => $phase1, phase2 => $phase2 };
	}
	$sub = sub {
		return $a->{sort_info}{phase1} cmp $b->{sort_info}{phase1} ||
			$a->{sort_info}{phase2} cmp $b->{sort_info}{phase2};
	};
	@out = sort $sub @{$in};
	return \@out;
}

#===========================================================
# ソートして分割。
#===========================================================
sub sort_and_divide {
	my ($sort_list, $idx, $list, $lang_mark) = @_;
	my ($cnt);

	$list = $SORT{$lang_mark}($list);
	for my $i (@{$list}) {
		my ($h);

		$i->{text} =~ /^(.)/;
		$h = $1;
		if ($HUNGEUL_KIND{$h} eq $HUN_HEAD[6]) {
			last;
		}
		$cnt++;
	}
	@{$sort_list->[$idx++]{data}} = @{$list}[0 .. $cnt - 1];
	@{$sort_list->[$idx]{data}} = @{$list}[$cnt .. $#{$list}];
}

#===========================================================
# 単語ソート結果出力。
#===========================================================
sub output_word {
	my ($sort_list, $lang_mark, $lang) = @_;
	my ($str, $idx);

	$str = "";
	$str .= "#ifdef __PMS_INPUT_RES__\n\n";
	$str .= "#define PMS_WORDID_END\t(0xffff)\n";
	$str .= "#define PMS_WORDID_DUP\t(0xfffe)\n\n";
	$str .= "#define PMS_WORDNUM_MAX\t(1495)\t// 総単語数\n\n";
	$str .= "//==========================================================\n";
	$str .= "// カテゴリごとの単語IDテーブル\n";
	$str .= "//==========================================================\n\n";
	$idx = 0;
	for my $i (@{$sort_list->{each}}) {
		my ($cnt, $prev, $dup_id);
		my $no = sprintf("%02d", $idx + 1);

		$str .= "// $i->{name}\n";
		$str .= "static const u16 PMS_CategoryTable_${no}[] = {\n";
		$prev = "";
		for my $j (@{$i->{data}}) {
			my ($info);

			$info = $letter::SIMPLE_DUP{$lang}{$j->{text}};
			if ($j->{text} ne $prev) {
				$dup_id = 0;
			}
			if (defined($info)) {
				if ($word_list::NO{$j->{msg_id}} != $info->[$dup_id]{no}) {
					die "$j->{text}の$word_list::NO{$j->{msg_id}}は" . ($dup_id + 1) . "番目でない。\n";
				}
				if ($info->[$dup_id]{use}) {
					$str .= sprintf("\t%4d,\n", $word_list::NO{$j->{msg_id}});
					$cnt++;
				}
				$dup_id++;
			} else {
				if ($j->{text} eq $prev) {
					print errata::out("未ダブり", $i->{name}, $j->{text});
				}
				$str .= sprintf("\t%4d,\n", $word_list::NO{$j->{msg_id}});
				$cnt++;
			}
			$prev = $j->{text};
		}
		$str .= "\tPMS_WORDID_END\n";
		$str .= "};\n\n";
		$str .= "#define  PMS_Category_${no}_MaxNum\t($cnt)\n\n";
		$idx++;
	}
	$str .= "//==========================================================\n";
	$str .= "// 文字順単語IDテーブル\n";
	$str .= "//==========================================================\n\n";
	$idx = 0;
	for my $i (@{$sort_list->{cap}}) {
		$i->{name} =~ s/^/\/\/ /mg;
		$str .= $i->{name};
		$str .= "static const u16 PMS_InitialTable_${idx}[] = {\n";
		for my $j (@{$i->{data}}) {
			if (ref($j) eq "ARRAY") {
				my ($info);

				$info = $letter::SIMPLE_DUP{$lang}{$j->[0]{text}};
				if (defined($info)) {
					my ($dup_id);

					# カテゴリ内でダブルものが、カテゴリを越えて一致しないという前提。
					# 仮にそのようなことがあれば、カテゴリごとの出力でエラーになる。
					for my $k (@{$j}) {
						if ($info->[$dup_id]{use}) {
							$str .= sprintf("\t%4d,\n", $word_list::NO{$k->{msg_id}});
							last;
						}
						$dup_id++;
					}
				} else {
					$str .= "\tPMS_WORDID_DUP," . scalar(@{$j}) . ", ";
					for my $k (@{$j}) {
						$str .= "$word_list::NO{$k->{msg_id}},";
					}
					$str .= "\n";
				}
			} else {
				$str .= sprintf("\t%4d,\n", $word_list::NO{$j->{msg_id}});
			}
		}
		$str .= "\tPMS_WORDID_END\n";
		$str .= "};\n\n";
		$idx++;
	}
	$str .= "// 文字順テーブルの先頭アドレスをテーブル化しておく\n";
	$str .= "static const u16* const PMS_InitialTable[] = {\n";
	for my $i (0 .. $#{$sort_list->{cap}}) {
		$str .= "\tPMS_InitialTable_$i,\n";
	}
	$str .= "};\n\n";
	$str .= "#undef __PMS_INPUT_RES__\n";
	$str .= "#endif  // __PMS_INPUT_RES__\n";

	util::updateAndSave(sprintf($WORD, $lang_mark), $str, "encoding(shiftjis)");
}

#===========================================================
# 単語数テーブル結果出力。
#===========================================================
sub output_index {
	my ($sort_list, $lang_mark, $lang) = @_;
	my (@dup, $str, $idx, @line);

	# ダブりのデータを抽出。
	for my $i (@{$sort_list->{cap}}) {
		for my $j (@{$i->{data}}) {
			if (ref($j) eq "ARRAY" && !defined($letter::SIMPLE_DUP{$lang}{$j->[0]{text}})) {
				for my $k (@{$j}) {
					push @{$dup[$idx]}, $word_list::NO{$k->{msg_id}};
				}
				$idx++;
			}
		}
	}

	if (!open(HEAD, "pms_input_head.txt")) {
		die "ファイルが開けません。\n";
	}
	binmode(HEAD, "encoding(shiftjis)");
	@line = <HEAD>;
	close HEAD;
	$str = join "", @line;
	$idx = 0;
	for my $i (@dup) {
		$str .= "static const PMS_WORD DupWord_" . sprintf("%02d", $idx) . "[] = {\n";
		$str .= "\t";
		for my $j (@{$i}) {
			$str .= "$j,";
		}
		$str .= "\n";
		$str .= "};\n\n";
		$idx++;
	}
	$str .= "";
	$str .= "static const struct {\n";
	$str .= "    const PMS_WORD* data;\n";
	$str .= "    int   count;\n";
	$str .= "}DupWordTable[] = {\n";
	$idx = 0;
	for my $i (@dup) {
		$str .= "\t{ DupWord_" . sprintf("%02d", $idx) . ", " . scalar(@{$i}) . " },\n";
		$idx++;
	}
	$str .= "};\n\n";
	$str .= "//==========================================================\n";
	$str .= "// 関連定数\n";
	$str .= "//==========================================================\n\n";
	$str .= "#define  PMS_SRCFILE_MAX  (11)\n\n";
	$str .= "#undef __PMS_WORD_RES__\n";
	$str .= "#endif\n";

	util::updateAndSave(sprintf($DUP, $lang_mark), $str, "encoding(shiftjis)");
}

#===========================================================
# パスワードに使用するワードを出力。
#===========================================================
sub output_pwdlist {
	my ($pwd_list, $lang_mark) = @_;
	my ($str);

	for my $i (@{$pwd_list}) {
		$str .= "$i->{text}\n";
	}

	util::updateAndSave(sprintf($PWD, $lang_mark), $str, "utf8", "\x{ef}\x{bb}\x{bf}");
}

#===========================================================
# ジオネットのソート結果を出力。
#===========================================================
sub output_geonet {
	my ($sort_list, $except, $lang_mark) = @_;
	my ($str, $idx);

	$str .= "// 本ファイルはコンバータで出力しています。\n\n";
	$str .= "#ifndef\t__COUNTRY_PLACE_SORT_H__\n";
	$str .= "#define\t__COUNTRY_PLACE_SORT_H__\n\n";
	for my $i (@{$sort_list->{place}}) {
		$str .= "#include \"msgdata/msg_wifi_place_msg_$i->{name}.h\"\n";
	}
	$str .= "#include \"msgdata/msg_wifi_place_msg_world.h\"\n\n";
	$idx = 0;
	for my $i (@{$sort_list->{place}}) {
		$str .= "#define\tPLACE_NAME_SORT_NUM_${idx}\t(" .
			scalar(@{$i->{data}}) . ")\t\t// $i->{name}の実際の個数。\n\n";
		$str .= "// $i->{name}\n";
		$str .= "static const u8 PlaceNameSort${idx}[] = {\n";
		for my $j (@{$i->{data}}) {
			$str .= "\t$j->{def},\n";
		}
		$str .= "};\n\n";
		$idx++;
	}
	$str .= "#define\tCOUNTRY_NAME_SORT_NUM\t(" .
		(scalar(@{$sort_list->{country}}) - scalar(keys %{$except})) .
		")\t\t// 国名の実際の個数。\n\n";
	$str .= "static const u8 CountryNameSort[] = {\n";
	for my $i (@{$sort_list->{country}}) {
		if (!exists $except->{$i->{def}}) {
			$str .= "\t$i->{def},\n";
		}
	}
	for my $i (keys %{$except}) {
		$str .= "\tcountry000,\n";
	}
	$str .= "};\n\n";
	$str .= "#endif\n";

	util::updateAndSave(sprintf($GEO, $lang_mark), $str, "encoding(shiftjis)");
}

1;
