File: neo-geo.cpp

package info (click to toggle)
ares 126-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 32,600 kB
  • sloc: cpp: 356,508; ansic: 20,394; makefile: 16; sh: 2
file content (137 lines) | stat: -rw-r--r-- 5,424 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
struct NeoGeo : Cartridge {
  auto name() -> string override { return "Neo Geo"; }
  auto extensions() -> vector<string> override { return {"ng"}; }
  auto read(string location, string match) -> vector<u8>;
  auto load(string location) -> bool override;
  auto save(string location) -> bool override;
  auto analyze(vector<u8>& p, vector<u8>& m, vector<u8>& c, vector<u8>& s, vector<u8>& v) -> string;
  auto endianSwap(vector<u8>&) -> void;
};

auto NeoGeo::read(string location, string match) -> vector<u8> {
  bool characterROM = match == "*.c*";
  bool programROM   = match == "*.p*";

  vector<u8> output;
  //parse Neo Geo ROM images in MAME ZIP file format:
  if(location.iendsWith(".zip")) {
    Decode::ZIP archive;
    if(archive.open(location)) {
      //find all files that match the requested pattern and then sort the results
      vector<string> filenames;
      for(auto& file : archive.file) {
        if(file.name.imatch(match)) filenames.append(file.name);
      }
      filenames.sort();

      //interleave charater ROM bitplanes
      if(characterROM) {
        vector<string> interleaved;
        for(u32 index = 0; index < filenames.size(); index += 2) interleaved.append(filenames[index]);
        for(u32 index = 1; index < filenames.size(); index += 2) interleaved.append(filenames[index]);
        filenames = interleaved;
      }

      //build the concatenated ROM image
      bool first = true;
      for(auto& filename : filenames) {
        for(auto& file : archive.file) {
          if(file.name != filename) continue;
          auto input = archive.extract(file);
          output.resize(output.size() + input.size());
          if(first && programROM) {
            first = false;
            if(input.size() > 1_MiB) {
              memory::copy(output.data() + output.size() - input.size(), input.data() + 1_MiB, input.size() - 1_MiB);
              memory::copy(output.data() + output.size() - (input.size() - 1_MiB), input.data(), 1_MiB);
              continue;
            }
            while(output.size() < 1_MiB) {
              memory::copy(output.data() + output.size() - input.size(), input.data(), input.size());
              output.resize(output.size() + min(1_MiB - output.size(), input.size()));
            }
          }
          memory::copy(output.data() + output.size() - input.size(), input.data(), input.size());
        }
      }
      if(programROM) endianSwap(output);
    }
  }
  return output;
}

auto NeoGeo::load(string location) -> bool {
  vector<u8> programROM;    //P ROM (68K CPU program)
  vector<u8> musicROM;      //M ROM (Z80 APU program)
  vector<u8> characterROM;  //C ROM (sprite and background character graphics)
  vector<u8> staticROM;     //S ROM (fix layer static graphics)
  vector<u8> voiceROM;      //V ROM (ADPCM voice samples)
  if(directory::exists(location)) {
    programROM   = file::read({location, "program.rom"});
    musicROM     = file::read({location, "music.rom"});
    characterROM = file::read({location, "character.rom"});
    staticROM    = file::read({location, "static.rom"});
    voiceROM     = file::read({location, "voice.rom"});
  } else if(file::exists(location)) {
    programROM   = NeoGeo::read(location, "*.p*");
    musicROM     = NeoGeo::read(location, "*.m*");
    characterROM = NeoGeo::read(location, "*.c*");
    staticROM    = NeoGeo::read(location, "*.s*");
    voiceROM     = NeoGeo::read(location, "*.v*");
  }
  if(!programROM  ) return false;
  if(!musicROM    ) return false;
  if(!characterROM) return false;
  if(!staticROM   ) return false;
  if(!voiceROM    ) return false;

  Hash::SHA256 hash;
  hash.input(programROM);
  hash.input(musicROM);
  hash.input(characterROM);
  hash.input(staticROM);
  hash.input(voiceROM);
  auto sha256 = hash.digest();

  this->location = location;
  this->manifest = analyze(programROM, musicROM, characterROM, staticROM, voiceROM);
  auto document = BML::unserialize(manifest);
  if(!document) return false;

  pak = new vfs::directory;
  pak->setAttribute("sha256",  sha256);
  pak->setAttribute("title",   document["game/title"].string());
  pak->append("manifest.bml",  manifest);
  pak->append("program.rom",   programROM);
  pak->append("music.rom",     musicROM);
  pak->append("character.rom", characterROM);
  pak->append("static.rom",    staticROM);
  pak->append("voice.rom",     voiceROM);
  return true;
}

auto NeoGeo::save(string location) -> bool {
  auto document = BML::unserialize(manifest);

  return true;
}

auto NeoGeo::analyze(vector<u8>& p, vector<u8>& m, vector<u8>& c, vector<u8>& s, vector<u8>& v) -> string {
  string manifest;
  manifest += "game\n";
  manifest +={"  name:   ", Medium::name(location), "\n"};
  manifest +={"  title:  ", Medium::name(location), "\n"};
  manifest += "  board\n";
  manifest +={"    memory type=ROM size=0x", hex(p.size(), 8L), " content=Program\n"};
  manifest +={"    memory type=ROM size=0x", hex(m.size(), 8L), " content=Music\n"};
  manifest +={"    memory type=ROM size=0x", hex(c.size(), 8L), " content=Character\n"};
  manifest +={"    memory type=ROM size=0x", hex(s.size(), 8L), " content=Static\n"};
  manifest +={"    memory type=ROM size=0x", hex(v.size(), 8L), " content=Voice\n"};
  return manifest;
}

auto NeoGeo::endianSwap(vector<u8>& memory) -> void {
  for(u32 address = 0; address < memory.size(); address += 2) {
    swap(memory[address + 0], memory[address + 1]);
  }
}