この記事は さくらインターネット Advent Calendar 2024 5日目の記事ですが、さくらインターネットに関係のある話は1mmも出てきません
傷ありDVD-ROMを購入
メルカリでPS2ソフトを買った。起動確認済みであるが盤面に傷ありということで相場より安かったのだが、まあなんとかなるだろうということで購入した。
実際にゲームをプレイしてみると、問題とされていた傷も特に影響はないようで、一応オールクリアとされるラインまで到達することはできた 1 。
ここでめでたしめでたし…として終わっても良いのだが、傷がデータに影響を与える可能性があったのか、それともデータの方が十分内側に収まっていることでそもそも関係なかったのかが気になる。
というわけでDVD-ROMの仕様書である ECMA-267 2 を参考に検討していく。DVDフォーラムの規格の方が本家っぽい気はするが、タダで読めるのでECMAの資料を参考にすることとした。
更新履歴
- 2025/01/16: 表現が妙なところを修正
- 2024/12/05: 初版
目次
傷の位置
とりあえず当該のメディアをスキャナでスキャンした。スキャンしたらあまりにもわかりづらい画になったので赤矢印を入れてあるが、チェック ☑ 状に傷が入っている。
よく見ると傷はディスク最外周まで伸びている。 仮に外側から内側に向けてデータが書かれていたとすると、データを持っている領域すべてにおいて誤り訂正パワーでどうにか乗り切ったことになる。
DVD-Rとかにデータ記録すると、内側から外側に向けて記録した跡が見て取れるのでなんとなく内→外なのではという気はする。
一方、ディスク外側の方が読み出し速度速そうな気がするし、メディアとしては確実に読み出し速いところ使えた方が得では?みたいな気持ちもある。実際のところどちらなのか?
内から書くか外から書くか
PTP or OTP
内から書くか外から書くかについては ECMA-267 の 10.6.3 に定義されている。 ざっくり言うと Parallel Track Path (PTP) と Opposite Track Path (OTP) の2種類が定義されており、かつType A/BのディスクではPTPモード、Type C/DのディスクではPTP/OTPのどちらかで記録される、みたいなことが書かれている。
Type A/B/C/Dってなんやねんとなるが、それは 7. General description of the disk に書かれている。ざっくりまとめると
- Type A - 片面1層 4.7GB
- Type B - 両面1層 9.4GB
- Type C - 片面2層 8.5GB
- Type D - 両面2層 17.0GB
1層/2層と片面/両面で4パターンという感じのようだ。A/BはPTPモードとされているので、2層メディアではない時点でPTPであることが確定する。
結局どっちから書くのか
突然出てきたPTP/OTPとかいう区別と書き込み方向には何の関係があるのか?改めて 10.6.3 Track modes を見返すとこの疑問は解消する。
In PTP mode, tracks are read from the inside diameter of the Information Zone to its outside diameter, this applies to both Layer 0 and Layer 1 for Types C and D, see figure 5a. On both layers, the track spiral is turning from the inside to the outside.
In OTP mode, tracks are read starting on Layer 0 at the inner diameter of the Information Zone, continuing on Layer 1 from the outer diameter to the inner diameter. Thus, there is a Middle Zone at the outer diameter on both layers, see figure 5b. The track spiral is turning from the inside to the outside on Layer 0 and in the reverse direction on Layer 1.
何かいろいろと書かれているが要するに
- PTPモードでは内周から外周に向けて書く
- 2層メディアにおいても常に内→外で書く
- OTPモードでは1層目(Layer 0)を内周から外周に向けて書いたあと、2層目(Layer 1)は外周から内周方向に書く
ということが書かれている。
つまりDVD-ROMにおいて外→内に記録することはあるが、それは2層メディアの2層目に限られ、1層メディアにおいては存在し得ないということがわかる。
今回のPS2ソフトは1層メディアのようなので、データ容量が少なければ傷の影響を受けない可能性はありそうだ。
なお、現物が手元にあるならばディスクをDVDドライブに突っ込み、ImgBurnあたりで情報表示することでもっとシンプルに判別が可能である。全部これでいいじゃん
データ領域の始まりと終わり
DVD-ROMのほとんどが内側から書かれており、今回のPS2ソフトもそうであることがわかった。
データが内側から始まることはわかったが、データ本体ってディスクのどこから始まってんの?という疑問が湧いてくる。
これは 10.6.1 Sub-divisions of the Information Zone の辺りに答えが書いてある。以下ECMA-267 3rd Edition p12より引用。
The main parts of the Information Zone are
* the Lead-in Zone
* the Data Zone
* the Lead-out Zone
(略)
* it is the beginning of the Lead-in Zone for Types A and B, and each pair of layers for Type C and D in PTP mode and on Layer 0 in OTP mode,
* it is the end of the Lead-out Zone on Layer 1 for Types C and D in OTP mode.
In the first case, the Lead-in Zone shall end at diameter
d8 = 48,0 mm + 0,0 mm, - 0,4 mm
which is the beginning of the Data Zone.
(略)
The Data Zone shall start after the Lead-in Zone at diameter d8, it shall extend up to diameter d9 = 116,0 mm max.
まとめると、
- the Lead-in Zone がだいたい直径48mmの位置で終わり、そこがデータ領域(the Data Zone)の始点となる
- データ領域は最大で直径116mmの位置まで広がる
みたいなことが書かれている。
容量の計算方法の検討
ディスク上の直径48mm地点からデータを書き始め、外周方向に向けてデータが伸びていくことまで理解できた。
ほとんどのディスクメディアにおいて 3 、ディスクの円周方向に描いた線上に情報を記録する。 データを記録する線はトラックと呼ばれ、トラック上にデータを並べることで連続的にデータを読めるようになっている4 磁気ディスクのトラックは同心円状に配置されるが、DVD-ROMにおいては1本のトラックがらせんを描いている 5。
計算方法を知らないが、どう考えてもめんどくさそうなので、本稿では同心円状に複数のトラックがあるものとして考える。とりあえず答えが知れれば満足なので間違ってたらごめんなさい。
何にせよ描いた線上におけるデータ密度がわかれば、特定容量のデータを記録するのに必要なトラックの長さがわかる。
必要な長さ分の線を描くのにトラックが何周必要かを計算し、トラックピッチを掛け合わせれば特定容量のデータを記録するのにディスクのどの辺までを使用するかを求めることができるはず。
トラックピッチ
トラックピッチは 10.6.2 Track geometry に記載があり、 0.74μm ±0.03μm とされている。
トラックピッチとはトラックの中心から隣のトラックの中心までが0.74μmということだが、雑にトラックの幅と考えてしまっても問題ないはずなのでここではトラック幅と書くことにする。
線のデータ密度
ECMA-267 を見ていくと 26.5.1 Physical format information の項目に Recording density の定義がある。 それによればディスク上に存在するチャンネルビット長がディスク上のコントロールデータゾーンに書かれているらしく、値は 0.133μm か 0.147μm のどちらかを取るらしい。
残念ながらコントロールデータゾーンをそのまま読む手段を持っていない。しかし、見ていたところどうもImgBurnがLinear Densityという値でデータ密度を表示してくれていることに気づいた。
Linear Density: 0.267 um/bit
という表示が確認できるが、これは明らかにRecording densityが取り得る 0.133μm と 0.147μm のどちらでもない。
0.133μmを倍にすると0.266μmなので、おそらく倍の値が表示されているのだろう。少し乱暴だがとりあえず 0.267 um/bit
を前提として先に進むことにする。
データ領域が特定の直径になるまでの総トラック長
残る要素は必要トラック数を計算する、つまり内側から外側まで何周らせんを描いたら必要なデータ量が書けるのかを調べるだけとなった。
先に述べたとおりデータ領域は直径48mm地点から始まることがわかっており、ここから外周に向けてらせん状に、0.74μmピッチで多数のトラックが存在している。
ということは
- データ開始地点の直径の円周長を計算して全長とし
- そこより1.48μm (0.74μmの倍) 大きい直径の円周長を計算し全長に足し込む
- 上記を繰り返し、求めたい特定の径まで計算対象の直径が到達したときの全長をデータ密度で割る
…とすれば、何GB地点で特定の直径になるのかが求められるのではないかと考えた。
なんかもっとスマートにやる方法があるのかもしれないが、答えが出ればいいのでとりあえず計算してみる。
120mm ディスクの容量を計算してみる
ぱっと見アプローチは正しそうな気がするが、2桁の足し算も怪しい自分が考えることなので全く信用できない。
そこでデータの始点と終点、格納できる容量が明らかになっているもの、すなわち120mmディスク全領域を使った時のデータ容量を求めてみることで安心感を得ることとした。 先に述べたとおりデータ領域は最大で116mm地点まで広がるので、終点を116mmとして手で計算してみる。
0.74μmの糸(トラック)を48mmの円柱(データ領域最内周地点)に巻き付けていき、円柱が糸によって116mmまで太ったときの巻き数を計算することで総トラック数が求められるはずだ。 48mmから116mmまで太ったということは増分は68mm分、0.74μm幅のトラックが一周すると直径は1.48μm増えるので、68mmを1.48μmで割ってやれば必要な巻き数が計算できる。
mmとμmが混在してややこしいのでメートル単位に統一して計算すると
>>> 0.068 / 0.00000148
45945.94594594595
だいたい45946トラックあるという結果になった。あとは1.48μm直径が増えた円の線長を足し込んでいく処理を45946回繰り返せば答えが出るが、面倒なのでPythonに任せることにした。Copilotに聞いたら数列の和の公式使えとか言われたが、ぱっと見で意味わからんだろこんなもんということでforのままにした。
>>> import math
>>> l = 0
>>> for n in range(0, math.ceil(0.068 / 0.00000148)+1):
... l = l + (math.pi * (0.048+2*n*0.00000074))
...
>>> l
11836.439901538502
だいたい総延長11.8kmぐらいの線になるようだ。不連続とはいえ直径12cmのディスクにそんな長い溝が掘られていると考えると感慨深いものがあるが、とりあえず今はどうでもいいので次に進む。
データ容量の計算
ImgBurnの情報からLinear Densityが 0.267 μm / bit であることがわかっている
DVDのセクタ配置はCLV 6 7、つまりディスクのどこにおいても同一の記録密度で書かれているはずなので、長さを密度で雑に割ってみる
>>> 11836 / 0.000000267
44329588014.98128
ビット単位なので8で割り、さらにMBに直すと
>>> 11836 / 0.000000267 / 8 / 10**6
5541.19850187266
5541MBとなった。片面1層のDVD-ROMは最大4.7GBのはずなのに、明らかにそれより多い数字が出てしまった。これは一体どういうことなのか?
エラー訂正符号によるオーバーヘッド
ECMA-267 を読み進めていくと、なんだかデータにいろんなかぶせ物がされていることがわかる。 オーバーヘッドをさっ引くといい感じの数字になりそうな気配がするので、これらについて見ていく。
Data Frame
ECMA-267 の 16. Data Frames を見ると データ2048byteごとにフレームID、IDのエラー検出コード、著作権制御情報、データ本体のエラー検出コードが16byte付く。 Figure 16がわかりやすい。
これらの値はデータそのものとは関係ないのでオーバーヘッドとなる。保持している実データ量をフレーム全体のサイズで割り、効率を計算してみると
>>> 2048/(2048+16)
0.9922480620155039
だいたい99.2%(=0.8%ロス)となった。この調子でフレームを剥がしていけばどの程度オーバーヘッドがあるかわかるだろう。
ECC Block
ECC Blockは誤り訂正データが格納されている領域で、今回問題となった傷を救ってくれた可能性が高い、ありがたいデータ領域だ。
ありがたいのだが、最大4.7GBのデータを記録できるメディアなので、誤り訂正データもそれなりに大きなものになるのではないかというあたりまでは容易に想像が付く。
誤り訂正そのものについてはDVD-R焼き界隈の方により執筆されたわかりやすい資料 8 が存在するのでそれを見た方が良いが、記録容量への悪影響という意味で検討する人はあまりいないと思われるので、ECMA-267を見て考えてみよう。
というわけでECMA-267 の Figure 20をパクってきた。
これを読み解いていくと
- Data Frame(正確にはData FrameにスクランブルをかけたScrambled Frame)が172byte来るごとにパリティが10byte付与される
- 水平パリティってやつ?
- これをECC Blockの1rowとする
- 192row来る度にパリティのみのrowが16row分付与される
- 垂直パリティってやつ?
- 垂直パリティにも水平パリティが付与される
という感じのようで、水平垂直パリティの超強化版みたいなやつと考えれば良さそう。IPA試験でしか見たことのない水平垂直パリティってワードが思い出せなくて思い出すのにすごい時間が掛かった。
ゴチャゴチャ書いてきたが、要するに1つのECC Blockのサイズは (172+10) * (192+16) byteであり、そのうちデータそのものは 172*192 byteでしかなく、残りはパリティということだ。これらをそのまま計算機に突っ込んで割ってやれば答えが出る。
>>> (172*192)/((172+10)*(192+16))
0.8723584108199492
誤り訂正データによって13%ぐらいロスが出ることがわかった。1割消えてなくなると考えるとかなり大きい数字だが、前述の通りここのデータに救われた可能性が高いので感謝しなければならない。
Physical Sectors
パリティを盛ったらいよいよディスクにデータを記録…と行きたいところであるが、ピックアップの位置決めとかに使うのかデータ1456bit(=182byte)ごとに32bitのsync codeを付与する必要があるようだ。
もちろんこれもオーバーヘッドとなる。
>>> 1456/(1456+32)
0.978494623655914
2.2%ぐらいのロス
容量
これまで出てきた数字を全部掛け合わせてみて正しい数字になるとうれしい
>>> 0.993*0.872*0.978
0.846846288
だいたい85%らしい。さっき出た容量に掛けてみると
>>> 5541*0.993*0.872*0.978
4692.375281808
なんかすごく約4.7GBという感じの数字になった。
ECC BlockとPhysical Sectorの間
ここまでの文では都合良くすっ飛ばしているが、実際にECMA-267を読んでいくとECC BlockとPhysical Sectorsの間にRecording FramesとModulationという2ステップが挟まっていることがわかる。
Recording Frameについては、データ12rowごとにECC Blockの下の方にまとまって出てきたPOを1row切り出してきて差し込む形で書き込めみたいな話が書かれているだけっぽいので容量計算には関係なさそうだが、Modulationの方は8bitのデータを16bitにエンコードする的な話が書かれている。8bitが16bitに化けるということは記録できる量が半分になることは明らかで、今までの計算は何だったのかというぐらいには影響がある。
しかしよくよく考えると、ここまでの計算はImgBurnで出てきた0.267 μm / bitというぼんやりした数字に基づいて行ってきた。一方、線のデータ密度の項目で述べたとおり、Recording densityは 0.133μm か 0.147μm のどちらかの値を取るはずである。
とりあえず倍の値表示してんじゃね、という話でここまで書いてきたが、実際のところImgBurnの表示するLinear DensityはModulationを掛ける前の1bitを記録するために必要なチャンネルビット長を表示しているのではないかという推測が出来る。
結果が合ってるんだから合ってるだろみたいな雑な話ではあるが、Recording densityが半分で記録効率が倍なのに対してModulationで効率半分になる辺りを考えても辻褄は合う。
とりあえずここまで書いちゃったというのもあるので、本稿では記録密度は一貫して 0.267 μm / bit
で扱っていくこととする。
計算
これらを全部まとめてPythonスクリプトに落とし込んだのが以下。コマンドライン引数で直径も指定できるようにした。
effective:
で始まる行で実際のデータ位置、 raw:
で始まる行はオーバーヘッドを含まない物理的なデータ位置を表示するようになっている。ぶっちゃけraw要らんでしょという感じであるが、削るのも面倒なのでそのままにしてある。なおCopilotに指摘された良い感じに一撃で計算してくれるやつは素直に反映した。
import math
import sys
initial_diameter = 0.048
end_diameter = 0.116
pitch = 0.00000074
density = 0.000000267 # ImgBurn読みで出した値 0.133μmの倍
overhead_factor = 0.993*0.872*0.978
if __name__ == '__main__':
if len(sys.argv) > 1:
end_diameter = float(sys.argv[1]) / 1000
print('diameter: %.1f mm' % (end_diameter * 1000))
n_max = math.ceil((end_diameter - initial_diameter) / (pitch*2))
l = math.pi * (initial_diameter * (n_max + 1) + pitch * n_max * (n_max + 1))
size_byte = l / density / 8
size_mb = size_byte / 10**6
print("raw: %.1f bytes" % size_byte)
print("raw: %.1f MB" % size_mb)
print("raw: %.1f MiB" % (size_byte / 1024**2))
print("effective: %.1f bytes" % (size_byte * overhead_factor))
print("effective: %.1f MB" % (size_mb * overhead_factor))
print("effective: %.1f MiB" % (size_byte / 1024**2 * overhead_factor))
$ python dvd.py
diameter: 116.0 mm
raw: 5541404448.3 bytes
raw: 5541.4 MB
raw: 5284.7 MiB
effective: 4692717787.3 bytes
effective: 4692.7 MB
effective: 4475.3 MiB
$ python dvd.py 60
diameter: 60.0 mm
raw: 644122613.5 bytes
raw: 644.1 MB
raw: 614.3 MiB
effective: 545472844.2 bytes
effective: 545.5 MB
effective: 520.2 MiB
実験
一応それらしい数字を出すには至ったが、何分アホが雑に仕様書斜め読みして出した答えなのでいまいち信用できない。 実際にDVD-ROMに傷を付けてみて、エラーが出た位置と計算結果が合っていればいくらか説得力が増すだろう。
今回購入したPS2ソフトそのもので試すのは惜しいので、家に転がっていた適当なDVD-ROMに油性マジックで落書きをして読み取りエラーを起こさせてみることにした。
線状の傷
始めに試したのが今回問題となったPS2ソフトの傷を模したパターンで、似たような場所へ適当にチェック状の書き込みを行った。
結果はというと実験は失敗で、この程度の書き込みだとエラー訂正で補償されてしまうようで何も起きなかった 9 。
言われてみれば、この書き込みパターンでは円周方向の読み取りに対して散発的にエラーが発生する感じに思われるので、問題ないのも当然かなという感じ。 メディアをディスク形状にした時点でこのタイプの傷が多発しそうなことを考えるとよく出来た仕組みだなあと思わされる。
今回のPS2ソフトの傷が、読み取り面ではなくレーベル面に入っていたとしても問題なく読めたのではないか、という意味では希望が持てる結果ではあったが、これでは計算結果との比較が出来ないのでやり直し。
帯状の傷
というわけで塗りエリアを増やした。 線を書いたときに目視で適当に位置を決めた結果、容量計算する段になって書き込み場所の測定に大変苦労したので、今回は直径89mmの円形に切った紙で盤面中央部をマスキングし、紙より外側を帯状に塗りつぶすこととした。 本当は90mmとする予定だったのだが、コンパスカッターで適当に切り出した結果89mmになっていた。
ImgBurnで読み出してみたら見事エラーが出た。 Failed to Read Sector 1153129
とのことで、Data Frameの項で述べたとおりセクターサイズは2048 byte なので
>>> 1153129 * 2048 / 10**6
2361.608192
だいたい2361.6MB地点でエラーが出たことになる。 実際の数字が得られたので、前掲のスクリプトで計算上読み取り可能な位置を計算してみる。
$ python dvd.py 89
diameter: 89.0 mm
raw: 2791149309.3 bytes
raw: 2791.1 MB
raw: 2661.8 MiB
effective: 2363674431.8 bytes
effective: 2363.7 MB
effective: 2254.2 MiB
effective: 2363.7 MB
と出た。かなり良い感じなのでは?
本番
ディスク全域の容量計算とディスク汚し実験で良さそうな結果が得られたので、今回問題となったPS2ソフトで計算してみよう。無駄に長くなったがようやく答えが出せる。
傷位置の測定
盤面のスキャン画像を適当なCAD(Solid Edge 2D Drafting)に食わせ、目視で円を描画した上で寸法を測定することにより傷位置を測定した。その結果が以下の図。
目視で合わせたので真に中心が合ってるかと言われるとだいぶ微妙だが、それを言っても始まらない。
というわけで傷位置は直径91.48mmの地点にあるものとした。
計算
実験と値が近すぎるのでおおよその結果は見えてしまっているが、改めて計算してみる。
$ python dvd.py 91.48
diameter: 91.5 mm
raw: 3013597336.8 bytes
raw: 3013.6 MB
raw: 2874.0 MiB
effective: 2552053718.2 bytes
effective: 2552.1 MB
effective: 2433.8 MiB
というわけで約2.5GB地点辺りまでは無傷と言って良いのでは?という結果が得られた。
結論
データがだいたい3.7GBあるので影響は受け得る。解散!
まとめ
- 誤り訂正はすごい
- オチも何も考えず勢いで書き始めちゃったので、実験と本番の数字が似通ってしまったのが文章として失敗だなと思った
- 紙を切るのが意外と面倒なのでやり直す気力はなかった
- 3年前に購入してから今までずっと気になってたので解決できて良かった
謝辞
公開前の内容チェックにご協力頂いた @chibiegg @homelith 両氏に感謝いたします。
-
サントラで言うところのオールクリア!が聞けるところまではクリアしたが、バッドエンド一切見てないので実質嘘 ↩︎
-
“ECMA-267 120mm DVD - Read-Only Disk 3rd Edition” - Ecma International https://www.ecma-international.org/wp-content/uploads/ECMA-267_3rd_edition_april_2001.pdf ↩︎
-
例外があるのかはわからないが、そうでないとしたら円盤状にする意味はないはず ↩︎
-
“トラック (記録媒体)” https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%A9%E3%83%83%E3%82%AF_(%E8%A8%98%E9%8C%B2%E5%AA%92%E4%BD%93) ↩︎
-
10.6.2 Track geometry - ECMA-267 3rd Edition ↩︎
-
“混沌のDVD規格、ライトワンス型のDVD+Rが登場” - デジタルアドバンテージ https://atmarkit.itmedia.co.jp/fpc/kaisetsu/dvd_war/dvdwar01.html ↩︎
-
Bit b31に関する記述
shall be set to ZERO, indicating the CLV format for read-only disks
- ECMA-267 3rd Edition p24 ↩︎ -
“より深い理解のために(3) - ECCブロックと2種のパリティデータ” - Blu-ray/DVDメディア検証場 「しあにんなお昼ごはん」-thiaNine’s Brunch- https://dvd-r.jpn.org/help/advanced3.htm ↩︎
-
この結果が出た時点でそもそもこの記事の意味ないのでは、みたいなことは考えてはいけない ↩︎