/** * lädt eine ACT-File in ein ArchivInfo. * * @param[in] file Dateiname der ACT-File * @param[out] items ArchivInfo-Struktur, welche gefüllt wird * * @return Null bei Erfolg, ein Wert ungleich Null bei Fehler * * @author FloSoft * @author OLiver */ int libsiedler2::loader::LoadACT(const std::string& file, ArchivInfo& items) { long size; if(file.empty()) return 1; // Datei zum lesen öffnen boost::scoped_ptr<FILE> act(fopen(file.c_str(), "rb")); // hat das geklappt? if(!act) return 2; fseek(act.get(), 0, SEEK_END); size = ftell(act.get()); fseek(act.get(), 0, SEEK_SET); // sind es 256*3 Bytes, also somit 8bit-RGB? if(size != 256*3) return 3; ArchivItem_Palette* palette = (ArchivItem_Palette*)getAllocator().create(BOBTYPE_PALETTE); if(palette->load(act.get(), false) != 0){ delete palette; return 4; } // einlesen items.clear(); items.push(palette); // Alles OK return 0; }
/** * lädt eine Sounddatei in ein ArchivInfo. (midi, xmidi, wave) * * @param[in] file Dateiname der Sounddatei * @param[out] items ArchivInfo-Struktur, welche gefüllt wird * * @return Null bei Erfolg, ein Wert ungleich Null bei Fehler */ int libsiedler2::loader::LoadSND(const std::string& file, ArchivInfo& items) { if(file.empty()) return 1; // Datei zum lesen öffnen boost::iostreams::mapped_file_source mmapFile; try{ mmapFile.open(bfs::path(file)); } catch(std::exception& e){ std::cerr << "Could not open '" << file << "': " << e.what() << std::endl; return 2; } typedef boost::iostreams::stream<boost::iostreams::mapped_file_source> MMStream; MMStream snd(mmapFile); // hat das geklappt? if(!snd) return 2; baseArchivItem_Sound* sound = baseArchivItem_Sound::findSubType(snd); if(!sound) return 3; size_t size = getIStreamSize(snd); if(sound->load(snd, static_cast<unsigned>(size)) != 0) return 4; items.clear(); items.push(sound); return 0; }
/** * lädt eine BOB-File in ein ArchivInfo. * * @param[in] file Dateiname der BOB-File * @param[out] items ArchivInfo-Struktur, welche gefüllt wird * * @return Null bei Erfolg, ein Wert ungleich Null bei Fehler */ int libsiedler2::loader::LoadBOB(const std::string& file, const ArchivItem_Palette* palette, ArchivInfo& items) { unsigned int header; if(file.empty() || palette == NULL) return 1; // Datei zum lesen öffnen boost::iostreams::mapped_file_source mmapFile; try { mmapFile.open(bfs::path(file)); } catch(std::exception& e) { std::cerr << "Could not open '" << file << "': " << e.what() << std::endl; return 2; } typedef boost::iostreams::stream<boost::iostreams::mapped_file_source> MMStream; MMStream mmapStream(mmapFile); libendian::EndianIStream<false, MMStream& > bob(mmapStream); // hat das geklappt? if(!bob) return 2; // Header einlesen bob >> header; // ist es eine BOB-File? (Header 0xF601F501) if(header != 0x01F501F6) return 4; ArchivItem_Bob* item = dynamic_cast<ArchivItem_Bob*>(getAllocator().create(BOBTYPE_BOB)); boost::filesystem::path filePath(file); if(filePath.has_filename()) item->setName(filePath.filename().string()); if(item->load(bob.getStream(), palette) != 0) { delete item; return 5; } // Item alloziieren und zuweisen items.clear(); items.push(item); return 0; }
/** * Kopierfunktion von @p ArchivInfo. * * @param[in] to Zielposition * @param[in] from Quellposition * @param[in] count Anzahl * @param[in] source Quelle * * @author FloSoft */ void ArchivInfo::copy(size_t to, size_t from, size_t count, const ArchivInfo& source) { if(to + count > size()) data.resize(to + count); for(size_t f = from; f < from + count; ++to, ++f) setC(to, *source.get(f)); }
/** * lädt eine GER/ENG-File in ein ArchivInfo. * * @param[in] file Dateiname der GER/ENG-File * @param[out] items ArchivInfo-Struktur, welche gefüllt wird * @param[in] conversion Soll ggf. OEM-Charset in ANSI umgewandelt werden? * * @return Null bei Erfolg, ein Wert ungleich Null bei Fehler * * @bug Keine Erkennung ob Plain-Text oder "Irgendwas". */ int libsiedler2::loader::LoadTXT(const std::string& file, ArchivInfo& items, bool conversion) { short header; if(file.empty()) return 1; // Datei zum lesen öffnen boost::iostreams::mapped_file_source mmapFile; try{ mmapFile.open(bfs::path(file)); }catch(std::exception& e){ std::cerr << "Could not open '" << file << "': " << e.what() << std::endl; return 2; } typedef boost::iostreams::stream<boost::iostreams::mapped_file_source> MMStream; MMStream mmapStream(mmapFile); libendian::EndianIStream<false, MMStream& > fs(mmapStream); // hat das geklappt? if(!fs) return 2; size_t length = getIStreamSize(fs.getStream()); assert(length < std::numeric_limits<unsigned>::max()); // Header einlesen fs >> header; items.clear(); // ist es eine TXT-File? (Header 0xE7FD) if( header != (short)0xFDE7 ) { // den Header zurückspringen fs.setPositionRel(-2); ArchivItem_Text* item = (ArchivItem_Text*)getAllocator().create(BOBTYPE_TEXT); item->load(fs.getStream(), conversion); items.push(item); } else { // "archiviert" unsigned short count, unknown; unsigned int size; fs >> count; fs >> unknown; fs >> size; if(size == 0) size = static_cast<unsigned>(length); else size += 10; std::vector<unsigned> starts(count); // Starts einlesen for(unsigned short x = 0; x < count; ++x) { uint32_t s; fs >> s; if(s != 0) starts[x] = s + 10; } // Daten einlesen, zwecks Längenbestimmung size_t pos = fs.getPosition(); size_t rest = size - pos; std::vector<char> buffer(rest + 1); buffer.resize(rest); fs >> buffer; buffer.push_back(0); for(unsigned short x = 0; x < count; ++x) { unsigned i = starts[x]; if(i != 0) { // An Start springen fs.setPosition(i); // einlesen ArchivItem_Text* item = (ArchivItem_Text*)getAllocator().create(BOBTYPE_TEXT); assert(i >= pos); item->load(fs.getStream(), conversion, (unsigned int)strlen(&buffer[i - pos]) + 1); items.push(item); } else items.push(NULL); } } // alles ok return 0; }
/** * lädt eine BBM-File in ein ArchivInfo. * * @param[in] file Dateiname der BBM-File * @param[out] items ArchivInfo-Struktur, welche gefüllt wird * * @return Null bei Erfolg, ein Wert ungleich Null bei Fehler * * @author FloSoft * @author OLiver */ int libsiedler2::loader::LoadBBM(const std::string& file, ArchivInfo& items) { char header[4], pbm[4]; unsigned int chunk; unsigned int i = 0; if(file.empty()) return 1; // Datei zum lesen öffnen boost::scoped_ptr<FILE> bbm(fopen(file.c_str(), "rb")); // hat das geklappt? if(!bbm) return 2; long size = getFileLength(bbm.get()); // Header einlesen if(libendian::le_read_c(header, 4, bbm.get()) != 4) return 3; // ist es eine BBM-File? (Header "FORM") if(strncmp(header, "FORM", 4) != 0) return 4; // Länge einlesen unsigned length; if(libendian::le_read_ui(&length, bbm.get()) != 0) return 5; // Typ einlesen if(libendian::le_read_c(pbm, 4, bbm.get()) != 4) return 6; // ist es eine BBM-File? (Typ "PBM ") if(strncmp(pbm, "PBM ", 4) != 0) return 7; // Chunks einlesen while(!feof(bbm.get()) && ftell(bbm.get()) < size) { // Chunk-Typ einlesen if(libendian::be_read_ui(&chunk, bbm.get()) != 0) return 8; switch(chunk) { case 0x434D4150: // "CMAP" { // Länge einlesen if(libendian::be_read_ui(&length, bbm.get()) != 0) return 9; // Bei ungerader Zahl aufrunden if(length & 1) ++length; // Ist Länge wirklich so groß wie Farbtabelle? if(length != 256 * 3) return 10; // Daten von Item auswerten ArchivItem_Palette* palette = (ArchivItem_Palette*)getAllocator().create(BOBTYPE_PALETTE); items.push(palette); size_t namePos = file.find_last_of('/'); if(namePos != std::string::npos) { std::stringstream rName; rName << file.substr(namePos+1) << "(" << i << ")"; palette->setName(rName.str()); } // Farbpalette lesen Color colors[256]; if(libendian::le_read_uc(&colors[0].r, sizeof(colors), bbm.get()) != sizeof(colors)) return 10; // Farbpalette zuweisen for(unsigned int k = 0; k < 256; ++k) palette->set(k, colors[k]); ++i; } break; default: { // Länge einlesen if(libendian::be_read_ui(&length, bbm.get()) != 0) return 12; // Bei ungerader Zahl aufrunden if(length & 1) ++length; if(length > 0) { boost::scoped_array<unsigned char> buffer(new unsigned char[length]); // überspringen if(libendian::le_read_uc(buffer.get(), length, bbm.get()) != (int)length) return 13; } } break; } } if(items.size() == 0) return 14; // alles ok return 0; }
void unpack(const string& directory, const ArchivInfo& lst, const ArchivItem_Palette* palette) { CreateDirectoryA(directory.c_str(), NULL); for(unsigned int i = 0; i < lst.getCount(); ++i) { const ArchivItem* item = lst.get(i); bool changed = false; stringstream newfile; newfile << directory << "\\" << i << "."; if(!item) { /*newfile << "empty"; FILE *n = fopen(newfile.str().c_str(), "wb"); if(n) fclose(n);*/ continue; } switch(item->getBobType()) { case BOBTYPE_SOUND: // WAVs, MIDIs { unsigned short subtype = SOUNDTYPE_NONE; const ArchivItem_Sound* i = dynamic_cast<const ArchivItem_Sound*>(item); if(item) subtype = i->getType(); switch(subtype) { case SOUNDTYPE_NONE: { } break; case SOUNDTYPE_MIDI: // MIDI { } break; case SOUNDTYPE_WAVE: // WAV { newfile << "wav"; cout << "extracting " << newfile.str() << ": "; const ArchivItem_Sound_Wave* wave = dynamic_cast<const ArchivItem_Sound_Wave*>(item); FILE* fwave = fopen(newfile.str().c_str(), "wb"); if(fwave && wave && wave->write(fwave, false) == 0) { fclose(fwave); cout << "done" << endl; } else cout << "failed" << endl; } break; case SOUNDTYPE_XMIDI: // XMIDI { } break; case SOUNDTYPE_OTHER: // Andere { } break; } } break; case BOBTYPE_FONT: // Font { cout << "extracting " << newfile.str() << ": "; const ArchivItem_Font* font = dynamic_cast<const ArchivItem_Font*>(item); newfile << "dx" << (short)font->getDx() << ".dy" << (short)font->getDy() << "."; newfile << "fon"; unpack(newfile.str(), *font, palette); } break; case BOBTYPE_PALETTE: // Palette { ArchivInfo items; items.pushC(item); newfile << "bbm"; cout << "extracting " << newfile.str() << ": "; if(WriteBBM(newfile.str().c_str(), &items) != 0) cout << "failed" << endl; else cout << "done" << endl; } break; case BOBTYPE_BOB: // Bobfiles { const ArchivItem_Bob* bob = dynamic_cast<const ArchivItem_Bob*>(item); unpack(directory, bob, palette); } break; case BOBTYPE_MAP: // Mapfiles { } break; case BOBTYPE_TEXT: // Text { } break; case BOBTYPE_RAW: // Raw-Item { } break; case BOBTYPE_MAP_HEADER: // Mapheader-Item { } break; case BOBTYPE_BITMAP_RLE: // RLE komprimiertes Bitmap { if(!changed) { newfile << "rle."; changed = true; } } case BOBTYPE_BITMAP_PLAYER: // Bitmap mit spezifischer Spielerfarbe { if(!changed) { newfile << "player."; changed = true; } } case BOBTYPE_BITMAP_SHADOW: // Schatten { if(!changed) { newfile << "shadow."; changed = true; } } case BOBTYPE_BITMAP_RAW: // unkomprimiertes Bitmap { const ArchivItem_Bitmap* bitmap = dynamic_cast<const ArchivItem_Bitmap*>(item); ArchivInfo items; items.pushC(bitmap); newfile << "nx" << bitmap->getNx() << ".ny" << bitmap->getNy() << "."; newfile << "bmp"; cout << "extracting " << newfile.str() << ": "; if(WriteBMP(newfile.str().c_str(), palette, &items) != 0) cout << "failed" << endl; else cout << "done" << endl; } break; } } }