GPD WINとArchLinuxInstallBattle

今更な話な上に大した内容でもなかった感。(この記事、実は何ヶ月も前に書いてあったんだけど、どういう訳か下書きのまま放置されてたんだよね、それを今回公開しただけという…)

 

本体ストレージをいじらずにいきたいので、小型のUSBメモリを用意して、そこにインストールする事にする。

BIOSでなくUEFIな環境にインストールする事を念頭に作業を進めれば特に問題は無い。

 

以下、引っかかったところとかをピックアップ。

 

・Live環境の起動時にUSB error -110を吐いて死ぬ

最初の見た目の状況としてはUSBメモリのラベル名が正しく設定されてない時に近かったが、USBメモリを見失って起動できなくなっているという状況の方が正しかった。

よくよく見てみるとUSB error -110を吐いて死んでるっぽかった。

usb – device descriptor read/64, error -110 – Stack Overflow

によると、要は電力が足りてないらしい。

USB3.0ポートにUSB3.0メモリを刺してるんだから動いて当然っしょと思っていたけど、何らかの理由で、電力供給が不安定になっていたみたい。しょうがないのでUSB2.0の延長ケーブルを通して接続してみたら、問題無く認識するようになった。

延長ケーブルを使うのは持ち歩きに邪魔なので、USB2.0メモリを使う事にした。

 

・画面向きがおかしいやつ

ArchWikiの通りに。

インストール後の環境にも適用するために、てきとうに設定しておく。今回はブートローダにgrubを使ったので、/etc/default/grubのGRUB_CMDLINE_LINUXにi915.fastboot=1 fbcon=rotate:1を追加しておく。

設定の適用はgrub-mkconfig -o /boot/grub/grub.cfgみたいな感じで。

 

・インストールしてからwifi-menuがdialogとかwpa_supplicantを要求するのでwifi-menuが使えない

これはGPD WINつーかただ自分がアホなだけ

Live環境からてきとうに入れておきましょう

 

GPD WINはおもちゃとして面白いと思うんだけど、外でホイホイっと使うには、入力系統がしんどくてあんまり気が乗らないんですよね。そういう意味ではGPD Pocketの方が優秀なんだけど、両方なんて買うお金は無いし、GPD WINの頃にGPD Pocketなんて知らなかったし…という

php-mposのPROP計算を追いかける

初のAdvent Calendar参加です。1日遅れですが、暗号通貨Advent Calendar 5日目に登録しました。

お茶漬けと申します。普段は都内で学生やってます。研究進まない。

昨日4日目は@zinntikumugai氏の仮想通貨を変換した時の金額を求めるTwitterBOT作ったお話でした。JPYやBTCを挟まずに、目的の通貨へのレートを一発で教えてもらえるTwitterBOTだそうです。中間に基準通貨のようなものを挟まずともレートを教えてくれるというのは、便利で良さそうです。新たに対応通貨を増やす等、精力的に活動しているようで、今後が楽しみですね。

 

さて、プールマイニングを行った場合に、報酬はShareの比率に従って分配されるとかなんとか言いますが、その比率とは何を元に算出されるのか、きちんと考えたことが無かったので、具体的に理解する事が目標です。報酬の分配方式としては、代表的なものにPPLNSがあると思いますが、ここではPROPのみに絞ります(単純に私の技量と気力が足りなかったので)。また、実装を見させてもらうのは、MPOS/php-mposです。

基本的にはgithubのソースを眺めて追いかけていきますが、随時データベースの中身も利用します(採掘経験のある、mposデータベースが手元にあるので)。

ちなみに私は、きちんとPHPを触ったことがない上に、人のコードを読む経験が非常に少ないので、かなり手探りになります。ご了承ください。また、間違い等あったらご指摘頂けると大変嬉しく思います。

 

TL;DR

比率の計算は各ユーザの提出したShare達のDifficultyの総和を元に行う。おしまい。

 

以下はphp初心者が手探りで読んでいった痕跡です。

 

眺める起点

  • /cronjobs/run-crons.shをcronで回すので、それを見てみる
~略~
# List of cruns to execute
CRONS="findblock.php proportional_payout.php pplns_payout.php pps_payout.php blockupdate.php payouts.php tickerupdate.php notifications.php statistics.php tables_cleanup.php"
~略~
for cron in $CRONS; do
 [[ $VERBOSE == 1 ]] && echo "Running $cron, check logfile for details"
 $PHP_BIN $cron $PHP_OPTS
done
  • $CRONSの中にproportional_payout.phpとかいうドンピシャくさいものがある

 

proportional_payout.phpを読んでみる

  • creditで検索してみると、122行目にAdd new credit transactionという文言がある

  • そこの周辺はこんな感じ

// Add new credit transaction
if (!$transaction->addTransaction($aData['id'], $aData['payout'], 'Credit', $aBlock['id']))
 $log->logFatal('Failed to insert new Credit transaction to database for ' . $aData['username'] . ': ' . $transaction->getCronError());
  • $transaction->addTransaction()に失敗したらログに出力するよ」みたいに見える

  • addTransaction()の引数にはid, payout, Creditとかそれっぽいキーワードが並ぶ

  • addTransaction()の定義はどこに?$transactionの定義を探せば良い?

  • $transactionはこの行が初出なのでこのファイルには無さそう

  • transaction->addTransactionでリポジトリ検索してみると、include/classes/transaction.class.phpが見つかる

  • 5行目に$table = 'transactions'があって、クエリにはINERT INTO $this->table...とか書いてあるので、transactionsテーブルをいじるクラスだと想像される

  • 18行目、addTransactionの引数リストを見ると、account_id, amount, type, …と並んでいるので、上記の$aData['payout']は払い出しの分量っぽいことがわかる

  • では$aData['payout']どうやって決定される?

  • proportional_payout.phpの95行目付近にそれらしいものが見つかる

$aData['percentage'] = ( 100 / $iRoundShares ) * $aData['valid'];
$aData['payout'] = ( $aData['percentage'] / 100 ) * $dReward;
  • 100 / $iRoundShares$aData['valid']を掛け算して出てきたpercentagedRewardの積がpayoutになる、といったところ

  • 不明な変数は3つ、$iRoundShares, $aData['valid'], $dReward

 

$dRewardの記述を探す

  • proportional_payout.phpの71行目に以下の記述
$config['reward_type'] == 'block' ? $dReward = $aBlock['amount'] : $dReward = $config['reward'];

普通のifで書き直したのはこちら

if(config['reward_type'] == 'block'){
$dReward = $aBlock['amount']
}else{
$dReward = $config['reward'];
}
  • config['reward_type']include/config/global.inc.dist.php(実稼働環境ではglobal.inc.phpになるだろうが)の244行目に$config['reward_type'] = 'block'の記述あり

  • つまり$dRewardには$aBlock['amount']が入る

  • さて$aBlock['amount']はどこで決まる?→49行目のforeachで$aBlockが出て来る、その元は$aAllBlocks→35行目で$block->getAllUnaccounted('ASC');で得られている

 

block->getAllUnaccounted()を読む

  • その定義はinclude/classes/block.class.phpの96行目~

  • 97行目のクエリは「blocksテーブルからaccountedが0のレコードを取得してくる」もの

  • blocksテーブルに並んでいるのはプールで発見したブロックたちのように見える

  • データベースにてdesc blocksすると、accountedカラムのDefault値が0なことがわかる→値を設定せずINSERTされれば0になる

  • block.class.php内のINSERTを探すとaddBlock($block)が見つかる

  • クエリを眺めるといかにもブロック発見時の動作に見えるが、利用シーンを探して検証する

 

addBlock()が呼ばれているところを探してみる

  • リポジトリ内検索をするとcronjobs/findblock.phpに1つあるのみ(82行目で$aDataを引数に呼ばれている)

  • $aDataはどこで得られるか?→53行目のforeachで出て来る、その元は$aTransactions['transactions']

  • $aTransactionsは41行目の$bitcoin->listsinceblock($strLastBlockHash)で得られている

  • listsinceblockは置いておいて、$strLastBlockHashを先に見てみる

  • $strLastBlockHashは30行目で$aLastBlock['blockhash']が代入されている

  • $aLastBlockは29行目で$block->getLastValid()が代入されている

  • include/classes/block.class.phpの24行目~getLastValid()の定義がある

  • そこにあるクエリは「blocksテーブルからconfirmationsが-1よりも大きいレコードでheightが最も高いものを1つ取得してくる」もの(つまり直近に見つかったブロックを取得する)

  • …つまり$bitcoin->listsinceblock($strLastBlockHash)は直近に発見したブロックのblockhashを引数に使う事になる

  • listsinceblock()をリポジトリ内検索してみても、ヒットは無い

  • PHPには、存在しないメソッドを呼び出した場合の動作を決めるマジックメソッド__callがあるので、それを使っているのでは?

  • block.class.php内には__callは見当たらないので、requireしてるinclude/lib/jsonRPCClient.phpを見てみると83行目からそれらしい記述がある

  • coindに喋りかけてそうな感じに見えるが、それも確認してみる

  • パラメータの流れを見ると$urlあたりから全部来てる→__construct$urlが代入されている

  • $urlはコンストラクタの引数なので、それを利用するシーンを探す

  • bitcoin.class.phpの241行目からjsonRPCClientクラスを継承したBitcoinClientクラスの定義で、基底クラスのコンストラクタは278行目で実行される→その引数$uriは277行目で定義されるが、そのデータはBitcoinClientのコンストラクタの引数から与えられる

  • BitcoinClientをリポジトリ内検索すると、include/classes/bitcoinwrapper.class.phpで定義されるBitcoinWrapperが見つかる(これはBitcoinClientを継承するクラス)

  • bitcoinwrapper.class.phpの112行目でインスタンスの生成が行われており、そのパラメータ$config['wallet']['username']$config['wallet']['password']等はinclude/config/global.inc.dis.phpの70行目付近で設定されている

  • このパラメータはウォレットにアクセスする情報なので、やはりinclude/lib/jsonRPCClient.phpの83行目~はcoindに喋りかけているようだ

  • coindのコマンドとしてのlistsinceblockは「指定ブロック以降の(ウォレットに影響を与えた)トランザクションを取得」

  • つまり$aTransactionsは「直近に見つかったブロック以降の、ウォレットに影響を与えるトランザクション全て」になる

  • 要は、addBlockの引数は「直近に発見したブロック以降ウォレットに影響を与えたトランザクション」であって、blocksテーブルは発見したブロック群であると思われる

 

結局$dRewardは何が入るか

  • 発見したブロック群からUnaccountedなレコードのみを取り出してきたのが$aAllBlocksである(UnaccountedはMPOSが未だ処理していないという意味と思われる)

  • $aAllBlocksのうちの1つ…のamountが$dRewardに代入される

 

一旦休憩、まとめ……

  • そろそろ忘れてるのでまとめると、
$aData['percentage'] = ( 100 / $iRoundShares ) * $aData['valid']
$aData['payout'] = ( $aData['percentage'] / 100 ) * $dReward

 

  • で報酬が決められているのでは?と思う

  • $dRewardは「直近に発見したブロック以降で、ウォレットに影響を与えたトランザクションたち」のうちの1つ、のamountが入る

  • つまり$dRewardは「発見したブロックで得られた報酬の量」である

  • 残りの不明な変数は$iRoundShares, $aData['valid']の2つ

 

$aData['valid']には何が入っている?

  • $aDatacronjobs/proportional_payout.phpの79行目で$aAccountSharesから得られる事がわかる

  • その次のところにSkip users with only invalidsとあるので、恐らく各ユーザのValidなShareの量である

  • $aAccountSharesは69行目で$share->getSharesForAccounts($iPreviousShareId, $aBlock['share_id']);が代入されている

  • include/classes/share.class.phpの96行目~getSharesForAccountsの定義がある

  • そこにあるクエリは、

SELECT
 a.id,
 SUBSTRING_INDEX( s.username , '.', 1 ) as username,
 a.no_fees AS no_fees,
 IFNULL(SUM(IF(our_result='Y', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) AS valid,
 IFNULL(SUM(IF(our_result='N', IF(s.difficulty=0, POW(2, (" . $this->config['difficulty'] . " - 16)), s.difficulty), 0)), 0) AS invalid
 FROM $this->table AS s
 LEFT JOIN " . $this->user->getTableName() . " AS a
 ON a.username = SUBSTRING_INDEX( s.username , '.', 1 )
 WHERE s.id > ? AND s.id <= ? AND a.is_locked != 2
 GROUP BY username DESC

 

 

  • ややこしいのでFROM句から見ていく。それぞれ変数やメソッドから出力されたものに書き直すと以下のような状態
FROM shares AS s
LEFT JOIN accounts AS a
ON a.username = SUBSTRING_INDEX( s.username, '.', 1 )

 

 

  • sharesテーブルはユーザが提出したShareの記録、accountsテーブルはユーザ一覧

  • sharesにおけるusernameはユーザ名.ワーカ名であって、accountsにおけるusernameはユーザ名であることに注意すれば、accountsに存在するユーザが提出したShareたちを取得しようとしている事がわかる

  • WHERE句はs.id > $previous_upstream AND s.id <= $current_upstream AND a.is_locked !=2という事らしい

  • 最後はロックされているかのチェックぽいので良いとして、もう一つは(まとめて書くと)$previous_upstream < s.id <= $current_upstreamであるが、その変数は呼び出し元ではどう扱われているか?

  • proportional_payout.phpの67行目に$iPreviousShareId = @$aAllBlocks[$iIndex - 1]['share_id'] ? $aAllBlocks[$iIndex - 1]['share_id'] : 0;の記述がある

  • $iIndex$aAllBlocksをforeachしたときのインデックスである(49行目)

  • 発見したブロック群のうち、Unaccountedなものである$aAllBlocksのうち、1つ前のshare_idが前述の不等式において$previous_upstreamで使われる(share_idがfalseの場合には0が使われる)

  • $current_upstream$aBlock['share_id']が使われる(今処理しているブロックのshare_idが使われる)

  • つまり「1つ前のブロック発見~今のブロックまでに提出された範囲内に絞る」という意味になるようだ

  • 最後に、SELECTするカラムのうち3つは、a.id, SUBSTRING_INDEX( s.username, '.', 1 ) as username, a.no_fees AS no_feesで、大変簡単で、それぞれアカウントID(ユーザ名でなくデータベース上のID)、ユーザ名、feeの状況。

  • valid, invalidについては、適宜並べてみると、以下のようになる

IFNULL(
 SUM(
  IF(
   our_result='Y',
   IF(
    s.difficulty=0,
    POW(2, (" . $this->config['difficulty'] . " - 16)),
    s.difficulty
   ),
   0
  )
 ),
 0
)
 AS valid

 

  • まずSUMから読んでいくと、our_result='Y'の時に最深のIF()を、our_result='N'の時0を足し算していく

  • 最深のIF()は、各Shareレコードのdifficultyが0だったら2^($this->config['difficulty'] - 16)を、0でなかったらdificultyをそのまま返す($this->config['difficulty']include/config/global.inc.dist.phpの237行目で定義されている)

  • SUMがNULLだった場合には、0を返す

  • invalidではour_resultのY, Nが逆になっている

  • 最後にGROUP BY usernameがあるので、各ユーザごとにまとまるようだ

  • つまりは、各ユーザが提出したShareのDifficultyのそれぞれの総和を返すものと思えば良い

  • そしてshares.difficultyはワーカに割り振られたdifficultyの値らしい(SELECT difficulty FROM shares WHERE username='てきとうなワーカ名' ORDER BY id DESC LIMIT 1;で得られた値と、マイナー側に示されているDiffが一致しているので)

 

$iRoundSharesには何が入っている?

  • cronjobs/proportional_payout.phpの70行目で$share->getRoundShares($iPreviousShareId, $aBlock['share_id']);が代入されている

  • getRoundSharesという名前から察せられるが、当該ラウンドのShare量が返されているのであろう

  • クエリのgetSharesForAccountsとの差は、GROUP BY句が無い事、our_result='Y'以外は無視する事

 

不明だった変数をまとめると

  • $iRoundShares:当該ラウンドにおいて全ユーザが提出したSharesのDifficultyの総和

  • $aData['valid']:当該ラウンドにおいて各ユーザが提出したSharesのDifficultyの総和

  • $dReward:当該ブロックにてcoindが得た報酬

 

これらを踏まえて最初の式を見ると

$aData['percentage'] = ( 100 / $iRoundShares ) * $aData['valid']
$aData['payout'] = ( $aData['percentage'] / 100 ) * $dReward

 

  • てきとうに代入して書き換えてみると、
$aData['payout'] = ( $aData['valid'] / $iRoundShares ) * $dReward

 

  • つまり、ブロック報酬$dRewardをラウンド内のSharesの割合で分配する、その割合の計算元は、SharesのDifficultyの総和である

AndroidStudioいじってたらjava.lang.IllegalStateException aapt2 is missingとか言われた

環境はArchLinux

Android Studio本体はパッケージ管理でインストールしたんだけど、それが必要とするコンポーネント(つーかAndroid SDK)はパッケージ管理だったりAndroid StudioのSDK Managerだったりあやふやな状態でした。Android – ArchWikiにある通り、

Android Studio をインストールして IDE を使って SDK を管理する場合、以下のパッケージをインストールする必要はありません。

以下のパッケージってのは、android-platform, android-sdk, android-sdk-platform-tools, android-sdk-build-toolsの事。

大抵の手順指南サイトではSDK Managerを利用して管理するので、それに倣うなら、環境構築の時にパッケージ管理で上記パッケージたちをインストールするべきではなかったのだ(ArchWikiはちゃんと読みましょう)。

まぁそれはともかく、結局パッケージ管理で導入しちゃって、更新が競合したっぽくて、環境が壊れてしまった。それが原因で、開発したアプリケーションを実行するときに、Execution failedとかなんとか。java.lang.IllegalStateException aapt2 is missing的なエラーが出たのだと思われる。

aapt2が無いよ、と言っているんだけど本当に無いのかfindで検索すると、/opt/android-sdk/build-tools/27.0.0以下とかに見つかる。

Android Studio側のメッセージを見ると、26.0.2以下を探している雰囲気がしたので、それを入れなおしてみる。

具体的には、

SDK Manager→SDK Toolsのタブ→Show Package Detailsにチェックを入れる→Android SDK Build-Toolsの26.0.2が見つかるので、これのチェックを外してApply(一旦削除)→チェックを付けてApply(インストール)

みたいな感じでなんとかなった。一旦削除する意味があるかは微妙だけど、結局うまくいったのでまぁいいや。

 

なんかノリでAndroidアプリを作ってみてるんだけど(勿論Javaは何一つ勉強してないです)、なんかAndroidStudioの動作が大変鈍重だし(比較対象はQt Creator)、UIはちっちゃくて見えないし、中々厳しい世界だなと思いました。まる。

NASからうっかり消してしまったデータを復元する

保存していた録画データを操作ミスで消してしまった。あまりにもつらい。

まとめ。

・extundelete:exFATに対応していない

・testdisk:目的ファイルは復元されたっぽいがデータが完全に壊れている

・photorec:死ぬほど時間がかかるが復元される、ただし目的のファイルがどれかは不明(最終的に目的データは復元できなかった)

 

 

以下、雑多な記録。

 

extundeleteが良さげだったけど、exFATに対応していないようなので別の手を探すと、testdiskが見つかる。どうだろ?

取り敢えずインストールして実行してみる。十字キーとEnterキーで操作できる。

$ sudo pacman -S testdisk
$ sudo testdisk

起動するとまずログを保存するか聞かれるので、取り敢えずCreateを選んでおく。

次にディスク選択画面に入るので、目的のディスクを選ぶ。

Select a media (use Arrow keys, then press Enter):
>Disk /dev/sda - 128 GB / 119 GiB - PLEXTOR PX-128M5S
Disk /dev/sdb - 4000 GB / 3725 GiB - MARVELL Raid VD
Disk /dev/sdc - 2000 GB / 1863 GiB - WDC WD20EZRX-00D8PB0

NASとして使っているのは/dev/sdbなのでこれを選択。するとPartition TableのTypeを選ぶ段になるが、正解がどれだかよーわからん…

Please select the partition table type, press Enter when done.
[Intel ] Intel/PC partition
[EFI GPT] EFI GPT partition map (Mac i386, some x86_64...)
[Humax ] Humax partition table
[Mac ] Apple partition map
>[None ] Non partitioned media
[Sun ] Sun Solaris partition
[XBox ] XBox partition
[Return ] Return to disk selection

Noneが自動選択されていたので、そのまま進んでみると、なんかコマンドメニューみたいなのが表示されるので、

[ Analyse ] Analyse current partition structure and search for lost partitions
>[ Advanced ] Filesystem Utils
 [ Geometry ] Change disk geometry
 [ Options ] Modify options
 [ Quit ] Return to disk selection

Analyseする。

Analyseが終わると、Quick Searchが選べるようになるので、取り敢えずそれを選ぶ→Structure:Okの表記が出る

パーティションが正しく発見されたっぽい

Disk /dev/sdb - 4000 GB / 3725 GiB - CHS 486382 255 63
 Partition Start End Size in sectors
 >P exFAT 0 0 1 486382 59 13 7813730560

ので、Pを押してファイルリストに入って、そのまま目的のファイルを探す。

見つけたら画面の指示に従ってコピーすると復元されるはずだ…………

 

しかし、復元された動画データは、完全に壊れていた…(再生できなかった)

 

もうひとつ、testdiskと一緒に入ってきたPhotoRecというものがある。どうも、testdiskはファイルではなくディスクの破損を修復するのがメインの目的で、ファイルについてはPhotoRecを使えという事みたい。ArchWikiには「ファイルシステムは無視して素のデータを探し当てるので、再フォーマットされたり完全に破壊されたファイルシステムやパーティションテーブルでも動作」とのことで、強い(こなみ)

それから、どうやらこのソフト、復元できるファイルを手当たり次第に復元しまくるらしい。また、このソフトが知っているファイル形式に限って復元可能である…とは言え、File Formats Recovered By PhotoRec – CGSecurityによれば大抵のファイルは問題なく復元可能である。中にはNintendo DSのゲームイメージであるnds形式なんかもあるし、結構強そう。

しかし、4TBのボリュームを選択しているからか、FilesystemAnalysisで異常に時間がかかった。ファイルが存在しない領域のみにしぼって復元するはずだが、結局5~10時間くらいかかっていたっぽい。

その後はドカドカ復元されていくので、保存先容量がいっぱいいっぱいにならないように順次チェック。

というのも、復元されたものはファイル名が無いので一つ一つ再生してみて確認しないといけないのだ。結構めんどい。

再生可能ファイルと再生不可能ファイルがそれぞれあったが、多分普通にデータが壊れている。空き容量に対して復元されたデータ量が3倍くらいあったので、一部分しか復元できなかったんだろう(ファイルサイズはそれらしいサイズだったので、重複したゴミデータでも含みまくっているのだろうか、と)

傾向としては、ファイルサイズが500MB程度までのもの(小さすぎるものは除外)だと再生可能な事が多かったように感じる。GBのオーダーまでいくと大抵の場合再生できなかった。断片化とかしてるともうダメなんだろうか?ファイルシステムとか全然勉強してないのでよーわからん。

 

取り敢えず、ファイルの復元手段を得たのは良いが、目的のファイルは復元できなかった。testdiskの時点で復元したものが再生不可だったので、そもそも復元不可だったのかもしれない。残念。

AndroidStudioの初期セットアップが容量不足で死ぬ

環境はArch Linux(いつもの通りAntergos)

素直に諸々チェックして進んでいくと、Android APIとAVDのところで死亡する。原因は容量不足。

見ると/tmpが完全に食いつぶされてしまっている。AntergosのインストーラはRAMの半分を/tmpとして用意するので、8GBのRAMを積んでいる今の環境では4GBが/tmpに割り当てられる。

 

やってみると「容量不足で死んだわ」しか言ってくれないので、一挙にやらずに少しずつやろうという事で、まずAndroid APIだけインストールしてみると、これはうまくいく。

残りのAVDは?

これがダメ。Download→unzipの、unzipに入った途端に、瞬間的にRAMの使用量が4GBくらい増える(/tmpが瞬間的に食いつぶされる)

で、これが一杯一杯になって死亡してるっぽい。Component Installerのテンポラリディレクトリを変更できれば良さそうなんだけど、Component Installerには設定画面が見当たらないし、Android Studioの設定見ても全然それらしいのが無いし、自力じゃどうしようもないと結論をつけ、Google先生に聞いてみる。

 

ぐぐったらこちらを発見。

I keep running out of tmpfs when trying to install Android SDK component – Ask Fedora: Community Knowledge Base and Support Forum

tmpfsをそもそも無効にするか、Android Studioのテンポラリディレクトリを変更すれば良いのでは?ってレスがついてるけど、その下に、具体的にどうすれば良いかが書いてあった。

export _JAVA_OPTIONS=-Djava.io.tmpdir=/var/tmp

よーわからんが、これAndroid Studioの設定じゃなくて、Javaの設定なのでは?という気持ちになる

なんでAndroid Studio側で設定が無いんだろうね

 

空腹がしんどい

PDFからPNGへ変換

スキャナで生成されたPDFデータをPNGに変換したいと思った時。1ページ1ページを分離してそれぞれ1つの画像ファイルとして保存したい。

 

1.ImageMagickで

$ convert hoge.pdf hoge.png

一番単純。これは画質がガタ落ちになる。densityオプションで画質向上できるけど、上げれば上げるだけどんどんRAMを必要とする。高画質な状態で作業したい時には不向き。density上げすぎると死亡する。

$ convert -density 600 hoge.pdf hoge.png
zsh: abort (core dumped) convert -density 600 hoge.pdf hoge.png

 

2.pdfimagesで

$ pdfimages -png hoge.pdf hoge

結構時間かかるが画質を落とさず取り出せる。

ただしスキャナの設定によって、理想通りの動作にならないことも。pdfimagesはPDFに画像データが貼り付けられていたらそれを取り出す、という動作のため、スキャンデータが1枚の画像として取り扱われる場合には(ただの画像データを貼り付けただけのページが並ぶため)PDF→PNG変換のように見える。しかし一部を文字として認識したりすると、各ページは複数のパーツ画像と文字、のようなデータになるため、pdfimagesを使うと、そこにあるパーツ画像のみが取り出される。

 

3.xpdfで

xpdfはpdftopngというそのものズバリなコマンドを持っている。xpdfからフォークしたPopplerではpdftoppmがあるので、これで一度ppmで書き出してからpngにしても良いかも。

$ pdftopng -r 600 hoge.pdf hoge

画質を落としたくないという事でDPIを600に設定してみたが、サクサク処理が進んでいく。軽い。今まで知らなかったけどめちゃくちゃ良いじゃん。

 

まとめ

convert→何でもおk、ファイル種別も自動判別のお手軽仕様だが、今回の用途では画質に拘ると死ぬ

pdfimages→割と手軽に使えるが、PDFデータの状態によっては理想通りの動作にはならない

pdftopng(Popplerならpdftoppm)→今の所マズイところが見当たらない

 

いつか読もうと思ってた本だけどレビューはそんな評価高くなくて微妙な気持ちになってる(ならなぜこれのリンクを張るのだといのは置いといて)

githubにAPIアクセスキーとかをpushすると警告メールを飛ばしてくれるGitGuardian

手持ちの本を管理するプログラムが欲しくて、作ることにした。

以前ヤフオクで落としたバーコードリーダーOPI-2201をつかって、背表紙にあるISBNのバーコードを読み取って、あとはこれをAmazonのProductAdvertising API使って書籍情報を取得してくる…みたいなのを作っていたんだけど、PA-APIアクセスのキーとかをソースコードにベタ打ちしたcommitをgithubにpushしたら、GitGuardianとやらから警告メールが飛んできた。

 

メールの日付を見るに、pushから数十秒くらいで送信されているみたい。すごい早いじゃーんと思ってGitGuardianのページを眺めに行くと、どうやら人工知能に興味がある人が趣味的にやってるサイトらしくて、機械学習で自動判別しているらしい。

毎日200万くらいあるPublicなcommitをひたすらチェックしていってるみたいで、 平均すると2時間あたり3個くらいのペースでSensitiveなcommitがあるらしい。ぼくもそのうちの一つであったのだ…

Good Guyのありがたい通知に従って、今はもうこのAPIキーは無効にして、configから読み取るようにしたけれど、できればお世話になるようなヘマはしたくないなーなどと。そういうサンプルとそのフィードバックがあるからもっと精度も良くなっていくんでしょうけどね。

 

Windows10でいくつかのアプリケーションが落ちる

例えば、

・まめFile5SE

・桜時計

・Exact Audio Copy

・StepMania

あたり。

まめFile5SEはぶっちゃけただのファイラーだしExplorerで代用できる(というか本来はExplorerの代用として使う立場なんだけど、Explorerあんま使い勝手良くないので)し、桜時計もWindows標準の時刻合わせに頼る事にすれば(数分くらいのズレを許容すれば(許容できないでしょ))使わなくても良い。ただ、EACは代替ソフトがあんま見つからなかったし、StepManiaなんかゲームなんだし代替ソフトもクソもねーよ。

いつからこうなったのか記憶がはっきりしないけど、OSバージョンはWin10 Pro 1703。

まめFileでは起動後タブリストが復元されて、ファイルリストが表示された直後に「動作を停止しました」エラー。

桜時計はタスクトレイにいる間は何も言わないんだけど、ウィンドウを開くと「動作を停止しました」エラー。

EACはCDの読み込みまでは良くて、リッピングを開始しようと(Alt+F7)すると「動作を停止しました」エラー。

StepManiaはそもそも起動もしてくれなくて、「エラー起こったよ!詳しくはログ見てね!」なメッセージが表示されるのみ。ログ眺めると「なんか知らんがデータ無かったわwwwww」みたいな感じ。

 

最初はHyperThreadingのエラーかなと思って(使用CPUはIntel Core i7 6700KなのでSkylakeだ)、HTTをオフにしてみたりもしたけど状況変わらず。使用マザーボードはASRock Z170 Gaming K4だけどマイクロコードアップデート降ってないかなと思ったら、7.10が最新(既に適用済み)、ベータ版の7.12ではHTTについてのマイクロコードアップデートが入ってるらしいが、ベータ版を迂闊に適用できるほど人柱ではないので敬遠。

 

もうちょっとちゃんと調べようと思って、イベントビューアを見てみる事にした。EACを起動して落ちたタイミングのイベントを見てみると、エラー情報が。

障害が発生しているアプリケーション名はEAC.exeで良いんだけど、障害が発生しているモジュール名にMacType.dllの文字…

 

取り敢えずMacTypeを無効にしてみると、今まで落ちまくってたアプリケーションが全く問題なく動作するように。

フォントレンダリングの汚さがWindows10でも改善されてなかったから、Windowsインストール直後にMacTypeを入れていたんだけど、それが悪さをしていたみたい。

最近のWindows Updateで降ってきた更新とMacTypeが競合するような内容だったのかもしれない。

 

 

最近ノートPCがサイズ変更無しに1366×768から1920×1080に変わった(どちらも13インチモデルなので寸法はあまり変わってないはず)お陰で画素密度が向上して、文字が随分見やすくなった。というわけで、23インチとか21インチの面積にFullHDを放り込む状況、いい加減やめにして、もうちょっと高解像度な世界で生きていけば、文字もなんとなく見やすくなるんじゃなかろか、という気持ちだ。

4Kモニタ買うなら40インチ前後にして、外形寸法あんま変わらないようにしようと思ってたけど、30インチ切りとかでも綺麗さで言えば向上するはずだから、そういう手もあるんだよな、と思ったりした。4Kモニタでも小さいサイズなら手を伸ばしやすい価格帯になるし。

この辺ワンチャンありそうじゃない?まぁお金無いけど。

シェルで直前の入力を使う

なんか使いたくなったときはいつも忘れてるのでメモ。

まず、直前に入力した引数を使いまわすには!$を使う。

$ mkdir hoge
$ cd !$

直前に入力したコマンドそのものを使いまわすには!!を。

$ pacman -Syu
$ sudo !!

!!が直前の入力に置き換わるような感じなので、次のような使い方もできる。

$ sudo eject
$ !! -t

ejectコマンドは-t, –traycloseをつけると光学ドライブのトレイを閉じてくれる。

Linuxで音楽CDをリッピング

今メインに使ってるWindows PCにディスクドライブが装備されていないので、リッピング部分のみLinuxマシンにやらせて、あとの変換とか再生行程は従来どおりにやりたい的な感じ。

当初/dev/sr0からddで引っ張り出せばいいだろと思ってたけどダメらしい。てきとうにぐぐった結果できそうだったのでやってみる。

 

CDを挿入後lsblkにエントリが追加される

$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 119.2G 0 disk
├─sda1 8:1 0 200M 0 part /boot/efi
├─sda2 8:2 0 256M 0 part /boot
├─sda3 8:3 0 111G 0 part /
└─sda4 8:4 0 7.8G 0 part [SWAP]
sdb 8:16 0 3.7T 0 disk /mnt/r_array
sdc 8:32 0 1.8T 0 disk
sr0 11:0 1 324.2M 0 rom

ケツにあるsr0がそうだ。cdrdaoでリッピング。

$ sudo cdrdao read-cd --device /dev/sr0 --read-raw --datafile ZMCZ-11306.{bin,toc}
Cdrdao version 1.2.3 - (C) Andreas Mueller <andreas@daneb.de>
/dev/sr0: HL-DT-ST DVDRAM GH24NS50      Rev: XP01
Using driver: Generic SCSI-3/MMC - Version 2.0 (options 0x0000)

Reading toc and track data...
~以下略

tocファイルをcueシートに変換。

$ toc2cue ZMCZ-11306.{toc,cue}

できあがったbinファイルはエンディアンが異なるっぽいのでddで変換する。このまま再生するとホワイトノイズにしかならない。

$ dd conv=swab if=./ZMCZ-11306.bin of=./ZMCZ-11306.bin.swap

以上でリッピングは終了。

 

ここまででbin+cueな状態にできたので、完了ということで。あとはてきとうに、煮るなり焼くなり。

 

ちなみに上記で使っていたZMCZ-11306はメイドインアビスのED。

あとOPも買ったよ

原作コミックスも全部買った。最高に心がきゅうっとするのでおすすめです(?)

ひどい広告記事になってしまった…