diff --git a/include/beatsaber-impl/beatlevelimpl.h b/include/beatsaber-impl/beatlevelimpl.h index fff2dbf..00005f1 100644 --- a/include/beatsaber-impl/beatlevelimpl.h +++ b/include/beatsaber-impl/beatlevelimpl.h @@ -22,6 +22,8 @@ protected: const json& base; + std::vector notes; + public: BeatLevelImpl(std::weak_ptr p, std::shared_ptr r, const json& j); virtual ~BeatLevelImpl(); @@ -34,6 +36,7 @@ public: virtual std::string getCustomData() const override; //TODO: get level content + virtual const std::vector& getNotes() const override; virtual void printDebug() const override; diff --git a/include/beatsaber-impl/beatnoteimpl.h b/include/beatsaber-impl/beatnoteimpl.h new file mode 100644 index 0000000..fde6aed --- /dev/null +++ b/include/beatsaber-impl/beatnoteimpl.h @@ -0,0 +1,12 @@ +#pragma once + +#include "beatnote.h" + +#include +using json = nlohmann::json; + +namespace Beatsaber { + +void from_json(const json& j, Note& n); + +} \ No newline at end of file diff --git a/include/beatsaber-impl/filereader.h b/include/beatsaber-impl/filereader.h index 811a973..a876b13 100644 --- a/include/beatsaber-impl/filereader.h +++ b/include/beatsaber-impl/filereader.h @@ -1,8 +1,9 @@ #pragma once +#include #include #include -#include +#include // this classes form a abstraction to read from folders and zipfiles the same way. Maybe add other sources too? @@ -15,6 +16,7 @@ public: virtual ~FileReader() {} virtual std::shared_ptr getFileStream(const std::string& filename) = 0; + virtual void getEntrys(std::vector& out) const = 0; }; } \ No newline at end of file diff --git a/include/beatsaber-impl/filereaderimpl.h b/include/beatsaber-impl/filereaderimpl.h index fb44d10..7b6a7f8 100644 --- a/include/beatsaber-impl/filereaderimpl.h +++ b/include/beatsaber-impl/filereaderimpl.h @@ -19,6 +19,7 @@ public: virtual ~FolderReader(); virtual std::shared_ptr getFileStream(const std::string& filename) override; + virtual void getEntrys(std::vector& out) const override; }; #if BEATSABERZIPSUPPORT == 1 @@ -32,6 +33,7 @@ public: virtual ~ZipReader(); virtual std::shared_ptr getFileStream(const std::string& filename) override; + virtual void getEntrys(std::vector& out) const override; }; #endif diff --git a/include/beatsaber/beatlevel.h b/include/beatsaber/beatlevel.h index 59c10a9..ad50eb9 100644 --- a/include/beatsaber/beatlevel.h +++ b/include/beatsaber/beatlevel.h @@ -2,7 +2,9 @@ #include #include +#include +#include "beatnote.h" #include "difficulties.h" namespace Beatsaber { @@ -24,6 +26,7 @@ public: virtual std::string getCustomData() const = 0; //TODO: get level content + virtual const std::vector& getNotes() const = 0; virtual void printDebug() const = 0; diff --git a/include/beatsaber/beatnote.h b/include/beatsaber/beatnote.h new file mode 100644 index 0000000..c9e0c2c --- /dev/null +++ b/include/beatsaber/beatnote.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace Beatsaber { + +struct Note { + std::uint32_t time; //time in beats when the note reaches the player + std::uint_fast8_t line; //colum, 0 = most left, 3 = most right + std::uint_fast8_t layer; //layer 0 = bottom, 2 = top + std::uint_fast8_t type; //0 = rednote, 1 = bluenote, 2 = unused, 3 = bomb + std::uint_fast8_t cutdir; +}; + +} \ No newline at end of file diff --git a/src/beatlevel.cpp b/src/beatlevel.cpp index 492be40..2e5fae2 100644 --- a/src/beatlevel.cpp +++ b/src/beatlevel.cpp @@ -4,6 +4,7 @@ #include #include "beatset.h" +#include "beatnoteimpl.h" namespace Beatsaber { @@ -18,9 +19,23 @@ BeatLevelImpl::BeatLevelImpl(std::weak_ptr p, std::shared_ptr stream = r->getFileStream(filename); - json json; - (*stream) >> json; - //TODO + json diffjson; + (*stream) >> diffjson; + + const std::string& version = diffjson["_version"]; + if(version != "2.0.0") { + //not supported + return; + } + + //load notes + const json& jnotes = diffjson["_notes"]; + if(jnotes.is_array()) { + notes.reserve(jnotes.size()); + for(const json& note : jnotes) { + notes.push_back(note); + } + } } catch(...) { std::cout << "Could not load difficulty: " << filename << std::endl; } @@ -56,11 +71,16 @@ std::string BeatLevelImpl::getCustomData() const { return ""; } +const std::vector& BeatLevelImpl::getNotes() const { + return notes; +} + void BeatLevelImpl::printDebug() const { std::cout << " Difficulty: " << Difficulty::toString(dif) << " (" << diffRank << ")" << "\n Filename: " << getFilename() << "\n njs: " << getNoteJumpSpeed() << " nso: " << getNoteStartOffset() + << "\n noteCount: " << notes.size() << std::endl; } diff --git a/src/beatmap.cpp b/src/beatmap.cpp index 3568c10..88bbeba 100644 --- a/src/beatmap.cpp +++ b/src/beatmap.cpp @@ -9,7 +9,7 @@ namespace Beatsaber { bool BeatMapImpl::load() { // try to open info.dat - std::shared_ptr stream = reader->getFileStream("info.dat"); + std::shared_ptr stream = reader->getFileStream("Info.dat"); try { (*stream) >> infobase; diff --git a/src/beatnote.cpp b/src/beatnote.cpp new file mode 100644 index 0000000..191a330 --- /dev/null +++ b/src/beatnote.cpp @@ -0,0 +1,15 @@ +#include "beatnoteimpl.h" + +#include + +namespace Beatsaber { + +void from_json(const json& j, Note& n) { + n.time = j["_time"]; + n.line = j["_lineIndex"]; + n.layer = j["_lineLayer"]; + n.type = j["_type"]; + n.cutdir = j["_cutDirection"]; +} + +} \ No newline at end of file diff --git a/src/filereader.cpp b/src/filereader.cpp index 6ff8a55..3a0923c 100644 --- a/src/filereader.cpp +++ b/src/filereader.cpp @@ -1,9 +1,19 @@ #include "filereaderimpl.h" +#include #include +#include //c++17 required namespace Beatsaber { +static const std::string* findbest(const std::vector& haystack, const std::string& needle) { + for(const auto& it : haystack) { + if(it == needle) return ⁢ + if(strcasecmp(it.c_str(), needle.c_str()) == 0) return ⁢ + } + return nullptr; +} + FolderReader::FolderReader(const std::string& path) : path(path) { // make path end with / @@ -13,10 +23,29 @@ FolderReader::FolderReader(const std::string& path) : path(path) { FolderReader::~FolderReader() {} std::shared_ptr FolderReader::getFileStream(const std::string& filename) { - std::shared_ptr stream = std::make_shared(path + filename); + std::shared_ptr stream = std::make_shared(path + filename); + if(!stream->is_open()) { + //search for other files + std::vector entrys; + getEntrys(entrys); + const std::string* best = findbest(entrys, filename); + if(best) { + return std::make_shared(path + *best); + } + } return stream; } +void FolderReader::getEntrys(std::vector& out) const { + for(const auto& i : std::filesystem::directory_iterator(path)) { + if(i.is_regular_file() && i.exists()) { + std::string filename = i.path().filename(); + if(!filename.empty() && filename[0] != '.') { //ignore dotfiles + out.push_back(filename); + } + } + } +} #if BEATSABERZIPSUPPORT == 1 @@ -27,7 +56,29 @@ ZipReader::~ZipReader() { } std::shared_ptr ZipReader::getFileStream(const std::string& filename) { - return file.getInputStream(filename); + std::shared_ptr stream = file.getInputStream(filename); + + if(stream) { + //search for other files + std::vector entrys; + getEntrys(entrys); + const std::string* best = findbest(entrys, filename); + if(best) { + return file.getInputStream(*best); + } + } + + return stream; +} + +void ZipReader::getEntrys(std::vector& out) const { + const auto entr = file.entries(); + out.reserve(entr.size()); + for(auto it : entr) { + if(!it->isDirectory() && it->isValid()) { + out.push_back(it->getFileName()); + } + } } #endif diff --git a/tests/main.cpp b/tests/main.cpp index d78680f..c88f394 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -33,6 +33,9 @@ int main(int argc, char** argv) { std::shared_ptr bmap = Beatsaber::BeatMap::loadFromFolder("/home/yannis/Nextcloud/yannis/Beatsaber/BeatSaverLevel/1dd (Portal - Still Alive (Uppermost Remix) - kryptikos)"); if(!bmap) std::cout << "Could not load File" << std::endl; else bmap->printDebug(); + bmap = Beatsaber::BeatMap::loadFromZip("/home/yannis/Nextcloud/yannis/Beatsaber/BeatSaverLevel/18b92 (Empress of Light - ShadowLantern).zip"); + if(!bmap) std::cout << "Could not load File" << std::endl; + else bmap->printDebug(); return failcount > 0; }