mode1to2.lua 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. -- Copyright © 2019 Pedro Gimeno Fortea
  2. -- This program may be used by anyone and may be freely copied and distributed
  3. -- in verbatim form. Modifications for personal use are allowed.
  4. -- NO IMPLIED WARRANTIES.
  5. -- TODO: Decide a proper license, as the one above sucks.
  6. -- Usage: luajit mode1to2.lua infile outfile
  7. -- Reconstruct MODE2/2352 from a MODE1/2048 image.
  8. -- The reconstruction does not include Reed-Solomon ECC.
  9. -- It only includes the 32-bit CRC EDC.
  10. -- This suffices for Mednafen's PSX module to accept the image as valid.
  11. -- According to the spec, the poly is:
  12. -- (x^16 + x^15 + x^2 + 1) * (x^16 + x^2 + x + 1)
  13. -- which equals x^32 + x^31 + x^16 + x^15 + x^4 + x^3 + x + 1
  14. -- but the x^31 term is in bit 0 of the byte
  15. -- therefore the polynomial word is (in bit reverse order):
  16. -- 11011000000000011000000000000001
  17. -- which in hex is: 0xD8018001
  18. local ffi = require('ffi')
  19. local bit = require('bit')
  20. local bxor = bit.bxor
  21. local band = bit.band
  22. local blshift = bit.lshift
  23. local brshift = bit.rshift
  24. local CRCpoly = 0xD8018001
  25. local CRCtable
  26. local function makeCRCTable()
  27. -- Make a bit-reversal table
  28. local B = ffi.new('unsigned char[256]')
  29. for i = 0, 255 do
  30. B[i] = band(blshift(i, 7), 0x80)
  31. + band(blshift(i, 5), 0x40)
  32. + band(blshift(i, 3), 0x20)
  33. + band(blshift(i, 1), 0x10)
  34. + band(brshift(i, 1), 0x08)
  35. + band(brshift(i, 3), 0x04)
  36. + band(brshift(i, 5), 0x02)
  37. + band(brshift(i, 7), 0x01)
  38. end
  39. CRCtable = ffi.new('uint32_t[256]')
  40. for i = 0, 255 do
  41. local ShiftReg = B[i]
  42. for j = 0, 7 do
  43. ShiftReg = bxor(brshift(ShiftReg, 1),
  44. band(CRCpoly, -band(ShiftReg, 1)))
  45. CRCtable[B[i]] = band(ShiftReg, 0xFFFFFFFF)
  46. end
  47. end
  48. end
  49. local function CRC(data)
  50. local crc = 0
  51. for i = 16, 2071 do
  52. crc = bxor(brshift(crc, 8), CRCtable[band(bxor(crc, data[i]), 0xFF)])
  53. end
  54. return crc
  55. end
  56. local function main()
  57. makeCRCTable()
  58. local buf = ffi.new('unsigned char[2352]')
  59. ffi.fill(buf, 2352)
  60. local f = io.open(arg[1], "rb")
  61. local size = f:seek("end")
  62. if size % 2048 ~= 0 then
  63. f:close()
  64. error("File length is not a multiple of 2048.")
  65. end
  66. f:seek("set")
  67. local data = ffi.new('unsigned char[?]', size)
  68. print("reading image")
  69. for i = 0, size - 1, 2048 do
  70. ffi.copy(data + i, f:read(2048))
  71. if i % 16777216 == 0 then
  72. collectgarbage() -- needs a bit of help
  73. end
  74. end
  75. f:close()
  76. print("processing")
  77. f = io.open(arg[2], "wb")
  78. -- 0-11 is sync data.
  79. ffi.copy(buf, "\0\255\255\255\255\255\255\255\255\255\255\0")
  80. -- 12, 13 and 14 are BCD minute/second/frame and are calculated per sector
  81. buf[15] = 2 -- CD-XA Mode 2
  82. -- 16-23 is the subheader. 20-23 is a copy of 16-19.
  83. -- 16 is file (?). 17 is channel. 18 is submode (bitfield). 19 is coding.
  84. -- No idea about what file/channel/coding are used for. Maybe audio/video.
  85. -- For submode, see Mednafen 0.9.41+dfsg sources, src/psx/cdc.cpp line 621.
  86. -- We set the submode on sectors 12-16 like in PSX discs; the rest as DATA.
  87. -- Not sure if it makes a difference.
  88. -- 24-2071 is the data (payload).
  89. -- 2072-2075 is CRC, calculated over the subheader + data without padding.
  90. -- Bit-reversed like CRC-32; little-endian.
  91. -- 2076-2247 is P-parity (Reed-Solomon ECC). Not calculated here.
  92. -- 2248-2351 is Q-parity (Reed-Solomon ECC). Not calculated here.
  93. -- For Reed-Solomon details see ECMA-130 and the Mednafen sources.
  94. local i = 0
  95. repeat
  96. do
  97. local msf_sector = brshift(i, 11) + 150
  98. buf[14] = msf_sector % 75
  99. buf[13] = (msf_sector - buf[14]) / 75 % 60
  100. buf[12] = (msf_sector - buf[13] * 75 - buf[14]) / (75 * 60)
  101. end
  102. do
  103. -- Convert to BCD
  104. local tmp = buf[12] % 10
  105. buf[12] = (buf[12] - tmp) / 10 * 16 + tmp
  106. tmp = buf[13] % 10
  107. buf[13] = (buf[13] - tmp) / 10 * 16 + tmp
  108. tmp = buf[14] % 10
  109. buf[14] = (buf[14] - tmp) / 10 * 16 + tmp
  110. end
  111. buf[18] = (i >= 0x6000 and i < 0x8000) and 0x20 -- FORM = 2
  112. or (i == 0x8000) and 0x09 -- DATA, EOR
  113. or 0x08 -- DATA
  114. buf[22] = buf[18]
  115. ffi.copy(buf + 24, data + i, 2048)
  116. local crc = CRC(buf)
  117. buf[2072] = band(crc, 0xFF)
  118. buf[2073] = band(brshift(crc, 8), 0xFF)
  119. buf[2074] = band(brshift(crc, 16), 0xFF)
  120. buf[2075] = band(brshift(crc, 24), 0xFF)
  121. f:write(ffi.string(buf, 2352))
  122. i = i + 2048
  123. if i % 16777216 == 0 then
  124. collectgarbage()
  125. end
  126. until i == size
  127. f:close()
  128. print("done")
  129. end
  130. return main()