目的
- あつまれどうぶつの森の、スマホアプリ「タヌポータル」で読み込めるQRコードを作りたい
- んでそれを、ドラクエ10の顔アイコンWebサイトに組み込みたい
- まあ、比較的邪悪!
- 実装は土日の2日かな?、なんとなく、Qiitaにやりくち出してみたり
成果物は、ここにコッソリ実装してありますよん。
http://dqxmosaic.eucaly.net/app/faceicon
おおざっぱなやりざま
もともとゲーム用のテクスチャそのままなので、結構制約が厳しいです。
- 32 x 32
- 15色
- パレットは固定
- パレットの色IDは固定
- パレットの並びに法則性はあるらしいがよーわからん
Numpyなりなんなり使って、ゴリゴリ計算してもいいのですが、オイラは数学赤点気味なのでパース!。
ImageMagickでなんとか誤魔化す方向で、がんばってみました。
オイラは古い人間なので、perlなCGIで
- 色変換用パレット一式の作成
- 対応画像の作成
- パレットID変換
- QRコード用データの作成
- QRコード生成 ( ここだけpython )
な感じで!
色変換用パレット一式の作成
偉い人が作ってくれたカラーパレットをまず読み込みます、と。
https://docs.google.com/spreadsheets/d/1KD1pGXKMuglCX6M3mgvcaeEevQAsTTO2DgSdsuidMg4/edit#gid=0
まあ、ここから、カラーコードを出して、こんなテキストファイルを作りますよと。
全部貼るのもアレなんで、上のほうだけね。
#ffefff
#ff9aad
#ef559c
#ff65ad
#ff0063
#bd4573
#ce0052
#9c0031
#522031
#ffbace
んでこれを、まあ適当に書いたperlでHTMLテーブルへ展開。
$filename = "./palette.txt";
open (FILE,$filename);
(@array) = (<FILE>);
close (FILE);
print "<html><body><table border=0 cellpadding=0 cellspacing=0>";
$count = 0;
foreach $line (@array){
if ($line !~ /#/){next;}
$line =~ s/\W//g;
if ($count == 0){
print "<tr cellpadding=0 cellspacing=0>";
}
print "<td bgcolor=\"#".$line."\" width=8 height=8 cellpadding=0 cellspacing=0> </td>";
$count++;
if ($count == 8){
print "</tr>";
$count = 0;
}
}
print "</tr></table></body></html>";
で、出てきたHTMLを、chromeなりに読み込ませて、適当な画像ソフトでスクリーンショットを切り出して保存と。
ま、こんなデータになりますよ。
対応画像の作成
さて、Imagemagickパート。
やりくちは・・・。
- 背景用画像を合成
- 画像をトリミング
- 「最大」32x32にリサイズ (縦横比くずれる)
- 32x32の画像を背景色で生成、元ファイルを中央に合成
- カラーパレットを基に15色へ減色
- カラーパレットを基にディザ掛けながらtrueカラーへ増色、BMPで保存
な感じで。
perlで書くと、こんな感じ。
まあ、shとかにも簡単に落とし込めるかと。
$paletteimagefilename = "./palette.png";
$inputfilename = "./input.png";
$temppngfilename = "/ramdisk/temp.png";
$tempbmpfilename = "/ramdisk/output.bmp";
$systemcommand = "montage -background $bgcolor $inputfilename $temppngfilename";
system ($systemcommand);
$systemcommand = "mogrify -fuzz 0% -trim +repage $temppngfilename";
system ($systemcommand);
$systemcommand = "mogrify -resize 32x32 $temppngfilename";
system ($systemcommand);
$systemcommand = "convert -size 32x32 xc:$bgcolor $temppngfilename -gravity center -compose over -composite $temppngfilename";
system ($systemcommand);
$systemcommand = "mogrify -map $paletteimagefilename -colors 15 $temppngfilename";
system ($systemcommand);
$systemcommand = "convert $temppngfilename +dither -map $paletteimagefilename -type truecolor $tempbmpfilename";
system ($systemcommand);
paletteimagefilenameは、前に作ったパレット画像ね。
パレットID変換
作成した画像の色データを、色ごとに割り当てられたIDへ変換しないとダメなんです。
ま、がんばりましょ。
カラーパレット画像んとこでも使ったスプレッドシートから、今度はNoとColorCode列を取り出します。
カラーコードの色を、Noに変換、て感じの処理っすね。
0 #ffefff
1 #ff9aad
2 #ef559c
3 #ff65ad
4 #ff0063
5 #bd4573
6 #ce0052
7 #9c0031
んでまあ、処理するファイルはBMPファイルなので、バイナリのRGBな並びが違います。
BGRになっちゃってます。
なのでまあ、並び替え。
$filename = "./palettecode.txt";
open (FILE,$filename);
(@array) = (<FILE>);
close (FILE);
$count = 0;
foreach $line (@array){
if ($line !~ /#/){next;}
($code,$palette) = split(/\t/,$line);
lc ($palette);
if ($palette =~ /(\w\w)(\w\w)(\w\w)/){
$bitmappalette = $3 . $2 . $1;
print "$code\t$bitmappalette\t\n";
}
}
できたー
0 ffefff
1 ad9aff
2 9c55ef
3 ad65ff
4 6300ff
5 7345bd
6 5200ce
7 31009c
QRコード用データの作成
さてと、前提となるデータは揃いました。
あとは、ゴリゴリデータを作っていくだけです。
えーと、作るデータのカタマリは、以下の2つ
- 使用するパレットID ( 15個、15バイト )、0からEに対応
- 画像データ、0からEの1HEXづつ、ベタなバイナリデータで
画像データの並びは、左上→右下な感じです。
perl的な実装としては、こんな感じ。
- BMP用のパレットファイルを読み込み、連想配列へ展開
- BMP画像をバイナリとして読み込み
- パレットID保存用配列、及びパレット用配列を準備
- BMPバイナリを、276バイト目から16進HEXデータへ変換
- BMPデータは画像の下から上にデータが入っているので、適宜並び替えつつパレットID変換し、画像HEXデータ作成
- パレットIDのHEXデータ作成
- ヘッダくっつけて、バイナリに変換して保存
まあ、連想配列に突っ込んだ6バイトのGBRデータを、そのまま変換に使ってるあたりが手抜きポイントかな?。
これで出来るファイルを、「.acnl」ファイルと言うらしいです。
どうぶつの森の海外名称、「Animal Crossing: New Leaf」からつけられてるっぽい。
ファイルの構造は、以下参照!。
https://programresource.net/2013/05/04/2177.html
んで、ヘッダは思いっきりHEXで書いてますが、ココに罠が潜んでいます。
ヘッダの中に、「ユニークID」という要素があり、「作者」「村」にそれぞれIDがあるみたい。
まあこれで自作か他から持ってきたのかを識別してるらしいのですが。
3DS用の「とびだせどうぶつの森」では、ユニークIDが「00」でOKだったのですが。
タヌポータルでは、ここに意味のある文字列を求めます。
00だと、「読み込めるけどSwitchに転送できない」QRコードが爆誕してしまいます、ぶう。
$palettefilename = "./palettecodebitmap.txt";
$tempbmpfilename = "/ramdisk/output.bmp";
open (FILE,$palettefilename);
(@palettearray) = (<FILE>);
close (FILE);
undef (%palettecode);
foreach $paletteline (@palettearray) {
($paletteid,$palettedata,$temp) = split(/\t/,$paletteline);
if ($paletteid ne ""){
$palettecode{$palettedata} = $paletteid;
}
}
open (FILE,$tempbmpfilename);
$/ = "";
binmode (FILE);
$bitmapraw = <FILE>;
close (FILE);
$/ = "\n";
(@palettenumberarray) = ("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e");
undef (@paletteselectarray);
$palettecount = 0;
$endiancount = 0;
$bitmapdigit = substr(unpack("H*",$bitmapraw),276);
$bitmaphex = "";
for $i (0..31){
$ri = 31 - $i;
for $j (0..31){
$k = $ri * ( 32 * 6 ) + ( $j * 6 );
if ($endiancount == 0){
$k = $k + 6;
$endiancount = 1;
}
else {
$k = $k - 6;
$endiancount = 0;
}
$targetdot = substr($bitmapdigit,$k,6);
$palettesel = $palettecode{$targetdot};
if (length($palettesel) == 1){
$bitmaphex .= $palettesel;
next;
}
$paletteselectarray[$palettecount] = $palettesel;
$palettecode{$targetdot} = $palettenumberarray[$palettecount];
$palettecount++;
if ($palettecount > 15){
exit;
}
$bitmaphex .= $palettecode{$targetdot};
}
}
$palettehex = "";
for $i (0..14){
if ($paletteselectarray[$i] eq ""){
$palettehex .= "ef";
}
else {
$palettehex .= $paletteselectarray[$i];
}
}
$qrhex = "6400710078006d006f007300610069006300000000000000000000000000000000000000000000000000B6EC65007500630061006c007900000000000000000044C54100730074006f006c00740069006100000000001931" . $palettehex . "cc0a090000" . $bitmaphex;
$qrbin = pack("H*", $qrhex);
open (OUTPUT,">$outputfilename");
binmode (OUTPUT);
print OUTPUT $qrbin;
close (OUTPUT);
QRコード生成
QRコードは、エラー対応レベルだのバージョンだのありますが。
デフォルトからずらすと、認識されなくなっちゃいます。
なのでまあ、手抜きなこんなコードに。
import sys
import qrcode
infile = open(sys.argv[1], 'r')
data = infile.read()
img = qrcode.make(data)
img.save(sys.argv[2])
ま、これをperl側からsystemでkickしてますわな。
ぶじできたー
やったー!。
そして先人のみなさま、ありがとうございます!。
以下を参考にしました、割とゴリゴリバイナリ解析とかしたりしましたとさ。
https://programresource.net/2013/05/04/2177.html
https://docs.google.com/spreadsheets/d/1KD1pGXKMuglCX6M3mgvcaeEevQAsTTO2DgSdsuidMg4/edit#gid=0
https://acpatterns.com/editor
・・・まあオイラ、元々のQRコードが作れる、「とびだせどうぶつの森」、持ってないんですけどね・・・。
おしまい。