Javaで使用するファイルを保存するディレクトリどこに置きますか?
Javaで作ったアプリケーションを公開しようと考えていたのですが、アプリケーションで読み書きするファイルをどこに保存したらいいのか悩んでしまいました。
読むだけの場合はJarファイルに一緒に固めてしまえばいいのですが、書き込みも行う場合はそうもいきません。
このようなファイルの保存先を選択するには大きく分けて
- アプリケーションが勝手に決める
- ユーザに決めさせる
の二つがあります。
1. アプリケーションが保存先を勝手に決める
Jarファイルと同じディレクトリでもいいのですが、そこがProgram FilesなどだとWindows Vista以降ではUACのせいで色々と面倒になります。
ここで挙げるのはホームディレクトリのパスを取得し、そこにOSごとの保存先パスを付け足してやる方法です。
String homePath = System.getProperty("user.home"); String osName = System.getProperty("os.name"); String subPath = ""; if (osName.contains("Windows")) { if (osName.contains("Vista") || osName.contains("7")) { subPath = "Documents"; } else { subPath = "My Documents"; } } else if (osName.contains("Mac OS X")) { subPath = "Library/Preferences"; } java.io.File prefDirParent = new java.io.File(homePath, subPath); java.io.File prefDir = new java.io.File(prefDirParent, "ProjectName");
OSごとの設定が必要にはなりますが、ユーザに手間をかけさせずに決めることができます。
2. ユーザに保存先を決めさせる
アプリケーションが勝手に決めるのではなくどこそこに保存することをユーザに操作させる方法です。
特に巨大なファイルを操作するときに別のディスクに保存しておきたいというユーザの希望にも応えることができます。
javax.swing.JFileChooser dirChooser = new javax.swing.JFileChooser("."); dirChooser.setDialogTitle("保存先を指定してください"); dirChooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY); int ret = dirChooser.showOpenDialog(null); if (ret != javax.swing.JFileChooser.APPROVE_OPTION) return; prefDir = dirChooser.getSelectedFile();
初期起動時に決めさせるかインストーラで管理者権限のあるうちにやってしまうかなどいくつか方法があります。ちなみに「ユーザが決めた設定(ここでは保存先のパス)をどこに保存するか?」という疑問に対しては"java.util.prefs.Preferences"を使うのがいいと思います。Windowsの場合レジストリを使用するようなのでそれが嫌な場合には使えませんが:(
まとめ
- 読み込みしかしないのであればJarに入れてClass#getResourceAsStream
- 自分しか使わないのであれば固定パス(orカレントディレクトリ)
- 文字列しか設定しないのであればPreferences
- それ以外は上記の方法くらいしか思いつきません。
- もっと上手な方法があればコメントにて教えてくださいませ
D言語からFTGLでも使ってみるデモ
先日OpenGLからフォントを使えるライブラリFTGLをビルドしてそのまま放置していたのですが、重い腰を上げてD言語から使ってみました。
FreeType関係の定義をD言語で書き直すのが面倒なので、まずはC言語用のデモを動かしてみるところまでやってみました。
用意するもの
glut32.lib, ftgl.libの変換
コンパイラにdmdを使う場合、今回使用するglut32.libとftgl.libをdmd用に変換しないと使えません。
変換方法はやねうらおさんのサイトを参考にBorland C++ Compilerを使えば無料で変換できます。
ダウンロード先は今確認したところembarcaderoならインストーラを登録なしでダウンロードできるようです。
implib(bccなら"$(BCC)/Bin/implib.exe")を用意できたら、
$ implib -c -a glut32.lib glut32.dll
のようにglut32.libとftgl.libを変換したものをdmdのlibフォルダ("$(DMD)/lib")に入れておきましょう。
D言語に変換(写経とも)
c-demo.cがincludeしているヘッダファイルを見ながら手直しします。
私が書いたソースコードはhttp://yukkepc.web.fc2.com/src/ftgl-demo.dに上げておきます*2。
c-demo.cとの違いは次の通りです。
- ftglCreateCustomFontは面倒だったので省略しました。
- 終了の仕方が分からなかったので例外を投げています。
- フォントをMSゴシックにして"こんにちは FTGL!"と表示します。
WindowsでFTGLを使ってOpenGLで日本語フォントを表示する
非力な環境では遅いらしいSDL_ttfの代用としてOpenGLでTrueTypeフォントを FTGL というライブラリで扱えるかどうかを試してみます。
今回使用する環境
全て無料で手に入る物で揃えてみました。
Visual C++ 2008 Express Edition (SP1) のインストール
VS2003以降ならコンパイルできるようですが、ここでは無料で手に入るVC2008 SP1を使います。Visual Studioをインストールしていない人はVisual Studio 2008 Express Editionからインストールしておきましょう。
名称が長いので以下ではVisual C++ 2008 Express EditionをVCと省略することにします。
FreeType のダウンロード
SourceForgeのFreeTypeダウンロードページからft***.zipをもらってきて展開します。私が取得したときの最新はft239.zipでした。
次にFTGLのビルドに必要なので展開したディレクトリのパスを環境変数 システム -> システムの詳細設定 -> 環境変数">*1"FREETYPE"に記述します。私は"C:\Library\"以下にまとめて置くことにします。
FreeType のビルド
VCから"$(FREETYPE)/builds/win32/vc2008/freetype.sln"を開きます*2。
ターゲットを"LIB RELEASE"にしてソリューションのビルドを行います(必要なら"LIB DEBUG"でもビルドします)。
うまくビルドできると"$(FREETYPE)/objs/win32/vc2008"に"freetype***.lib"ができている*3ので、二階層上の"$(FREETYPE)/objs"にコピーしておきます。
FTGL のビルド
SourceForgeのFTGLダウンロードページからftgl-*.tar.gzをもらってきて展開します。私が取得したときの最新はftgl-2.1.3-rc5.tar.gzでした。
以下では展開したディレクトリパスを$(FTGL)と記述することにします。環境変数にする必要はありません。
VCから"$(FTGL)/msvc/vc8/ftgl.sln"を開きます。ソリューションファイルの変換をウィザードに沿って行います。
プロジェクトftgl_dllのプロパティを開き、構成プロパティ→リンカ→入力→追加の依存ファイルの中の"freetype234.lib"を先ほど作成したファイル名に書き換えます。
準備が整ったのでftgl_dllのビルドを行います。成功すれば"$(FTGL)/msvc/Build"にftgl.dll, ftgl.libができているはずです。
GLUT のダウンロード
下記のデモで使うためにGLUT (OpenGL Utility Toolkit)を用意しておきます。GLUT はビルド済みファイルが公開されているので、GLUT for Windowsからバイナリをダウンロードして展開します。
README-win32.txtにしたがい、glut32.dllを"/Windows/System"に、glut32.libを"(MSVC9のインストール先)/lib"に、glut.hを"((MSVC9のインストール先)/include/GL"に置きます。
FTGL のデモをビルド
ここまでうまくいっていることを確かめるために FTGL についているデモを実行してみます。
VC から"$(FTGL)/msvc/vc8/ftgl_demo.sln"を開きます。
プロジェクトFTGLDemo のプロパティを開き、構成プロパティ→リンカ→全般→追加のライブラリ ディレクトリに"$(FREETYPE)/objs"を追加します。
FTGLDemo.cppを開き、
# define FONT_FILE "C:\\WINNT\\Fonts\\arial.ttf"
このファイルパスを使用したいフォントのパスに書き換えてビルドします。
FTGL のデモで日本語文字を表示する
上記のデモの文章を修正して適当な日本語の文章(後半はWikipediaのOpenGLのページからのコピー)をMSゴシックで表示させてみました。
プログラムをバイナリエディタで開いてみると、コンパイルされた状態で既に文字化けしているのでVC++側の問題なのだと思います。試しにソースコード中の文字化けした文字列をエスケープ文字列にしたらちゃんと表示されます。
ソースコードがエスケープ文字列だらけになってしまうのが困りものですが:-(
この問題を検索してみると、VC++.NET と UTF-8 -C++ で書いたソースコードに UTF-8 エンコーディン- C言語・C++・C# | 教えて!gooのようにUTF-8のファイルを使うとおかしくなるようです。OSのロケールを変えなければならない、って単なるIDE(or コンパイラ)のバグなのでは??
D言語からは使えないのが困った
FTGLはC++言語で記述されているため、DLLを変更せずに直接使うことはできません(参考:C++とのインターフェイス)。ということで私はFTGLは使わないことにします。ここまで書いておいてひどいオチだ;-)
(追記) FTGL付随のデモを見てみたところ、C言語インターフェイスも用意されているようです。早とちりしてごめんなさい。
(さらに追記) D言語から使ってみました。
Intel MacでSDLを使ったプログラムをDMDでコンパイルしapp形式にする
Mac OS X 上でSDLを使ったプログラムをDMDでコンパイルしてもそのままでは起動できなくて苦労しました。
試しに以前作成したソフトウェアをMac OS X 10.4 Intel上で使えるようにしてみたのでその記録を残しておきたいと思います。
1. はじめに
ビルドに使用した環境は次の通りです。
2. SDL.frameworkを使ったビルド
の三つの方法があります。
ここでは作成したプログラムを配布する時のことを考えまして、三番目の「フレームワークを使う」方法をとることにします。
dmdでリンクするときには、リンカオプションを"-Lリンカオプション"のようにする必要があります。たとえばSDL.frameworkを使ってリンクするときには次のようになります。
$ dmd -L-framework -LSDL obj1.o obj2.o -ofprogram
otoolを使って使用しているライブラリを調べると、私のプログラムではSDL, SDL_image, SDL_mixerを使っていることが確認できます。
$ otool -L program program: @executable_path/../Frameworks/SDL.framework/Versions/A/SDL (compatibility version 1.0.0, current version 1.0.0) @executable_path/../Frameworks/SDL_mixer.framework/Versions/A/SDL_mixer (compatibility version 1.0.0, current version 1.0.0) @executable_path/../Frameworks/SDL_image.framework/Versions/A/SDL_image (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.3.11)
フレームワークはコンピュータ全体にインストールしてもいいですし、自分だけが使えるようにインストールしてもかまいません。
前者は"/Library/Frameworks/", 後者は"$(HOME)/Library/Frameworks/"にSDL.frameworkを配置すれば(自分の環境では)使用することができます。
3. sdlbootを使って起動する
さてビルドに成功したプログラムを実行してみると、次のようなエラーが出て起動できません。
$ ./program ****-**-** **:**:**.*** program[593] *** _NSAutoreleaseNoPool(): Object 0x******** of class NSLock autoreleased with no pool in place - just leaking (中略) ****-**-** **:**:**.*** program[***] *** Uncaught exception: <NSInternalInconsistencyException> Error (1002) creating CGSWindow Trace/BPT trap
SDLを使ったプログラムをMac OS X用にビルドするとCocoaの初期化フェーズを飛ばしてしまうのが原因のようです。詳しくはsdlbootのページを見てください。
main_hook.tgzを上記リンク先からダウンロードし展開します。
main_hook/sdlboot内のビルド済みファイルはFinkのSDLパッケージを使っているようなので、フレームワークを使ってビルドし直します。
私が修正したMakefileの内容を記述しておきます。
all: sdlboot.dylib sdlboot.dylib: sdlboot.o # $(CC) -dynamiclib -fPIC -o $@ $< `sdl-config --libs | sed s/-lSDLmain//` # $(CC) -flat_namespace -dynamiclib -init _sdlboot_init_ -single_module -fPIC -o $@ $< `sdl-config --libs | sed s/-lSDLmain//` $(CC) -dynamiclib -fPIC -o $@ $< -framework SDL -framework Cocoa sdlboot.o: sdlboot.m ../main_hook.c # $(CC) -c -fPIC $< `sdl-config --cflags` $(CC) -c -fPIC $< -framework SDL -I/Library/Frameworks/SDL.framework/Headers -framework Cocoa clean: rm -f *.o *.dylib
ビルドしたプログラムと同じ場所にsdlboot, sdlboot.dylibをコピーし、
$ ./sdlboot ./program
のように使うとやっと起動することができるようになりました。
(おまけ) gdcを使ってビルドする場合
Mac OS X用のgdcを使っている場合はSDLのDevelopment用のアーカイブの中にあるSDLMain.mを使うことで起動できない問題を回避できます。
D言語プログラムをgdcでビルドするときには、
// via http://gamehell2000.googlecode.com/svn/trunk/omega/koke/boot.d version (darwin) { extern (C) int _d_run_Dmain(int argc, char* argv[]); extern (C) int SDL_main(int argc, char* argv[]) { return _d_run_Dmain(argc, argv); } }
というコードを追加しておき、SDL-devel-1.2.X-extras.dmgの中のSDLMain.mと一緒にコンパイルすることで正しい順番でCocoaアプリケーションの初期化作業が行われるようになります。
この方法ですとsdlbootを使わずに起動できるようになるのでdmdと比べると簡単です。
4. .app形式の作成
最後にせっかくですのでパッケージ化してみます。MacでSDLアプリケーションの作り方を見ると、Info.plistを作らなくても実行できることがわかりました。簡単なのでその方法にすることにします。
4.1. 必要なファイル構造を作る
$ mkdir -p test.app/Contents/MacOS $ cp program sdlboot sdlboot.dylib test.app/Contents/MacOS
この状態でtest.appを開くとtest.app/Contents/MacOS/testを実行してくれるようです。
4.2. sdlbootでprogramを起動するスクリプトを作る
上述のようにtest.appを開くとtest.app/Contents/MacOS/testを実行してくれますが、今回は引数を渡す必要があるのでシェルスクリプトを使います。WindowsやLinuxなどで動くプログラムと同じソースコードが使えるようにするため、カレントディレクトリをプログラムと同じ場所に変更するスクリプトを用意します。
$ echo -e '#!/bin/sh\ncd (dirname $0)\n./sdlboot ./program' > test.app/Contents/MacOS/test $ chmod +x test.app/Contents/MacOS/test
4.3. アイコン画像
アイコン画像は、Finderからアプリケーションの「情報を見る」→アイコンのペースト、で貼り付けることができます。
パッケージの作成に必要な作業はこれだけですが、
- PROGRAM.app/Contents/MacOS/ にPROGRAMを置けばそのファイルが実行される
- sdlbootを使った場合は、メニューバーに表示されるプログラム名は実際に実行しているファイル名になる
- アプリケーションの情報を見ても著作権情報やバージョンが表示されない
- アイコンを付けるとPROGRAM.app/ContentsにIconという名前の不可視ファイルが作成される
という制限があります。ちゃんとInfo.plistを書いてPROGRAM.app/Contents/に置いた方がユーザフレンドリーなプログラムになるでしょう。
5. 配布するとき
開発環境のみで起動するにはこのままでいいですが、使用しているフレームワーク(今回はSDLフレームワーク)を
PROGRAM.app/Contents/Frameworks
におくことで、フレームワークがインストールされてない環境でもSDLを使うことができます。
またアイコンをFinderから貼り付けた場合にはアイコンファイルがパッケージ内に保存されているので、dmg(ディスクイメージ)やFinderからzip書庫にまとめた方がいいでしょう。
6. 参考にしたページ
- MacOSX版Ruby/SDLバイナリの作成方法
- Info.plistの書き方にも触れられています。
- MacOSX+SDLでの配布物作成法
- SDL.frameworkを使ってビルドする方法など。
標準エラーに出力すると勝手に終了してしまう
Windows (XP) 環境で標準エラーに出力するとアプリケーションが勝手に終了してしまいました。
ただし終了するのはコンソールを非表示にしたときに標準エラーに出力する場合のみで、コンソールが見えている状態や標準出力への出力なら問題がないようです。
stderr.d のソースコード(ウィンドウを出してループするだけ)
ウィンドウを出しておくのにSDLを使用しています。
// Using dmd v1.043, Tango 0.99.8, Windows XP Home SP3 import SDL; import tango.io.Stdout; void main() { SDL_Init(SDL_INIT_VIDEO); SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE); SDL_Event event; bool done = false; while (!done) { while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: done = true; break; default: } } Stderr("stderr").newline; SDL_Delay(33); } SDL_Quit(); }
$ dmd stderr.d -I../SDL ../SDL/*.lib -L/subsystem:windows:4.0
端末から実行すると"stderr"という文字列が沢山流れますが、ダブルクリックで実行するとすぐに終了してしまいます。
また"-L/subsystem:windows:4.0"を取ってやれば、(実行時にコンソールが表示されますが)終了しなくなります。
原因はよく分かってないので調査してみます(もしくは諦めます;-P)。
ディアボロの試練 タイムアタック風味
能力DISC
ハーミットパープル+2 (最大値+10)
感想
ザ・ハンドの突出した強さを感じました。3,4回攻撃するだけでどんなに強いモンスターでも無力化できるので、手持ちのアイテムを確認する手間が省くことができるのが大きいです。チリペッパーは今回のように防御の修正値が高い時には必然ではないなと感じました*1。シルバーチャリオッツを引けた場合は達人二刀流とザ・ハンドに入れるのとどっちが早いのか気になるんですが、そうそう低層で銀チャリは引けないんですよね…。
今回は低層でホワイトアルバムを二枚引いた*2ので攻撃に入れましたが、ホワルバをエコーズACT3で代用するのもいいのか気になります。
反省点
「この一撃を外したら死ぬかもしれない」ときについつい殴ってしまうのを何とかしないといけないですね。おまけに今回はザ・ハンドだというのに:-P
見える特殊MHに操作が止まらずに突入してしまう癖もあるので、早さよりも安定さを重視する必要があると思います。
「クリック数をカウントするソフト(仮称)」を公開しました
yukkeのサイト
最大目標への道が大分遠いことにうんざりしてきたので、D言語とSDLを使ったソフトウェアを作ってWWWで公開してみました。詳細はリンク先で確認してもらえると嬉しいです。
実行環境はWindowsとLinuxです。私の使える環境が少ない(Windows XP SP3, Ubuntu Desktop 8.04.1)のですが、それでも動かなかったらごめんなさい。
全部で411行のちっちゃいプログラムです(機能も極力最低限にしてあります)。一応ソースコードも添付してあります。
HTMLを久しぶりに書いたのですがかなりの苦痛を感じました。CSSは面倒&必要がないので省略しました。その点ブログは設定でちょいちょい選ぶだけでカッコイイサイトができるのでやっぱり楽ですね。
なお、このソフトウェアはゲームではありませんのでご了承下さい;)
このソフトの全機能
- ウィンドウ上をクリックするごとに画面右下のカウンタが増えていきます。
- ウィンドウ上でマウスを往復する(撫でる)とキャラが喜びます。
- 画像や音声ファイルを置き換えることでオリジナルソフトに早変わり;-P
工夫したこと
与太話
基本のアイデアはパソコンを殴るフラッシュムービーです。普通はイライラしたことがあったときに実際のマシンを殴る代わりにムービー中のマシンを殴って破壊するのですが、ある人がいいことがあったときにムービー中のマシンを撫でている(といっても画面上でマウスドラッグをするだけなのですが)のを見て作ってみようと思いました。あとどう見てもへぇボタンのパクリなのは制作者自身何度も感じています;(
使用している絵については、私自身は絵がほとんど描けないので「ゆっくりしていってね!!」から適当に選んだものにパンチの絵(これだけ自作)を付け加えました。
プログラムの作成は別プログラムで使用している自作ライブラリから機能を制限したもの(image.d, sound.d)を使っているので大したことはしていないのですが、公開するためのウェブサイトのアカウントを作って、readmeやHTMLを書いて、スクリーンショットを撮って保存してアップロードして、という処理が意外と大変だということを感じました。
このソフトはゲームではないので描画FPSを60FPS「くらい」にしているんですが、ほんとはちゃんとFPSを管理しないといけないんですよね。というか30FPSくらいでもいいのかな?