cdrom.hpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #pragma once
  2. #include <nall/array-span.hpp>
  3. #include <nall/cd.hpp>
  4. #include <nall/file.hpp>
  5. #include <nall/string.hpp>
  6. #include <nall/decode/cue.hpp>
  7. #include <nall/decode/wav.hpp>
  8. namespace nall::vfs {
  9. struct cdrom : file {
  10. static auto open(const string& cueLocation) -> shared_pointer<cdrom> {
  11. auto instance = shared_pointer<cdrom>{new cdrom};
  12. if(instance->load(cueLocation)) return instance;
  13. return {};
  14. }
  15. auto size() const -> uintmax override {
  16. return _image.size();
  17. }
  18. auto offset() const -> uintmax override {
  19. return _offset;
  20. }
  21. auto seek(intmax offset, index mode) -> void override {
  22. if(mode == index::absolute) _offset = (uintmax)offset;
  23. if(mode == index::relative) _offset += (intmax)offset;
  24. }
  25. auto read() -> uint8_t override {
  26. if(_offset >= _image.size()) return 0x00;
  27. return _image[_offset++];
  28. }
  29. auto write(uint8_t data) -> void override {
  30. //CD-ROMs are read-only; but allow writing anyway if needed, since the image is in memory
  31. if(_offset >= _image.size()) return;
  32. _image[_offset++] = data;
  33. }
  34. private:
  35. auto load(const string& cueLocation) -> bool {
  36. Decode::CUE cuesheet;
  37. if(!cuesheet.load(cueLocation)) return false;
  38. CD::Session session;
  39. session.leadIn.lba = -LeadInSectors;
  40. session.leadIn.end = -1;
  41. int lbaDisc = Track1Pregap;
  42. int endDisc = lbaDisc;
  43. for(auto& file : cuesheet.files) {
  44. for(auto& track : file.tracks) {
  45. session.tracks[track.number].control = track.type == "audio" ? 0b0000 : 0b0100;
  46. session.tracks[track.number].address = 0b0001;
  47. for(auto& index : track.indices) {
  48. session.tracks[track.number].indices[index.number].lba = lbaDisc + index.lba;
  49. session.tracks[track.number].indices[index.number].end = lbaDisc + index.end;
  50. }
  51. }
  52. lbaDisc += file.tracks.last().indices.last().end + 1;
  53. endDisc = lbaDisc;
  54. }
  55. session.leadOut.lba = endDisc;
  56. session.leadOut.end = endDisc + LeadOutSectors - 1;
  57. for(uint track : range(100)) {
  58. if(!session.tracks[track]) continue;
  59. session.firstTrack = track;
  60. for(uint index : range(100)) {
  61. if(!session.tracks[track].indices[index]) continue;
  62. session.tracks[track].firstIndex = index;
  63. break;
  64. }
  65. break;
  66. }
  67. for(uint track : reverse(range(100))) {
  68. if(!session.tracks[track]) continue;
  69. session.lastTrack = track;
  70. for(uint index : reverse(range(100))) {
  71. if(!session.tracks[track].indices[index]) continue;
  72. session.tracks[track].lastIndex = index;
  73. break;
  74. }
  75. break;
  76. }
  77. session.tracks[1].indices[0].lba = 0; //track 1, index 0 is not present in CUE files
  78. session.tracks[1].indices[0].end = Track1Pregap - 1;
  79. _image.resize(2448 * (LeadInSectors + endDisc + LeadOutSectors));
  80. lbaDisc = Track1Pregap;
  81. for(auto& file : cuesheet.files) {
  82. auto location = string{Location::path(cueLocation), file.name};
  83. auto filedata = nall::file::open(location, nall::file::mode::read);
  84. if(file.type == "wave") filedata.seek(44); //skip RIFF header
  85. uint64_t offset = 0;
  86. for(auto& track : file.tracks) {
  87. for(auto& index : track.indices) {
  88. for(int sector : range(index.sectorCount())) {
  89. auto target = _image.data() + 2448ull * (LeadInSectors + lbaDisc + index.lba + sector);
  90. auto length = track.sectorSize();
  91. if(length == 2048) {
  92. //ISO: generate header + parity data
  93. memory::assign(target + 0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff); //sync
  94. memory::assign(target + 6, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00); //sync
  95. auto [minute, second, frame] = CD::MSF(lbaDisc + index.lba + sector);
  96. target[12] = CD::BCD::encode(minute);
  97. target[13] = CD::BCD::encode(second);
  98. target[14] = CD::BCD::encode(frame);
  99. target[15] = 0x01; //mode
  100. filedata.read({target + 16, length});
  101. CD::RSPC::encodeMode1({target, 2352});
  102. }
  103. if(length == 2352) {
  104. //BIN + WAV: direct copy
  105. filedata.read({target, length});
  106. }
  107. }
  108. }
  109. offset += track.sectorSize() * track.sectorCount();
  110. }
  111. lbaDisc += file.tracks.last().indices.last().end + 1;
  112. }
  113. auto subchannel = session.encode(LeadInSectors + session.leadOut.end + 1);
  114. if(auto overlay = nall::file::read({Location::notsuffix(cueLocation), ".sub"})) {
  115. auto target = subchannel.data() + 96 * (LeadInSectors + Track1Pregap);
  116. auto length = (int64_t)subchannel.size() - 96 * (LeadInSectors + Track1Pregap);
  117. memory::copy(target, length, overlay.data(), overlay.size());
  118. }
  119. for(uint64_t sector : range(size() / 2448)) {
  120. auto source = subchannel.data() + sector * 96;
  121. auto target = _image.data() + sector * 2448 + 2352;
  122. memory::copy(target, source, 96);
  123. }
  124. return true;
  125. }
  126. vector<uint8_t> _image;
  127. uintmax _offset = 0;
  128. static constexpr int LeadInSectors = 7500;
  129. static constexpr int Track1Pregap = 150;
  130. static constexpr int LeadOutSectors = 6750;
  131. };
  132. }