code:
#ifndef UTIL3DS_TWEENABLEOBJECTS_H
#define UTIL3DS_TWEENABLEOBJECTS_H

#include <TweenEngine/Tweenable.h>
#include <cpp3ds/Graphics/Transformable.hpp>
#include <cpp3ds/Graphics/Sprite.hpp>
#include <cpp3ds/Graphics/Text.hpp>
#include <cpp3ds/Graphics/Shape.hpp>
#include <cpp3ds/Graphics/RectangleShape.hpp>
#include <cpp3ds/Graphics/CircleShape.hpp>
#include <cpp3ds/Graphics/ConvexShape.hpp>
#include "GUI/NinePatch.hpp"

namespace util3ds {

template <class T>
class TweenTransformable: public T, public TweenEngine::Tweenable
{
public:
	static const int POSITION_X  = 1;
	static const int POSITION_Y  = 2;
	static const int POSITION_XY = 3;
	static const int ROTATION    = 4;
	static const int SCALE_X     = 5;
	static const int SCALE_Y     = 6;
	static const int SCALE_XY    = 7;
	static const int ORIGIN_X    = 8;
	static const int ORIGIN_Y    = 9;
	static const int ORIGIN_XY   = 10;

protected:
	virtual int getValues(int tweenType, float *returnValues)
	{
		switch (tweenType) {
			case POSITION_X: returnValues[0] = T::getPosition().x; return 1;
			case POSITION_Y: returnValues[0] = T::getPosition().y; return 1;
			case POSITION_XY:
				returnValues[0] = T::getPosition().x;
				returnValues[1] = T::getPosition().y;
				return 2;
			case ROTATION: returnValues[0] = T::getRotation(); return 1;
			case SCALE_X: returnValues[0] = T::getScale().x; return 1;
			case SCALE_Y: returnValues[0] = T::getScale().y; return 1;
			case SCALE_XY:
				returnValues[0] = T::getScale().x;
				returnValues[1] = T::getScale().y;
				return 2;
			case ORIGIN_X: returnValues[0] = T::getOrigin().x; return 1;
			case ORIGIN_Y: returnValues[0] = T::getOrigin().y; return 1;
			case ORIGIN_XY:
				returnValues[0] = T::getOrigin().x;
				returnValues[1] = T::getOrigin().y;
				return 2;
			default: return -1;
		}
	}

	virtual void setValues(int tweenType, float *newValues)
	{
		switch (tweenType) {
			case POSITION_X: T::setPosition(newValues[0], T::getPosition().y); break;
			case POSITION_Y: T::setPosition(T::getPosition().x, newValues[0]); break;
			case POSITION_XY: T::setPosition(newValues[0], newValues[1]); break;
			case ROTATION: T::setRotation(newValues[0]); break;
			case SCALE_X: T::setScale(newValues[0], T::getScale().y); break;
			case SCALE_Y: T::setScale(T::getScale().x, newValues[0]); break;
			case SCALE_XY: T::setScale(newValues[0], newValues[1]); break;
			case ORIGIN_X: T::setOrigin(newValues[0], T::getOrigin().y); break;
			case ORIGIN_Y: T::setOrigin(T::getOrigin().x, newValues[0]); break;
			case ORIGIN_XY: T::setOrigin(newValues[0], newValues[1]); break;
			default:
				break;
		}
	}
};

template <class T>
class TweenShape: public TweenTransformable<T>
{
public:
	static const int FILL_COLOR_RGB      = 11;
	static const int FILL_COLOR_ALPHA    = 12;
	static const int OUTLINE_COLOR_RGB   = 13;
	static const int OUTLINE_COLOR_ALPHA = 14;
	static const int OUTLINE_THICKNESS   = 15;

protected:
	virtual int getValues(int tweenType, float *returnValues)
	{
		switch (tweenType) {
			case FILL_COLOR_RGB: {
				cpp3ds::Color color = T::getFillColor();
				returnValues[0] = color.r;
				returnValues[1] = color.g;
				returnValues[2] = color.b;
				return 3;
			}
			case OUTLINE_COLOR_RGB: {
				cpp3ds::Color color = T::getOutlineColor();
				returnValues[0] = color.r;
				returnValues[1] = color.g;
				returnValues[2] = color.b;
				return 3;
			}
			case FILL_COLOR_ALPHA: returnValues[0] = T::getFillColor().a; return 1;
			case OUTLINE_COLOR_ALPHA: returnValues[0] = T::getOutlineColor().a; return 1;
			case OUTLINE_THICKNESS: returnValues[0] = T::getOutlineThickness(); return 1;
			default:
				return TweenTransformable<T>::getValues(tweenType, returnValues);
		}
	}

	virtual void setValues(int tweenType, float *newValues)
	{
		switch (tweenType) {
			case FILL_COLOR_RGB: {
				cpp3ds::Color color;
				color.r = std::max(std::min(newValues[0], 255.f), 0.f);
				color.g = std::max(std::min(newValues[1], 255.f), 0.f);
				color.b = std::max(std::min(newValues[2], 255.f), 0.f);
				color.a = T::getFillColor().a;
				T::setFillColor(color);
				break;
			}
			case FILL_COLOR_ALPHA: {
				cpp3ds::Color color = T::getFillColor();
				color.a = std::max(std::min(newValues[0], 255.f), 0.f);
				T::setFillColor(color);
				break;
			}
			case OUTLINE_COLOR_RGB: {
				cpp3ds::Color color;
				color.r = std::max(std::min(newValues[0], 255.f), 0.f);
				color.g = std::max(std::min(newValues[1], 255.f), 0.f);
				color.b = std::max(std::min(newValues[2], 255.f), 0.f);
				color.a = T::getOutlineColor().a;
				T::setOutlineColor(color);
				break;
			}
			case OUTLINE_COLOR_ALPHA: {
				cpp3ds::Color color = T::getOutlineColor();
				color.a = std::max(std::min(newValues[0], 255.f), 0.f);
				T::setOutlineColor(color);
				break;
			}
			case OUTLINE_THICKNESS: T::setOutlineThickness(newValues[0]); break;
			default:
				TweenTransformable<T>::setValues(tweenType, newValues);
		}
	}
};

template <class T>
class TweenColorTransformable : public TweenTransformable<T>
{
public:
	static const int COLOR_RGB   = 11;
	static const int COLOR_ALPHA = 12;

protected:
	virtual int getValues(int tweenType, float *returnValues)
	{
		switch (tweenType) {
			case COLOR_RGB: {
				cpp3ds::Color color = T::getColor();
				returnValues[0] = color.r;
				returnValues[1] = color.g;
				returnValues[2] = color.b;
				return 3;
			}
			case COLOR_ALPHA: returnValues[0] = T::getColor().a; return 1;
			default:
				return TweenTransformable<T>::getValues(tweenType, returnValues);
		}
	}

	virtual void setValues(int tweenType, float *newValues)
	{
		switch (tweenType) {
			case COLOR_RGB: {
				cpp3ds::Color color;
				color.r = std::max(std::min(newValues[0], 255.f), 0.f);
				color.g = std::max(std::min(newValues[1], 255.f), 0.f);
				color.b = std::max(std::min(newValues[2], 255.f), 0.f);
				color.a = T::getColor().a;
				T::setColor(color);
				break;
			}
			case COLOR_ALPHA: {
				cpp3ds::Color color = T::getColor();
				color.a = std::max(std::min(newValues[0], 255.f), 0.f);
				T::setColor(color);
				break;
			}
			default:
				TweenTransformable<T>::setValues(tweenType, newValues);
				break;
		}
	}
};

template <class T>
class TweenShapeResizable : public TweenShape<T>
{
public:
	static const int SIZE = 16;

protected:
	virtual int getValues(int tweenType, float *returnValues)
	{
		switch (tweenType) {
			case SIZE: {
				returnValues[0] = T::getSize().x;
				returnValues[1] = T::getSize().y;
				return 2;
			}
			default:
				return TweenShape<T>::getValues(tweenType, returnValues);
		}
	}

	virtual void setValues(int tweenType, float *newValues)
	{
		switch (tweenType) {
			case SIZE: T::setSize(cpp3ds::Vector2f(newValues[0], newValues[1])); break;
			default:
				TweenShape<T>::setValues(tweenType, newValues);
				break;
		}
	}
};

template <class T>
class TweenColorTransformableResizable : public TweenColorTransformable<T>
{
public:
	static const int SIZE = 13;

protected:
	virtual int getValues(int tweenType, float *returnValues)
	{
		switch (tweenType) {
			case SIZE: {
				returnValues[0] = T::getSize().x;
				returnValues[1] = T::getSize().y;
				return 2;
			}
			default:
				return TweenColorTransformable<T>::getValues(tweenType, returnValues);
		}
	}

	virtual void setValues(int tweenType, float *newValues)
	{
		switch (tweenType) {
			case SIZE: T::setSize(cpp3ds::Vector2f(newValues[0], newValues[1])); break;
			default:
				TweenColorTransformable<T>::setValues(tweenType, newValues);
				break;
		}
	}
};

template <class T>
class TweenShapeText: public TweenShape<T>
{
public:
	static const int CHARACTER_SIZE      = 16;

protected:
	virtual int getValues(int tweenType, float *returnValues)
	{
		switch (tweenType) {
			case CHARACTER_SIZE: returnValues[0] = T::getCharacterSize(); return 1;
			default:
				return TweenShape<T>::getValues(tweenType, returnValues);
		}
	}

	virtual void setValues(int tweenType, float *newValues)
	{
		switch (tweenType) {
			case CHARACTER_SIZE: T::setCharacterSize(newValues[0]); break;
			default:
				TweenShape<T>::setValues(tweenType, newValues);
		}
	}
};

template <class T>
class TweenView: public T, public TweenEngine::Tweenable
{
public:
	static const int SIZE_X    = 1;
	static const int SIZE_Y    = 2;
	static const int SIZE_XY   = 3;
	static const int CENTER_X  = 4;
	static const int CENTER_Y  = 5;
	static const int CENTER_XY = 6;

protected:
	virtual int getValues(int tweenType, float *returnValues)
	{
		switch (tweenType) {
			case SIZE_X: returnValues[0] = T::getSize().x; return 1;
			case SIZE_Y: returnValues[0] = T::getSize().y; return 1;
			case SIZE_XY:
				returnValues[0] = T::getSize().x;
				returnValues[1] = T::getSize().y;
				return 2;
			case CENTER_X: returnValues[0] = T::getCenter().x; return 1;
			case CENTER_Y: returnValues[0] = T::getCenter().y; return 1;
			case CENTER_XY:
				returnValues[0] = T::getCenter().x;
				returnValues[1] = T::getCenter().y;
				return 2;
			default: return -1;
		}
	}

	virtual void setValues(int tweenType, float *newValues)
	{
		switch (tweenType) {
			case SIZE_X: T::setSize(newValues[0], T::getSize().y); break;
			case SIZE_Y: T::setSize(T::getSize().x, newValues[0]); break;
			case SIZE_XY: T::setSize(newValues[0], newValues[1]); break;
			case CENTER_X: T::setCenter(newValues[0], T::getCenter().y); break;
			case CENTER_Y: T::setCenter(T::getCenter().x, newValues[0]); break;
			case CENTER_XY: T::setCenter(newValues[0], newValues[1]); break;
			default:
				break;
		}
	}
};

typedef TweenColorTransformableResizable<gui3ds::NinePatch> TweenNinePatch;

typedef TweenColorTransformable<cpp3ds::Sprite> TweenSprite;
typedef TweenShapeText<cpp3ds::Text> TweenText;

typedef TweenShapeResizable<cpp3ds::RectangleShape> TweenRectangleShape;
typedef TweenShape<cpp3ds::CircleShape> TweenCircleShape;
typedef TweenShape<cpp3ds::ConvexShape> TweenConvexShape;

typedef TweenView<cpp3ds::View> TweenableView;

} // namespace util3ds

#endif // UTIL3DS_TWEENABLEOBJECTS_H


code:
#include "Util.hpp"
#include <dirent.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <cpp3ds/System/FileSystem.hpp>
#include <cpp3ds/System/FileInputStream.hpp>
#include <cpp3ds/System/I18n.hpp>
#include <inttypes.h>
#include <fstream>

namespace Template
{

bool pathExists(const char* path, bool escape)
{
	struct stat buffer;
	if (escape)
		return stat(cpp3ds::FileSystem::getFilePath(path).c_str(), &buffer) == 0;
	else
		return stat(path, &buffer) == 0;
}

bool fileExists (const std::string& name) {
	struct stat buffer;
	return (stat (name.c_str(), &buffer) == 0);
}

void makeDirectory(const char *dir, mode_t mode)
{
	char tmp[256];
	char *p = NULL;
	size_t len;

	snprintf(tmp, sizeof(tmp),"%s",dir);
	len = strlen(tmp);
	if(tmp[len - 1] == '/')
		tmp[len - 1] = 0;
	for(p = tmp + 1; *p; p++)
		if(*p == '/') {
			*p = 0;
			mkdir(tmp, mode);
			*p = '/';
		}
	mkdir(tmp, mode);
}

int removeDirectory(const char *path, bool onlyIfEmpty)
{
	DIR *d = opendir(path);
	size_t path_len = strlen(path);
	int r = -1;

	if (d) {
		struct dirent *p;
		r = 0;

		while (!r && (p = readdir(d))) {
			int r2 = -1;
			char *buf;
			size_t len;

			/* Skip the names "." and ".." as we don't want to recurse on them. */
			if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, ".."))
				continue;

			len = path_len + strlen(p->d_name) + 2;
			buf = (char*) malloc(len);

			if (buf) {
				struct stat sb;
				snprintf(buf, len, "%s/%s", path, p->d_name);

				if (!stat(buf, &sb)) {
					if (S_ISDIR(sb.st_mode))
						r2 = removeDirectory(buf, onlyIfEmpty);
					else if (!onlyIfEmpty)
						r2 = unlink(buf);
				}
				free(buf);
			}
			r = r2;
		}
		closedir(d);
	}
	if (!r)
		r = rmdir(path);

	return r;
}

void hexToRGB(std::string hexValue, int *R, int *G, int *B)
{
	std::string strR, strG, strB;

	strR = hexValue.substr(0, 2);
	strG = hexValue.substr(2, 2);
	strB = hexValue.substr(4, 2);

	std::istringstream(strR) >> std::hex >> *R;
	std::istringstream(strG) >> std::hex >> *G;
	std::istringstream(strB) >> std::hex >> *B;
}

std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) {
	size_t start_pos = 0;
	while((start_pos = str.find(from, start_pos)) != std::string::npos) {
		str.replace(start_pos, from.length(), to);
		start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
	}
	return str;
}

cpp3ds::String getUsername()
{
#ifndef EMULATION
	if (R_SUCCEEDED(cfguInit())) {
		u16 *userOut = static_cast<u16*>(malloc(0x1C));
		CFGU_GetConfigInfoBlk2(0x1C, 0xA0000, (u8*)userOut);

		cfguExit();

		cpp3ds::String username = cpp3ds::String::fromUtf16(userOut, userOut + 0x1C);
		free(userOut);

		return username;
	}
#else
	return "emu3ds";
#endif
}

} // namespace Template

license: unknown


code:
#ifndef TEMPLATE_UTIL_HPP
#define TEMPLATE_UTIL_HPP

#include <sys/stat.h>
#include <string>
#include <cpp3ds/Network/Http.hpp>
#include <cpp3ds/System/String.hpp>
#ifndef EMULATION
#include <3ds.h>
#endif

namespace Template
{

bool pathExists(const char* path, bool escape = true);
bool fileExists (const std::string& name);
void makeDirectory(const char *dir, mode_t mode = 0777);
int removeDirectory(const char *path, bool onlyIfEmpty = false);
void hexToRGB(std::string hexValue, int *R, int *G, int *B);
std::string ReplaceAll(std::string str, const std::string& from, const std::string& to);
cpp3ds::String getUsername();

} // namespace Template

#endif // TEMPLATE_UTIL_HPP


code:
#include "Template.hpp"

#ifndef EMULATION
#include <3ds.h>
#endif

#ifndef EMULATION
namespace {

aptHookCookie cookie;

void aptHookFunc(APT_HookType hookType, void *param)
{
	switch (hookType) {
		case APTHOOK_ONSUSPEND:
			// Fall through
		case APTHOOK_ONSLEEP:
			break;
		case APTHOOK_ONRESTORE:
		case APTHOOK_ONWAKEUP:
			break;
		case APTHOOK_ONEXIT:
			break;
		default:
			break;
	}
}

}
#endif

int main(int argc, char** argv)
{
#ifndef NDEBUG
	// Console for reading stdout
	cpp3ds::Console::enable(cpp3ds::BottomScreen, cpp3ds::Color::Black);
#endif

	cpp3ds::Service::enable(cpp3ds::Audio);
	cpp3ds::Service::enable(cpp3ds::Config);
	cpp3ds::Service::enable(cpp3ds::Network);
	cpp3ds::Service::enable(cpp3ds::SSL);
	cpp3ds::Service::enable(cpp3ds::Httpc);
	cpp3ds::Service::enable(cpp3ds::AM);

#ifndef EMULATION
	aptHook(&cookie, aptHookFunc, nullptr);
#endif

	srand(time(NULL));
	auto game = new Template::Template();
	game->run();

	delete game;
	return 0;
}


code:
enable_testing()
find_package(GTest REQUIRED)

list(REMOVE_ITEM SOURCE_FILES ${PROJECT_SOURCE_DIR}/src/main.cpp)
list(REMOVE_ITEM CPP3DS_EMU_LIBS cpp3ds-emu)

set(TEST_SOURCE_FILES
    ${PROJECT_SOURCE_DIR}/test/main.cpp
)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/bin)

include_directories(
    ${PROJECT_SOURCE_DIR}/include
    ${CPP3DS}/include
    ${GTEST_INCLUDE_DIRS}
)

link_directories(
    ${CPP3DS}/lib
)

add_executable(${PROJECT_NAME}-tests ${TEST_SOURCE_FILES} ${SOURCE_FILES} ${EMU_SOURCE_FILES})
target_link_libraries(${PROJECT_NAME}-tests ${GTEST_BOTH_LIBRARIES} cpp3ds-test ${CPP3DS_EMU_LIBS})

set_target_properties(${PROJECT_NAME}-tests PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${TEST_FLAGS} -std=c++11")
set_target_properties(${PROJECT_NAME}-tests PROPERTIES COMPILE_DEFINITIONS "EMULATION;TEST")
set_target_properties(${PROJECT_NAME}-tests PROPERTIES LINK_FLAGS "${CMAKE_CXX_FLAGS} ${TEST_FLAGS}")


code:
#include "gtest/gtest.h"

TEST(Test, EqualityCheck){
	EXPECT_EQ(true, true);
}


作者: 小橙子

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+详情：&&</div>

导演: 导演：&&</li>

主演: 主演：&&</li>

影片状态: 状态：&&</li>

影片类型: data vodTag">&&</li>

数组: list-width volistwidthb">&&</li>

标题: title="&&"

副标题: list-remarks hidden">&&</span>

图片: data-original="&&"

搜索数组: img lazy eclazy br"&&</a>

搜索标题: title="&&"

搜索副标题: list-remarks hidden">&&</span>

搜索图片: data-original="&&"

repo_name: xcz/xcz


站名: TVB云播

请求头: 手机

影片类型: 分类：&&主演

导演: 导演：&&

主演: 主演：&&

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+class="sketch content">&&</span>

副标题: pic-tag pic-tag-top">&&</span>

搜索数组: myui-vodlist__thumb img-lg-150 img-md-150 img-sm-150 img-xs-100 lazyload"&&</a>

线路标题: 🍊小橙子接口🍊+title">&&<

类型

key: cateId

n: 全部类型

n: 动作片

n: 喜剧片

n: 爱情片

n: 科幻片

n: 恐怖片

n: 剧情片

n: 战争片

剧情

key: class

n: 全部剧情

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 恐怖

v: 恐怖

n: 动作

v: 动作

n: 科幻

v: 科幻

n: 剧情

v: 剧情

n: 战争

v: 战争

n: 警匪

v: 警匪

n: 犯罪

v: 犯罪

n: 动画

v: 动画

n: 奇幻

v: 奇幻

n: 武侠

v: 武侠

n: 冒险

v: 冒险

n: 枪战

v: 枪战

n: 悬疑

v: 悬疑

n: 惊悚

v: 惊悚

n: 经典

v: 经典

n: 青春

v: 青春

n: 文艺

v: 文艺

n: 微电影

v: 微电影

n: 古装

v: 古装

n: 历史

v: 历史

n: 运动

v: 运动

n: 农村

v: 农村

n: 儿童

v: 儿童

n: 网络电影

v: 网络电影

地区

key: area

n: 全部地区

n: 大陆

v: 大陆

n: 香港

v: 香港

n: 台湾

v: 台湾

n: 美国

v: 美国

n: 法国

v: 法国

n: 英国

v: 英国

n: 日本

v: 日本

n: 韩国

v: 韩国

n: 德国

v: 德国

n: 泰国

v: 泰国

n: 印度

v: 印度

n: 意大利


站名: 七月影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+剧情：&&aaa

影片状态: 集数：&&</div>

影片类型: video-tag-icon">&&立即播放

副标题: module-item-note">&&</div>

搜索数组: module-card-item-class">&&</a>

搜索副标题: module-item-note">&&</div>

线路标题: 🍊小橙子接口🍊+&&[替换:本地>>专线#（快）>>(推荐)#>>共]+集

线路数组: data-dropdown-value=&&</div>


站名: 55影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+<p class="col-pd"&&

线路数组: <h3 class="title">&&/h3>

线路标题: 🥚小橙子接口🥚+>&&<


站名: 农民影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+简        介：&&</p>

影片年代: 年代：&&</span>

标题: sTit"&&</span>

副标题: <span>&&<em>

图片: data-echo="&&"

搜索数组: <div class="pic">&&<em

搜索副标题: sStyle">&&</span>

搜索图片: data-src="&&"

线路数组: javascript:;"&&</li>[替换:默认>>🍊小橙子接口🍊农民线路][不包含:奇艺#腾讯#优酷#芒果]

线路标题: 🍊小橙子接口🍊+>&&</a>

类型

n: 全部类型

n: 动作片

n: 喜剧片

n: 爱情片

n: 科幻片

n: 恐怖片

n: 剧情片

n: 战争片

n: 惊悚片

n: 奇幻片

n: 新加坡

v: 新加坡

n: 马来西亚

v: 马来西亚

n: 加拿大

v: 加拿大

n: 西班牙

v: 西班牙

n: 俄罗斯

v: 俄罗斯

n: 其它

v: 其它

时间

key: year

n: 全部时间

排序

key: by

n: 全部排序

n: 时间排序

v: time

n: 人气排序

v: hits

n: 评分排序

v: score

类型

n: 国产剧

n: 港台泰

n: 日韩剧

n: 欧美剧


站名: 土豪影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+video-info-content vod_content"&&</span>

导演: 导演：&&</div>

主演: 主演：&&</div>

影片状态: video-info-item">&&</div>

影片类型: video-info-aux scroll-content"&&立即播放

图片: data-src="&&"

副标题: module-item-text">&&</div>

线路标题: 🍊小橙子接口🍊+&&[替换:蓝光一线>>蓝光#超清高速>>超清#>>共]+集

播放数组: sort-item"&&</div>

搜索数组: lazy lazyload"&&<h3>

n: 全部类型

n: 动作片

n: 喜剧片

n: 爱情片

n: 恐怖片

n: 犯罪片

n: 科幻片

n: 惊悚片

n: 战争片

n: 悬疑片

n: 灾难片

n: 冒险片

n: 剧情片

n: 武侠片

n: 动画片

n: 历史片

n: 传记片

n: 歌舞片

n: 其他片

剧情

n: 全部剧情

n: 动作

v: 动作

n: 警匪

v: 警匪

n: 恐怖

v: 恐怖

n: 科幻

v: 科幻

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 剧情

v: 剧情

n: 战争

v: 战争

n: 犯罪

v: 犯罪

n: 动画

v: 动画

n: 奇幻

v: 奇幻

n: 武侠

v: 武侠

n: 冒险

v: 冒险

n: 枪战

v: 枪战

n: 悬疑

v: 悬疑

n: 惊悚

v: 惊悚

n: 经典

v: 经典

n: 青春

v: 青春

n: 文艺

v: 文艺

n: 微电影

v: 微电影

n: 古装

v: 古装

n: 历史

v: 历史

n: 运动

v: 运动

n: 农村

v: 农村

n: 儿童

v: 儿童

n: 网络电影

v: 网络电影

地区

n: 全部地区

n: 大陆

v: 大陆

n: 香港

v: 香港

n: 台湾

v: 台湾

n: 美国

v: 美国

n: 法国


站点: 奇优影院

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+在线免费观看，&&</div>

影片类型: 类型：&&立即播放

副标题: text-right">&&</span>

搜索数组: v-thumb stui-vodlist__thumb&&</a>

搜索副标题: text-right">&&</span>

线路数组: data-toggle="tab"&&</li>[替换:播放源1>>🍊小橙子接口🍊线路①#播放源2>>🍊小橙子接口🍊线路②#播放源3>>🍊小橙子接口🍊线路③#播放源4>>🍊小橙子接口🍊线路④#播放源5>>🍊小橙子接口🍊线路⑤#播放源6>>🍊小橙子接口🍊线路⑥]

播放数组: stui-content__playlist clearfix&&</ul>

排序

n: 时间排序

v: time

n: 人气排序

v: score

n: 评分排序

v: hits


作者: 荷城茶秀

站名: vs影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+onclick="MyTheme.Layer.Text('剧情简介','&&','500','350')

线路数组: #playlist&&</li>[不包含:无尽#八戒][排序:秒播>爱坤>优质>量子]

线路标题: 🍊小橙子接口🍊+>&&</a>+线路


站名: 疯狗影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+该站点无剧情介绍，暂时无法提供简介…

数组: <li class="col-xs-4">&&/span>

副标题: continu">&&<

线路标题: 🍊小橙子接口🍊+&&

播放数组: <ul class="list-unstyled row&&


站名: 真不卡影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+detail-sketch">&&<a class="detail-more

影片类型: 类型：&&

副标题: pic-text text-right"&&</span>

线路数组: <h3 class="title"&&/h3>[不包含:W云#S云]

线路标题: 🍊小橙子接口🍊+>&&<

播放数组: column8 clearfix">&&</ul>

类型

n: 全部类型

n: 动作片

n: 喜剧片

n: 爱情片

n: 科幻片

n: 恐怖片

n: 剧情片

n: 战争片

n: 粤语片

n: 院线片

n: 纪录片

n: 青春片

n: 邵氏电影

剧情

n: 全部剧情

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 恐怖

v: 恐怖

n: 动作

v: 动作

n: 科幻

v: 科幻

n: 剧情

v: 剧情

n: 战争

v: 战争

n: 警匪

v: 警匪

n: 犯罪

v: 犯罪

n: 动画

v: 动画

n: 奇幻

v: 奇幻

n: 武侠

v: 武侠

n: 冒险

v: 冒险

n: 枪战

v: 枪战

n: 悬疑

v: 悬疑

n: 惊悚

v: 惊悚

n: 经典

v: 经典

n: 青春

n: 文艺

地区

v: 法国

n: 英国

v: 英国

n: 日本

v: 日本

n: 韩国

v: 韩国

n: 德国

v: 德国

n: 泰国

v: 泰国


站名: 蛋蛋影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+<p class="col-pd">&&蛋蛋剧不参与

影片类型: 类型：&&<span class="split-line">

影片地区: 地区：&&<span class="split-line">

数组: ewave-vodlist__thumb lazyload"&&</a>

线路数组: #playlist&&</li>


站名: 觅知影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+<p>&&</p>

影片类型: module-info-tag">&&立即播放

线路标题: 🍊小橙子接口🍊+&&[替换:资源>>空#>>共]+集


站名: 超前B站

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+<div class="summary detailsTxt"*>&&<div

导演: 暂无

标题: title=*>&&</div>

副标题: movie-rating cor4">&&</div>

搜索标题: title=*>&&</div>

搜索副标题: movie-rating cor4">&&<

线路数组: titleName cr3&&/a>


站名: 达达龟影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+none;">&&</span>

影片类型: 类型：&& /

搜索副标题: pic-text text-right"&&</span>

线路标题: 🍊小橙子接口🍊+>&&<[替换:自营云播>>自营#量子云播>>量子#非凡云播>>非凡]

播放数组: playlist clearfix">&&</ul>

剧情

n: 全部剧情

n: 喜剧

v: 喜剧

n: 爱情

v: 爱情

n: 恐怖

v: 恐怖

n: 动作

v: 动作

n: 科幻

v: 科幻

n: 剧情

v: 剧情

n: 战争

v: 战争

n: 警匪

v: 青春

v: 文艺

n: 微电影

v: 微电影

n: 古装

v: 古装

n: 历史

v: 历史

n: 运动

v: 运动

n: 农村

v: 农村

n: 网络电影

v: 网络电影

n: 全部地区

n: 大陆

v: 大陆

n: 香港

v: 香港

n: 台湾

v: 台湾

n: 美国

v: 美国

n: 法国

n: 印度

v: 印度

n: 意大利

v: 意大利

n: 西班牙

v: 西班牙

n: 加拿大

v: 加拿大

n: 其他

v: 其他

语言

key: lang

n: 全部语言

n: 国语

v: 国语

n: 英语

v: 英语


站名: 骚火影视

简介: 【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+<p class="p_txt show_part"&&

线路标题: 🍊小橙子接口🍊+>&&<[替换:1号线路>>骚火专线#2号线路>>骚火备用]


站名: 黑狐影视

简介:
【小橙子】提醒您请勿相信影片中的广告，以免上当受骗！欢迎进入小橙子影视。+剧情介绍
&&

影片地区: 地区：&&

影片年代: 年份：&&

搜索数组: stui-vodlist__thumb lazyload"&&</h3>


code:
# Music Hoarders Wiki

Created to support the daily conversations and activities of the hoarders.

## License

This work is licensed under the [CC Attribution-NonCommercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0).


code:
---
hide:
  - navigation
---

# FAQ

## How can I obtain a deleted track from SoundCloud?<br/>

Unless someone else has downloaded the track you are looking for before it was deleted, which is unlikely, you can't obtain deleted tracks from SoundCloud.

## Is there a SoundCloud archive?

No public SoundCloud archive exists.

## Where can I find music?

See [Reference➜Music Sources](/reference/music-sources).

## What formats does a streaming service provide?

See [Reference➜Streaming Services](/reference/streaming-services).

## Why are lossless formats usually preferred compared to lossy ones?

Whilst a good quality lossy file can sound transparent to the end user, some information is discarded during the encoding process. The goal of archival is to have a perfect copy of the original, and lossless file are used as no data is discarded as a space-saving measure. The lossless archival copy can also be used to generate different kinds of lossy files, for example for use in mobile devices. If only a lossy file is present, it cannot be re-encoded to a potential newer and better lossy format down the line, whereas a lossless file can.

## How can I check if a file is MQA?

Reencoding an MQA file removes the MQA syncword, which makes the file undetectable by MQA identifiers.

Tools that search for the MQA syncword: [MQA Identifier](https://github.com/purpl3F0x/MQA_identifier), [mqaid](https://github.com/redsudo/mqaid)


code:
# Version: 1.0

import os
import math
import time
import signal
import struct
import traceback
from mmap import mmap
from pathlib import Path, PurePath
from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter, ArgumentTypeError
from subprocess import Popen, PIPE
from multiprocessing import Pool

class Result():
	def __init__(self, path: str):
		self.path = path

class SuccessResult(Result, Exception):
	def __init__(self, path: str, savings: int):
		super().__init__(path)
		self.savings = savings

class ErrorResult(Result, Exception):
	def __init__(self, path: str, err: str):
		super().__init__(path)
		self.err = err.replace("\r\n", "\n").strip()

"""Utils"""

def type_folder(value: str):
	if not Path(value).is_dir():
		raise ArgumentTypeError(f"invalid folder path: {value}")
	return value

def walk(path: str):
	for path, _, files in os.walk(path):
		for file in files:
			if file.endswith(".flac"):
				yield PurePath(path).joinpath(file).as_posix()

def process(args: list[str]):
	proc = Popen(args, stdout=PIPE, stderr=PIPE)
	if proc.wait() == 0:
		return proc.stdout.read().decode().replace("\r\n", "\n")
	else:
		raise ErrorResult(None, proc.stderr.read().decode().replace("\r\n", "\n"))

"""Worker"""

def get_flac_info(path: str):
	with open(path, "r+b") as f:
		with mmap(f.fileno(), length=4096) as m:
			m.seek(18)
			if m[0] != 0x66 or m[1] != 0x4C or m[2] != 0x61 or m[3] != 0x43:
				raise ErrorResult(path, "Invalid FLAC file header")
			return {
				"sr": struct.unpack('>I', m.read(4))[0] >> 12
			}

def get_flac_size(path: str):
	return os.stat(path).st_size

def get_flac_md5(path: str):
	return process(["metaflac", "--show-md5sum", path]).split("\n")[0]

def work(path: str):
	if get_flac_info(path)["sr"] < args.samplerate:
		return None

	old_size = get_flac_size(path)
	old_md5 = get_flac_md5(path)

	tmp_path = path + ".enchires"
	process(["ffmpeg", "-hide_banner", "-i", path, "-f", "flac", "-compression_level", str(args.level), "-y", tmp_path])
	process(["metaflac", "--dont-use-padding", "--remove", "--block-type=PICTURE,PADDING", tmp_path])
	process(["metaflac", "--add-padding=8192", tmp_path])

	if old_md5 != get_flac_md5(path):
		return ErrorResult(path, "MD5 mismatched after processing")

	os.replace(tmp_path, path)
	return SuccessResult(path, old_size - get_flac_size(path))

def work_handler(path: str):
	try:
		return work(path)
	except SuccessResult as res:
		return res
	except ErrorResult as res:
		res.path = path
		return res
	except Exception as ex:
		return ErrorResult(path, "".join(traceback.format_exception(type(ex), ex, ex.__traceback__)))

def work_init():
	signal.signal(signal.SIGINT, signal.SIG_IGN)

"""Main"""

parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter, description="Encode hires (>=88.2kHz) flac with ffmpeg to save space.")
parser.add_argument("path", type=type_folder, help="path to music")
parser.add_argument("-l", dest="level", type=int, help="ffmpeg compression level", default=11)
parser.add_argument("-sr", dest="samplerate", type=int, help="minimum sample rate to process", default=88200)
parser.add_argument("-w", dest="workers", type=int, help="parallel encoder count", default=math.floor(os.cpu_count() * .8))
args = parser.parse_args()

if __name__ == "__main__":
	timestamp = time.time()

	log_success = open("log_success.txt", "a+", encoding="utf-8")
	log_error = open("log_error.txt", "a+", encoding="utf-8")
	pool = Pool(args.workers, work_init)

	count = 0
	savings = 0
	for res in pool.imap_unordered(work_handler, walk(args.path)):
		if res is None:
			continue

		count += 1

		if isinstance(res, SuccessResult):
			savings += res.savings
			log_success.write(f"{res.path}\n")
		elif isinstance(res, ErrorResult):
			log_error.write(f"------------\n{res.path}\n------------\n\n{res.err}\n\n\n")

		if count % max(args.workers, 10) == 0:
			print(f" {count:>7} | {savings / 1024 / 1024: >7.0f} MiB | {PurePath(res.path).relative_to(args.path).parent.as_posix()}")
			log_success.flush()
			log_error.flush()

	print(f" {count:>7} | {savings / 1024 / 1024: >7.0f} MiB")

	pool.close()
	log_success.close()
	log_error.close()

	print(f"Finished in {(time.time() - timestamp) / 60:.0f} minutes")


code:
window.MathJax = {
	tex: {
		inlineMath: [
			[ '\\(', '\\)' ]
		],
		displayMath: [
			[ '\\[', '\\]' ]
		],
		processEscapes: true,
		processEnvironments: true
	},
	options: {
		ignoreHtmlClass: '.*|',
		processHtmlClass: 'arithmatex'
	}
}

document$.subscribe(() => {
	MathJax.typesetPromise()
})


code:
title: Ripping CDs Properly

nav:
  - ripping-on-windows.md
  - ripping-on-macos.md
  - ripping-on-linux.md
  - ...


code:
# Ripping CDs Properly

Rip logs are status report files produced by proper CD ripping software, usually ending in `.log`. They describe what settings a particular CD was ripped with and contain information about any errors that occurred during the ripping process. This gives an idea of how likely the extracted data is accurate to the data on the CD.

Rip logs are scored by log checkers, which can be found on private trackers or on GitHub at [OPSnet/Logchecker](https://github.com/OPSnet/Logchecker). When a rip scores 100% a `[100%LOG]` tag can be added to the folder or zip name when sharing.

If a rip scores less than 100% the configuration of the ripper should be rechecked. If the problem persists, the issue might be with the disc itself (dirty, scratched, degraded, etc) in which case cleaning the disc and retrying before switching to another ripper as a last resort is the way to go.

Each ripper should only be used on their respective platforms. Some communities don't acknowledge Whipper logs, consider finding a Windows machine or dual booting in that case.

Well known and battle tested rippers for all platforms:

* [Exact Audio Copy](https://exactaudiocopy.de) (EAC) for Windows
* [X Lossless Decoder](https://tmkk.undo.jp/xld/index_e.html) (XLD) for Mac OS
* [Whipper](https://github.com/whipper-team/whipper) for Linux

Guides for ripping with them:

* [Ripping on Windows](ripping-on-windows) with Exact Audio Copy
* [Ripping on macOS](ripping-on-macos) with X Lossless Decoder
* [Ripping on Linux](ripping-on-linux) with Whipper

!!! danger
	* Using rippers that don't produce scoreable logs or aren't widely used and battle tested by the community should be avoided as they are likely to alter the audio in unforeseen ways, not count for all variables during ripping and not perform thorough error checking.

	* Do not use any software with CD ripping as a secondary feature to rip discs!

!!! warning
	* People tend to confuse track quality and peak level in the log text with the actual log score. They are not the same. Track quality and peak level are not even used during the score calculation.

	* Logs can be forged but the effort required usually bars people from doing so.

!!! info
	* A 100% log does not guarantee that the data is accurate. It should be interpreted as "very likely to be accurate judging by the info in the log file" and not as "100% accurate".

{{ pageinfo(authors="Salty", created="2021/10/04", updated="2021/10/06") }}


code:
# Ripping on Windows

Make sure to download the latest version of [Exact Audio Copy](https://www.exactaudiocopy.de/en/index.php/resources/download).

!!! tip
	Skip to [**Ripping**](#ripping) if you already have everything setup and wish to skip to the ripping part of the guide.

!!! warning
	Opening any filesystem dialog from EAC when [WinFSP](http://secfs.net/winfsp) is installed will immediately crash the program [(#162)](https://github.com/billziss-gh/winfsp/issues/162). Either uninstall WinFSP or run [fix162.bat](https://github.com/billziss-gh/winfsp/blob/master/tools/fix162.bat) to solve the problem.

## **Setup**

The following should be done once when setting up the program.

### 1. Install

![](/assets/img/eac/1.png)

Uncheck the paid metadata plugin.

### 2. Launch

![](/assets/img/eac/2.png)

Cancel the setup wizard as we will configure  everything manually.

![](/assets/img/eac/3.png)

After inserting the most popular CD you own in the drive the AccurateRip dialog should appear. If it does not appear skip this step for now, otherwise proceed to configure AccurateRip, depending on the result you might need to insert multiple CDs and repeat this step.

### 3. EAC Options

![](/assets/img/eac/4.png)

Open EAC options...

![](/assets/img/eac/5.png)

![](/assets/img/eac/6.png)

![](/assets/img/eac/7.png)

![](/assets/img/eac/8.png)

Normalization should never be used as it irreversibly modifies the data, which guarantees an inaccurate rip. Non-destructive ReplayGain can be applied to the tracks after ripping instead.

![](/assets/img/eac/9.png)

The following track naming scheme is recommended for all rips. Do not include folder parts in the scheme as the generated cuesheet will get messed up.

![](/assets/img/eac/10.png)

Remove all replacement strings. Original titles are always available from the file tags anyway, replacing characters in filenames, especially with similar symbols, causes unnessecary confusion when performing file operations.

### 4. Drive Options

![](/assets/img/eac/11.png)

Open drive options...

![](/assets/img/eac/12.png)

![](/assets/img/eac/13.png)

Autodetect the read command.

![](/assets/img/eac/14.png)

If you failed to configure AccurateRip earlier in the guide find the offset for your drive from the [AccurateRip Drive Offsets](http://accuraterip.com/driveoffsets.htm) page and apply it as the read sample offset correction.

![](/assets/img/eac/15.png)

### 5. Compression Options

![](/assets/img/eac/16.png)

Open compression options...

![](/assets/img/eac/17.png)

Additional command-line options:

`-8 -V -T "ARTIST=%artist%" -T "TITLE=%title%" -T "ALBUMARTIST=%albumartist%" -T "ALBUM=%albumtitle%" -T "YEAR=%year%" -T "DISCNUMBER=%cdnumber%" -T "DISCTOTAL=%totalcds%" -T "TRACKNUMBER=%tracknr%" -T "TRACKTOTAL=%numtracks%" %source% -o %dest%`

![](/assets/img/eac/18.png)

### 6. Metadata Options

![](/assets/img/eac/19.png)

Open metadata options...

![](/assets/img/eac/20.png)

Pick the CUETools DB Metadata Plugin and proceed to configure it. If you submit metadata and discids to MusicBrainz before ripping, pick the MusicBrainz Metadata Plugin instead.

![](/assets/img/eac/21.png)

Pick large cover search mode, otherwise it'll only find thumbnails.

![](/assets/img/eac/22.png)

Use any random email.

## **Ripping**

The following should be done for every rip.

!!! warning
	To rip a disc with a red first track (HTOA, hidden track) you must rip the CD by the guide first (with the addition of unselecting the red track), then test gaps on silence (`Action -> Test Gaps On Silence`) to see if the gap before the first track contains audio and after that proceed to range rip (`Action -> Copy Range -> Compressed...`) from block 0 to block `[start sector of first track] - 1`.

### 1. Prerip

![](/assets/img/eac/23.png)

After inserting a disc the following metadata should be properly filled out: CD Title, CD Artist, Year, Title, Artist, Number of CDs, CD Number.

### 2. Midrip

![](/assets/img/eac/24.png)

![](/assets/img/eac/25.png)

![](/assets/img/eac/26.png)

![](/assets/img/eac/27.png)

Make sure to pick a folder that won't leave any personal details in the path which will be added to the generated rip log. It's recommended for the folder name to simple and be consistent, for example `Album Artist - Album Title (Year) {Cat. No}`.

### 3. Postrip

* Check the rip log with a logchecker. See [Ripping CDs Properly](.)

* It's recommended to make scans of your physical release.

* It's recommended to add a good quality cover.

{{ pageinfo(authors="Salty", created="2021/10/04", updated="2021/10/09") }}

[^1]: <http://doujinstyle.com/eac/setup.html>
[^2]: <http://doujinstyle.com/eac/ripping.html>


code:
# Saving Space by Taking Care of FLAC Files

Assuming all lossless audio is already encoded in [FLAC](https://xiph.org/flac/download.html) with the highest compression level, the following often overlooked procedures can be applied to save some additional space:

!!! tip
	The procedures in this guide can be automatically applied using the following Python script: [enchires.py](/assets/doc/enchires.py)

## 1. Remove embedded images

!!! warning
	* Will decrease compatibility with some hardware players.

Often images (covers, scans, etc) for an album are identical for all tracks and will be duplicated if stored embedded, therefore taking more space for no content gained.

Removing low quality embedded covers from tracks can free up enough space for a high quality separate cover without the overall size increasing by too much compared to when the covers were embedded.

=== "Recommended"
	```
	metaflac --remove --block-type=PICTURE $path
	```

## 2. Remove excess padding

!!! warning
	* May increase tagging time when embedding images.

Padding is used to make editing metadata faster. It is added to the beginning of the file, after the existing metadata and before the audio data. When metadata changes the padding will be overwritten instead of the file being lengthened and rewritten.[^1]

Some metadata editors replace removed embedded images with padding instead of shortening the file. This can leave megabytes of unused padding, which likely won't ever be used unless embedding other images later.

The recommended amount of padding is 8KiB, which is enough for editing vorbis comments.[^2]

=== "Recommended"
	```
	metaflac --dont-use-padding --remove --block-type=PADDING $path
	metaflac --add-padding=8192 $path
	```

## 3. Reencode hires with FFmpeg

!!! warning
	* Will decrease compatibility with some hardware players.

Reencoding hires audio (effectively >=88kHz) from libFLAC compression level 8 to FFmpeg compression level 11.

The FFmpeg encoder is better optimized for compressing hires audio than the official libFLAC encoder, therefore achieving higher compression rates at faster speeds. Its main feature being that it can compress silence, which is often found in high frequencies of hires audio.

* Compression level 11 is recommended for the best balance of compression ratio and speed.
* Compression level 12, although much slower, is advised when dealing with a massive library and plentiful CPU cores.
* Reencoding <88kHz hires audio is advised when saving space is of the utmost importance.

=== "Recommended"
	```
	ffmpeg -i $in -compression_level 11 $out
	```
=== "Not Advised"
	Slower and slightly worse than FFmpeg but better than using libFLAC with only maximum compression for this use case.
	```
	flac -V -8 -e -f $path
	```

Author: Salty | Created: 2021/10/02 | Updated: 2021/10/03

[^1]: <https://xiph.org/flac/format.html#def_PADDING>
[^2]: <https://xiph.org/flac/documentation_tools_flac.html#flac_options_padding>


code:
---
hide:
  - navigation
  - toc
---

# Home

Welcome to the Music Hoarders Wiki, created to support the daily conversations and activities of the hoarders.

### Contributing

Consult `@Salty` on Discord if you have feedback or want to write a page.

### License

[CC Attribution-NonCommercial-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-nc-sa/4.0)


code:
# Digital Stores

## Music

| Name                                            | Formats                                                                                | Comment                   |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------ | ------------------------- |
| [Bandcamp](https://bandcamp.com) [^1]           | **MP3**, **AAC**, **OGG**
**FLAC**, **ALAC**
**WAV**, **AIFF**                       |                           |
| [Qobuz Store](https://qobuz.com/us-en/shop)[^2] | **MP3**, **AAC**, **WMA**
128kbps
320kbps
**FLAC**, **ALAC**
**WAV**, **AIFF** |                           |
| [HDtracks](https://hdtracks.com)   [^3]         | **FLAC**, **ALAC**
**WAV**, **AIFF**
**DSD**                                         |                           |
| iTunes                                          | **AAC**
256kbps                                                                         |                           |
| [Amazon](https://amazon.com)                    | **MP3**
                                                                                |                           |
| [Ototoy](https://ototoy.jp) [^4]                | **MP3**, **AAC**
**FLAC**, **ALAC**
**WAV**, **DSD**                                 |                           |
| [Mora](https://mora.jp) [^5]                    | **AAC**
320kbps
**FLAC**
**DSD**                                                  | Serves uncompressed FLAC. |

{{ pageinfo(updated="2021/10/04") }}

[^1]: Bandcamp — <https://get.bandcamp.help/hc/en-us/articles/360009319033-In-which-formats-can-I-download-my-purchases>
[^2]: Qobuz Store — <https://help.qobuz.com/hc/en-us/articles/360026348572-What-are-the-different-audio-formats-available-for-download>
[^3]: HDtracks — <https://www.hdtracks.com/#/faqs>
[^4]: Ototoy — <https://ototoy.jp/feature/format>
[^5]: Mora — <https://mora.jp/help/faq_hires>


code:
# Music Sources

## Public Trackers

| Name                               | Content                 | Comment                                                                                |
| ---------------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| [RuTracker](https://rutracker.org) | Russian, Western, Asian |

A fair share of range rips with low log scores.

The site is entirely in Russian.

Need to create an account.

 |
| [Nyaa](https://nyaa.si/?c=2_1)     | Japanese, Korean, Asian |

A lot of WEB releases and CD releases without logs.

                                                                         |

## Private Trackers

| Name                                     | Content                     | Entering                                               | Ratio          | Comment                                                                                |
| ---------------------------------------- | --------------------------- | ------------------------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------ |
| [Orpheus](https://orpheus.network) (OPS) | Less Broad, Little Asian    | [Interview](https://interview.orpheus.network), Invite | Medium         |

Has a request thread for RED.

Very strict.

                                               |
| [Redacted](https://redacted.ch) (RED)    | Broad                       | [Interview](https://interviewfor.red), Invite          | Hard           |

Has request threads for Qobuz, Deezer and TIDAL.

Lots of 100% logs.

Very strict.

 |
| [AnimeBytes](https://animebytes.tv) (AB) | Japanese                    | Invite                                                 | Easy for Music |

Quite hard to get into.

Fair share of 60% logs.

                                          |
| [JPopSuki](https://jpopsuki.eu) (JPS)    | East Asian, Southeast Asian | Invite                                                 | Very Easy      |

Some torrents not seeded.

Very relaxed, friendly.

Sysop is dead.

                 |
| [SugoiMusic](https://sugoimusic.me) (SM) | East Asian, Southeast Asian | Invite                                                 | Quite Easy     |

Made as a replacement for JPS.

                                                                   |
| [U2](https://u2.dmhy.org)                | Japanese, Chinese           | Invite                                                 | Medium         |

All self-rips are automatically exclusive.

Fair share of 60% logs.

                       |

## Peer to Peer

| Name                                      | Content          | Comment                                                                                |
| ----------------------------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Soulseek](http://www.slsknet.org) (SLSK) | Relatively Broad |

Some users have their shares locked and are only willing to trade for something valuable, although sometimes just asking works too.

Need to be sharing something to not get blocked by other users.

 |
| [DC++](https://dcplusplus.sourceforge.io) | Random, Scene    |

Hard to get into rooms that matter, need to be sharing a certain amount of specific content.

                                                                                |

## Direct Download

| Name                                         | Content                     | Comment                                                                                |
| -------------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| [SquidBoard](https://squid-board.ru) (Squid) | Japanese, Little Western    |

Need to create an account.

                                                                                |
| [ASTOST](https://astost.com)                 | Soundtracks, Some Classical |

All self-rips are automatically exclusive.

The site is entirely in Chinese.

Lots of 60% logs.

 |
| [TSDM](https://tsdm39.net)                   | Japanese, Chinese           |

All self-rips are automatically exclusive.

The site is entirely in Chinese.

Lots of 60% logs.

 |

## Scene

To check if something has a scene release search on of the following indexers: [predb.ovh](https://predb.ovh), [predb.me](https://predb.me) or [srrdb.org](https://srrdb.org). After verifying the existence of the release search for it on Google or ask friends with scene access. Scene releases can sometimes also be found on peer to peer platforms and direct download sites among other places.

Most scene rips do not have logs, use an unpleasant naming scheme and follow outdated guidelines.

## Streaming

See [Reference➜Streaming Services](streaming-services#music).

## Stores

See [Reference➜Digital Stores](digital-stores#music).

{{ pageinfo(updated="2021/10/06") }}


code:
# Scripts

**Reference**

| Type              | Runtime                                          |
| ----------------- | ------------------------------------------------ |
| Bash (on Windows) | [Git Bash](https://gitforwindows.org)            |
| UserScript        | [Violentmonkey](https://violentmonkey.github.io) |
| DevTools          | Browser DevTools Console                         |
| Node              | [Node.js](https://nodejs.dev/)                   |
| Python            | [Python](https://www.python.org/)                |

### Zip all folders in working directory

=== "Bash on Windows"

    ```sh
    zipdirs () {
        for name in */; do "C:/Program Files/7-Zip/7z.exe" a -r "${name::-1}.zip" "$name"; done
    }
    ```

### Copy links of loaded albums from Amazon Music search

=== "DevTools"

    ```js
    copy(Array.from(document.querySelectorAll('music-vertical-item[primary-href*="albums"]').map(el => location.origin + el.getAttribute('primary-href')).join('\n'));
    ```


code:
def define_env(env):

	@env.macro
	def pageinfo(authors = None, created = None, updated = None):
		parts = []
		if authors is not None:
			parts.append(", ".join(authors if isinstance(authors, list) else [ authors ]))
		if created is not None:
			parts.append("Created " + created)
		if updated is not None:
			parts.append("Updated " + updated)
		return "<sub>" + " | ".join(parts) + "</sub>"


code:
site_name: MH Wiki
use_directory_urls: false

theme:
  name: material
  favicon: assets/logo.svg
  font:
    text: Inter
  icon:
    logo: fontawesome/solid/hdd
  features:
    - navigation.tabs
    - navigation.tabs.sticky
    - navigation.indexes
  palette:
    scheme: default
    primary: indigo
    accent: indigo

extra:
  analytics:
    provider: google
    property: G-8TZ5VBYR2G
  social:
    - icon: fontawesome/brands/reddit
      link: https://reddit.com/r/musichoarder
      name: Reddit
    - icon: fontawesome/brands/discord
      link: https://discord.gg/d4hgc7GCAj
      name: Discord

extra_javascript:
  - assets/extra.js
  - https://polyfill.io/v3/polyfill.min.js?features=es6
  - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js

extra_css:
  - assets/extra.css

markdown_extensions:
  - meta
  - abbr
  - def_list
  - attr_list
  - footnotes
  - admonition
  - pymdownx.keys
  - pymdownx.mark
  - pymdownx.tilde
  - pymdownx.caret
  - pymdownx.tabbed
  - pymdownx.details
  - pymdownx.snippets
  - pymdownx.highlight:
      linenums: true
  - pymdownx.arithmatex:
      generic: true
  - pymdownx.superfences
  - pymdownx.inlinehilite
  - pymdownx.smartsymbols
  - pymdownx.tasklist:
      clickable_checkbox: true
      custom_checkbox: true

plugins:
  - search
  - awesome-pages
  - macros


code:
read -p "Hostnames: " HOSTS

if [ "$EUID" -ne 0 ]; then
	echo "Must be ran as root!"
	exit 1
fi

if [ ! -f mkdocs.yml ]; then
	echo "Must be ran from the project root!"
	exit 1
fi

if [ ! -f ~/.ssh/mhwiki ]; then
	ssh-keygen -t ed25519 -q -N "" -f ~/.ssh/mhwiki
fi

echo "Deploy Key: $(cat ~/.ssh/mhwiki.pub)"

pip install mkdocs-material mkdocs-macros-plugin mkdocs-awesome-pages-plugin

cp setup/nginx.conf /etc/nginx/sites-enabled/mhwiki.conf
sed -i "s~%ROOT%~$PWD/site~" /etc/nginx/sites-enabled/mhwiki.conf
sed -i "s~%HOSTS%~$HOSTS~" /etc/nginx/sites-enabled/mhwiki.conf

nginx -t && nginx -s reload


code:
server {
	listen 80;
	listen 443 ssl http2;
	server_name %HOSTS%;

	root %ROOT%;
	index index.html;

	rewrite ^/(.*)(/|/index|\.html)$ /$1 permanent;
	location / {
		try_files $uri $uri.html $uri/index.html /404;
	}
}


code:
eval `ssh-agent -s`
ssh-add ~/.ssh/mhwiki

git checkout .
git pull -4

mkdocs build


repo_name: CYBERDEViL/qwbfsmanager

path: .directory


path: .gitignore


code:
[submodule "fresh.git"]
	path = fresh.git
	url = https://github.com/pasnox/fresh.git

path: .gitmodules


code:
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.


code:
QWBFS Manager
=============

QWBFS Manager is a cross platform WBFS manager running on Windows, OS X, Linux & Unix like systems.

Useful urls
===========

Home page: https://github.com/pasnox/qwbfsmanager
Downloads: https://github.com/pasnox/qwbfsmanager/releases
Report issue: https://github.com/pasnox/qwbfsmanager/issues
Forum: http://groups.google.com/group/qwbfs-discuss

IMPORTANT NOTICE
================

I DO NOT TAKE ANY RESPONSIBILITY FOR ANY DAMAGE TO YOUR HARDWARE OR YOUR DATA BECAUSE OF IMPROPER USAGE OF THIS SOFTWARE.

Requirements
============

- Build tool chain (GCC...)
- Qt >= 4.5.x, Qt >= 5.x.x
- libfresh (https://github.com/pasnox/fresh)
- Windows only: ws2_32, leay32 (OpenSSL: http://www.slproweb.com/download/Win32OpenSSL-0_9_8q.exe)
- Unix only: crypto, udev
- OS X only: crypto, DiskArbitration, CoreServices
- A computer
- A chair
- A brain
- Some fingers

Build instructions from sources packages
========================================

- unpack the sources archive
- create a folder named "qwbfs-build" somewhere on your disk (may be at the same level than the unpacked sources archives)
- cd in the "qwbfs-build" folder
- qmake -r PREFIX=/usr/local unpacked-sources-folder/qwbfs.pro
- make -j 4
- sudo make install

Build instructions from git clone
=================================

- clone qwbfsmanager reporitory: git clone https://github.com/pasnox/qwbfsmanager.git
- cd in the cloned folder and initialize submodules: init-repositories.sh (.bat for windows)
- create a folder named "qwbfs-build" somewhere on your disk (may be at the same level than the git cloned folder)
- cd in the "qwbfs-build" folder
- qmake -r PREFIX=/usr/local cloned-git-folder/qwbfs.pro
- make -j 4
- sudo make install

NOTES
=====

On some platform qmake can be named qmake-qt4 or qmake-qt5
On some paltform, the make tool can be named gmake, nmake or mingw32-make
The "make install" command is not mandatory for Windows platform
You can change the prefix at your convenience

License
=======

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.

This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

In addition, as a special exception, the copyright holders give permission
to link this program with the OpenSSL project's "OpenSSL" library (or with
modified versions of it that use the same license as the "OpenSSL"
library), and distribute the linked executables. You must obey the GNU
General Public License in all respects for all of the code used other than
"OpenSSL".  If you modify file(s), you may extend this exception to your
version of the file(s), but you are not obligated to do so. If you do not
wish to do so, delete this exception statement from your version.

Enjoy, P@sNox

path: README


code:
# Project Description
QWBFS Manager provides a cross platform Qt 4 GUI for working with hard disk drives that have been formatted to the WBFS file system.
This is a cross platform 32/64bits (Windows, OS X, Linux/Unix like) alternative to [WBFS Manager](http://wbfsmanager.codeplex.com/).

[Downloads](https://github.com/pasnox/qwbfsmanager/releases) - [Forum](http://groups.google.com/group/qwbfs-discuss) - [Downloads Feed](http://to-be-changed)

# Features
  * Build with Qt4 and/or Qt5 (1.2.5)
  * Partitions combobox widget presenting their name, file system and size for a better and easy access to your drives. (1.2.0)
  * Game sorting by id, title, size and region. (1.2.0)
  * New ListView widget providing enhanced list and icon mode. (1.2.0)
  * CoverFlow view. (1.2.0)
  * Worker thread has been rewrited and now support all kind of import/export/convert (ISO to ISO, ISO to WBFS, WBFS to ISO, WBFS to WBFS), opening the door for normal file system disc handling (FAT, NTFS...) in a next version. (1.3.0 ?)
  * Batch convertion of ISOs files to WBFSs files. (1.2.0)
  * Batch convertion of WBFSs files to ISOs files. (1.2.0)
  * Batch renaming of ISOs/WBFSs files using customizable mask. (1.2.0)
  * New application icon by Vasseur Jean-Baptiste (Exilys, exilys@hotmail.fr) (1.2.0)
  * New splashscreen by Nagy Franto (http://code.google.com/u/nagy.franto) (1.2.0)
  * Listing of games with titles, sizes and codes. (1.0.0)
  * Drag-and-drop support for adding multiple files at once to the WBFS drive (ISOs). (1.0.0) - Now support WBFSs files (1.2.0)
  * Easy to use interface which also reports available, total and used disk space at a glance. (1.0.0)
  * Batch processing of multiple ISOs. (1.0.0) - Now supports WBFSs files (1.2.0)
  * Rename discs on the WBFS drive. (1.0.0)
  * Multilingual support
    * en_US. (1.0.0)
    * fr_FR. (1.1.0)
    * es_ES by Frannoe (Ubuntu Cosillas) (frannoe@gmail.com) (1.2.0)
    * ca_ES by Frannoe (Ubuntu Cosillas) (frannoe@gmail.com) (1.2.0)
    * it_IT by Federico Pietta (Darkmagister, f.pietta@gmail.com) (1.2.0)
    * it_IT update by kimotori (kimotori, https://github.com/kimotori) (1.2.5)
    * ru_RU, sl_SI, pl_PL, zh_CN, he_IL, da_DK, sk_SK, ja_JP, uk_UA, cs_CZ, ar_SA, zh_TW, de_DE, pt_PT, sv_SE open for translation. (1.1.0)
  * ~~Homebrew Channel entry creation.~~
  * Direct & Indirect Drive-To-Drive transferring and cloning. (1.0.0)
  * ~~Automatic RAR archive extraction.~~
  * Batch extraction and deletion. (1.0.0)
  * ~~Exporting list of games on drive to a .CSV.~~
  * ~~Ability to use more than one cover directory.~~
  * ~~Channel Creation (NEW).~~
  * Using libwbfs directly to achieve greater flexiblity. (1.0.0)
  * Using libfresh for extending Qt, providing great classes like the update checker, the network cache... (1.2.0)
  * Use a separate worker thread to improve responsiveness when doing IO operations. (1.0.0)
  * Update checker. (1.1.0)
  * Passive error handling. (1.1.0)
  * Translations manager. (1.1.0)
  * Network cache based on max retry per query to avoid over bandwidth traffic. (1.1.0)

# Notes
  * Estimating the size of an ISO file can lead to the creation of a temporary file of 16MB in your system temporary path.
  * Indirect transfer can lead to WBFS convertion to ISO of 4.4GB in a temporary file in you system path.

# Requirements
  * Qt 4.5.0 minimum (bundled with mac os x & windows packages)
  * OpenSSL (bundled with mac os x & windows packages)
  * A brain
  * A chair
  * Some fingers

# Instructions
  * Install using setup.
  * Plug in the hard drive or USB stick.
  * Run the application.
  * Choose the correct drive letter.
  * Click Load. (If you haven't already formatted the disk to WBFS, you can do that by clicking Format).
  * You should now see any backups on the drive on the right hand side.
  * You can drag and drop ISO files from Windows Explorer onto the left hand side or you can browse the file system in the left hand view.
  * Click the Process the import list button to copy them over to the WBFS drive.
  * Enjoy!

# Remaining Future Plans
  * UnRar functionality to automatically UnRar files and add the iso to the WBFS drive.
  * ~~ISO library.~~
  * ~~Additional info from ISO (Game image shown in Disc Channel,...).~~

# They speak about QWBFS Manager
  * [Ubuntu Cosillas](http://ubuntu-cosillas.blogspot.com/2010/11/qwbfs-manager-gestor-de-particiones.html)
  * [WiiGen](http://www.wiigen.fr/qwbfs-manager-103-gestionnaire-disque-dur-multiplateforme-actualite-4543.html)
  * [WiiHacks](http://www.wiihacks.com/customization-apps/61394-qwbfs-manager-win-os-x-operating-systems.html)
  * [Logic Sunrise](http://www.logic-sunrise.com/news-111351-qwbfs-manager-v110-un-gestionnaire-de-hdd-wbfs.html)
  * [Wii Addict](http://www.wii-addict.fr/forum/QWBFS-Manager-110-t20412.html)
  * [Wii GX Mod](http://wii.gx-mod.com/modules/news/article.php?storyid=2817)
  * [Xav91Wii](http://xav91wii.free.fr/forum/viewtopic.php?f=11&t=2462&start=0&sid=20d4ea529d285f11288084fd6b9c1a6a)
  * [Wii Info](http://www.wii-info.fr/download-637-qwbfs-1-1-0-gestionnaire-wbfs.htm)

DISCLAIMER
THIS APPLICATION COMES WITH NO WARRANTY AT ALL, NEITHER EXPRESS NOR IMPLIED.
I DO NOT TAKE ANY RESPONSIBILITY FOR ANY DAMAGE TO YOUR WII CONSOLE
BECAUSE OF IMPROPER USAGE OF THIS SOFTWARE.

# Screenshots
## Linux
![Linux](https://cloud.githubusercontent.com/assets/858201/2847389/92f77d9e-d0ad-11e3-82a1-90a525104128.png)
## OS X
![OS X](https://cloud.githubusercontent.com/assets/858201/2847390/92f8a9bc-d0ad-11e3-8888-c064975d1ede.png)
## Windows
![Windows](https://cloud.githubusercontent.com/assets/858201/2847391/92f9b91a-d0ad-11e3-9b81-bc2256b30442.png)


code:
@echo off

REM Update external git repositories needed to build the project
git submodule update --init --recursive


code:
#!/bin/bash

GIT=/usr/bin/git

if [ ! -f ${GIT} ]; then
    GIT=/opt/local/bin/git
fi

# Update external git repositories needed to build the project
"$GIT" submodule update --init --recursive


code:
// Copyright 2007,2008  Segher Boessenkool  <segher@kernel.crashing.org>
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#include <string.h>
#include <stdio.h>

#ifdef WIN32
#include <tools.h>
#else
#include "tools.h"
#endif

static void bn_print(char *name, u8 *a, u32 n)
{
	u32 i;

	printf("%s = ", name);

	for (i = 0; i < n; i++)
		printf("%02x", a[i]);

	printf("\n");
}

static void bn_zero(u8 *d, u32 n)
{
	memset(d, 0, n);
}

static void bn_copy(u8 *d, u8 *a, u32 n)
{
	memcpy(d, a, n);
}

int bn_compare(u8 *a, u8 *b, u32 n)
{
	u32 i;

	for (i = 0; i < n; i++) {
		if (a[i] < b[i])
			return -1;
		if (a[i] > b[i])
			return 1;
	}

	return 0;
}

void bn_sub_modulus(u8 *a, u8 *N, u32 n)
{
	u32 i;
	u32 dig;
	u8 c;

	c = 0;
	for (i = n - 1; i < n; i--) {
		dig = N[i] + c;
		c = (a[i] < dig);
		a[i] -= dig;
	}
}

void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
{
	u32 i;
	u32 dig;
	u8 c;

	c = 0;
	for (i = n - 1; i < n; i--) {
		dig = a[i] + b[i] + c;
		c = (dig >= 0x100);
		d[i] = dig;
	}

	if (c)
		bn_sub_modulus(d, N, n);

	if (bn_compare(d, N, n) >= 0)
		bn_sub_modulus(d, N, n);
}

void bn_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
{
	u32 i;
	u8 mask;

	bn_zero(d, n);

	for (i = 0; i < n; i++)
		for (mask = 0x80; mask != 0; mask >>= 1) {
			bn_add(d, d, d, N, n);
			if ((a[i] & mask) != 0)
				bn_add(d, d, b, N, n);
		}
}

void bn_exp(u8 *d, u8 *a, u8 *N, u32 n, u8 *e, u32 en)
{
	u8 t[512];
	u32 i;
	u8 mask;

	bn_zero(d, n);
	d[n-1] = 1;
	for (i = 0; i < en; i++)
		for (mask = 0x80; mask != 0; mask >>= 1) {
			bn_mul(t, d, d, N, n);
			if ((e[i] & mask) != 0)
				bn_mul(d, t, a, N, n);
			else
				bn_copy(d, t, n);
		}
}

// only for prime N -- stupid but lazy, see if I care
void bn_inv(u8 *d, u8 *a, u8 *N, u32 n)
{
	u8 t[512], s[512];

	bn_copy(t, N, n);
	bn_zero(s, n);
	s[n-1] = 2;
	bn_sub_modulus(t, s, n);
	bn_exp(d, a, N, n, t, n);
}


code:
// Copyright 2007,2008  Segher Boessenkool  <segher@kernel.crashing.org>
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#include <string.h>
#include <stdio.h>

#include "tools.h"

// y**2 + x*y = x**3 + x + b
static u8 ec_b[30] =
	"\x00\x66\x64\x7e\xde\x6c\x33\x2c\x7f\x8c\x09\x23\xbb\x58\x21"
	"\x3b\x33\x3b\x20\xe9\xce\x42\x81\xfe\x11\x5f\x7d\x8f\x90\xad";

// order of the addition group of points
static u8 ec_N[30] =
	"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
	"\x13\xe9\x74\xe7\x2f\x8a\x69\x22\x03\x1d\x26\x03\xcf\xe0\xd7";

// base point
static u8 ec_G[60] =
	"\x00\xfa\xc9\xdf\xcb\xac\x83\x13\xbb\x21\x39\xf1\xbb\x75\x5f"
	"\xef\x65\xbc\x39\x1f\x8b\x36\xf8\xf8\xeb\x73\x71\xfd\x55\x8b"
	"\x01\x00\x6a\x08\xa4\x19\x03\x35\x06\x78\xe5\x85\x28\xbe\xbf"
	"\x8a\x0b\xef\xf8\x67\xa7\xca\x36\x71\x6f\x7e\x01\xf8\x10\x52";

static void elt_print(char *name, u8 *a)
{
	u32 i;

	printf("%s = ", name);

	for (i = 0; i < 30; i++)
		printf("%02x", a[i]);

	printf("\n");
}

static void elt_copy(u8 *d, u8 *a)
{
	memcpy(d, a, 30);
}

static void elt_zero(u8 *d)
{
	memset(d, 0, 30);
}

static int elt_is_zero(u8 *d)
{
	u32 i;

	for (i = 0; i < 30; i++)
		if (d[i] != 0)
			return 0;

	return 1;
}

static void elt_add(u8 *d, u8 *a, u8 *b)
{
	u32 i;

	for (i = 0; i < 30; i++)
		d[i] = a[i] ^ b[i];
}

static void elt_mul_x(u8 *d, u8 *a)
{
	u8 carry, x, y;
	u32 i;

	carry = a[0] & 1;

	x = 0;
	for (i = 0; i < 29; i++) {
		y = a[i + 1];
		d[i] = x ^ (y >> 7);
		x = y << 1;
	}
	d[29] = x ^ carry;

	d[20] ^= carry << 2;
}

static void elt_mul(u8 *d, u8 *a, u8 *b)
{
	u32 i, n;
	u8 mask;

	elt_zero(d);

	i = 0;
	mask = 1;
	for (n = 0; n < 233; n++) {
		elt_mul_x(d, d);

		if ((a[i] & mask) != 0)
			elt_add(d, d, b);

		mask >>= 1;
		if (mask == 0) {
			mask = 0x80;
			i++;
		}
	}
}

static const u8 square[16] =
	"\x00\x01\x04\x05\x10\x11\x14\x15\x40\x41\x44\x45\x50\x51\x54\x55";

static void elt_square_to_wide(u8 *d, u8 *a)
{
	u32 i;

	for (i = 0; i < 30; i++) {
		d[2*i] = square[a[i] >> 4];
		d[2*i + 1] = square[a[i] & 15];
	}
}

static void wide_reduce(u8 *d)
{
	u32 i;
	u8 x;

	for (i = 0; i < 30; i++) {
		x = d[i];

		d[i + 19] ^= x >> 7;
		d[i + 20] ^= x << 1;

		d[i + 29] ^= x >> 1;
		d[i + 30] ^= x << 7;
	}

	x = d[30] & ~1;

	d[49] ^= x >> 7;
	d[50] ^= x << 1;

	d[59] ^= x >> 1;

	d[30] &= 1;
}

static void elt_square(u8 *d, u8 *a)
{
	u8 wide[60];

	elt_square_to_wide(wide, a);
	wide_reduce(wide);

	elt_copy(d, wide + 30);
}

static void itoh_tsujii(u8 *d, u8 *a, u8 *b, u32 j)
{
	u8 t[30];

	elt_copy(t, a);
	while (j--) {
		elt_square(d, t);
		elt_copy(t, d);
	}

	elt_mul(d, t, b);
}

static void elt_inv(u8 *d, u8 *a)
{
	u8 t[30];
	u8 s[30];

	itoh_tsujii(t, a, a, 1);
	itoh_tsujii(s, t, a, 1);
	itoh_tsujii(t, s, s, 3);
	itoh_tsujii(s, t, a, 1);
	itoh_tsujii(t, s, s, 7);
	itoh_tsujii(s, t, t, 14);
	itoh_tsujii(t, s, a, 1);
	itoh_tsujii(s, t, t, 29);
	itoh_tsujii(t, s, s, 58);
	itoh_tsujii(s, t, t, 116);
	elt_square(d, s);
}

static int point_is_on_curve(u8 *p)
{
	u8 s[30], t[30];
	u8 *x, *y;

	x = p;
	y = p + 30;

	elt_square(t, x);
	elt_mul(s, t, x);

	elt_add(s, s, t);

	elt_square(t, y);
	elt_add(s, s, t);

	elt_mul(t, x, y);
	elt_add(s, s, t);

	elt_add(s, s, ec_b);

	return elt_is_zero(s);
}

static int point_is_zero(u8 *p)
{
	return elt_is_zero(p) && elt_is_zero(p + 30);
}

static void point_double(u8 *r, u8 *p)
{
	u8 s[30], t[30];
	u8 *px, *py, *rx, *ry;

	px = p;
	py = p + 30;
	rx = r;
	ry = r + 30;

	if (elt_is_zero(px)) {
		elt_zero(rx);
		elt_zero(ry);

		return;
	}

	elt_inv(t, px);
	elt_mul(s, py, t);
	elt_add(s, s, px);

	elt_square(t, px);

	elt_square(rx, s);
	elt_add(rx, rx, s);
	rx[29] ^= 1;

	elt_mul(ry, s, rx);
	elt_add(ry, ry, rx);
	elt_add(ry, ry, t);
}

static void point_add(u8 *r, u8 *p, u8 *q)
{
	u8 s[30], t[30], u[30];
	u8 *px, *py, *qx, *qy, *rx, *ry;

	px = p;
	py = p + 30;
	qx = q;
	qy = q + 30;
	rx = r;
	ry = r + 30;

	if (point_is_zero(p)) {
		elt_copy(rx, qx);
		elt_copy(ry, qy);
		return;
	}

	if (point_is_zero(q)) {
		elt_copy(rx, px);
		elt_copy(ry, py);
		return;
	}

	elt_add(u, px, qx);

	if (elt_is_zero(u)) {
		elt_add(u, py, qy);
		if (elt_is_zero(u))
			point_double(r, p);
		else {
			elt_zero(rx);
			elt_zero(ry);
		}

		return;
	}

	elt_inv(t, u);
	elt_add(u, py, qy);
	elt_mul(s, t, u);

	elt_square(t, s);
	elt_add(t, t, s);
	elt_add(t, t, qx);
	t[29] ^= 1;

	elt_mul(u, s, t);
	elt_add(s, u, py);
	elt_add(rx, t, px);
	elt_add(ry, s, rx);
}

static void point_mul(u8 *d, u8 *a, u8 *b)	// a is bignum
{
	u32 i;
	u8 mask;

	elt_zero(d);
	elt_zero(d + 30);

	for (i = 0; i < 30; i++)
		for (mask = 0x80; mask != 0; mask >>= 1) {
			point_double(d, d);
			if ((a[i] & mask) != 0)
				point_add(d, d, b);
		}
}

void generate_ecdsa(u8 *R, u8 *S, u8 *k, u8 *hash)
{
	u8 e[30];
	u8 kk[30];

	elt_zero(e);
	memcpy(e + 10, hash, 20);

	// should take random m --> but we take 1
	//	R = (mG).x
	//	S = m**-1*(e + Rk) (mod N)
	// so, we get:
	//	R = G.x
	//	S = e + Rk (mod N)

	elt_copy(R, ec_G);
	if (bn_compare(R, ec_N, 30) >= 0)
		bn_sub_modulus(R, ec_N, 30);
	elt_copy(kk, k);
	if (bn_compare(kk, ec_N, 30) >= 0)
		bn_sub_modulus(kk, ec_N, 30);
	bn_mul(S, R, kk, ec_N, 30);
	bn_add(S, S, e, ec_N, 30);
}

int check_ecdsa(u8 *Q, u8 *R, u8 *S, u8 *hash)
{
	u8 Sinv[30];
	u8 e[30];
	u8 w1[30], w2[30];
	u8 r1[60], r2[60];

	bn_inv(Sinv, S, ec_N, 30);

	elt_zero(e);
	memcpy(e + 10, hash, 20);

	bn_mul(w1, e, Sinv, ec_N, 30);
	bn_mul(w2, R, Sinv, ec_N, 30);

	point_mul(r1, w1, ec_G);
	point_mul(r2, w2, Q);

	point_add(r1, r1, r2);

	if (bn_compare(r1, ec_N, 30) >= 0)
		bn_sub_modulus(r1, ec_N, 30);

	return (bn_compare(r1, R, 30) == 0);
}

void ec_priv_to_pub(u8 *k, u8 *Q)
{
	point_mul(Q, k, ec_G);
}