GH-2859 remove twitch page and modpack import from URL
The functionality was broken, beyond repair and an ongoing maintenance nightmare.
This commit is contained in:
parent
47ed2f48d4
commit
af5120c828
@ -455,8 +455,6 @@ set(FLAME_SOURCES
|
||||
modplatform/flame/PackManifest.cpp
|
||||
modplatform/flame/FileResolvingTask.h
|
||||
modplatform/flame/FileResolvingTask.cpp
|
||||
modplatform/flame/UrlResolvingTask.h
|
||||
modplatform/flame/UrlResolvingTask.cpp
|
||||
)
|
||||
|
||||
add_unit_test(Index
|
||||
|
@ -1,175 +0,0 @@
|
||||
#include "UrlResolvingTask.h"
|
||||
#include <QtXml>
|
||||
#include <Json.h>
|
||||
|
||||
|
||||
namespace {
|
||||
const char * metabase = "https://cursemeta.dries007.net";
|
||||
}
|
||||
|
||||
Flame::UrlResolvingTask::UrlResolvingTask(const QString& toProcess)
|
||||
: m_url(toProcess)
|
||||
{
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::executeTask()
|
||||
{
|
||||
resolveUrl();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::resolveUrl()
|
||||
{
|
||||
setStatus(tr("Resolving URL..."));
|
||||
setProgress(0, 1);
|
||||
QUrl actualUrl(m_url);
|
||||
if(actualUrl.host() != "www.curseforge.com") {
|
||||
emitFailed(tr("Not a Twitch URL."));
|
||||
return;
|
||||
}
|
||||
m_dljob.reset(new NetJob("URL resolver"));
|
||||
|
||||
bool weAreDigging = false;
|
||||
needle = QString();
|
||||
|
||||
if(m_url.startsWith("https://")) {
|
||||
if(m_url.endsWith("?client=y")) {
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download?client=y
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088?client=y
|
||||
m_url.chop(9);
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
|
||||
}
|
||||
if(m_url.endsWith("/download")) {
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download -> need to dig inside html...
|
||||
weAreDigging = true;
|
||||
needle = m_url;
|
||||
needle.replace("https://", "twitch://");
|
||||
needle.replace("/download", "/download-client/");
|
||||
m_url.append("?client=y");
|
||||
} else if (m_url.contains("/download/")) {
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download/2697088
|
||||
m_url.replace("/download/", "/download-client/");
|
||||
}
|
||||
}
|
||||
else if(m_url.startsWith("twitch://")) {
|
||||
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
|
||||
m_url.replace(0, 9, "https://");
|
||||
// https://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
|
||||
}
|
||||
auto dl = Net::Download::makeByteArray(QUrl(m_url), &results);
|
||||
m_dljob->addNetAction(dl);
|
||||
if(weAreDigging) {
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processHTML);
|
||||
} else {
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCCIP);
|
||||
}
|
||||
m_dljob->start();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::processHTML()
|
||||
{
|
||||
QString htmlDoc = QString::fromUtf8(results);
|
||||
auto index = htmlDoc.indexOf(needle);
|
||||
if(index < 0) {
|
||||
emitFailed(tr("Couldn't find the needle in the haystack..."));
|
||||
return;
|
||||
}
|
||||
auto indexStart = index;
|
||||
int indexEnd = -1;
|
||||
while((index + 1) < htmlDoc.size() && htmlDoc[index] != '"') {
|
||||
index ++;
|
||||
if(htmlDoc[index] == '"') {
|
||||
indexEnd = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(indexEnd > 0) {
|
||||
QString found = htmlDoc.mid(indexStart, indexEnd - indexStart);
|
||||
qDebug() << "Found needle: " << found;
|
||||
// twitch://www.curseforge.com/minecraft/modpacks/ftb-sky-odyssey/download-client/2697088
|
||||
m_url = found;
|
||||
resolveUrl();
|
||||
return;
|
||||
}
|
||||
emitFailed(tr("Couldn't find the end of the needle in the haystack..."));
|
||||
return;
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::processCCIP()
|
||||
{
|
||||
QDomDocument doc;
|
||||
if (!doc.setContent(results)) {
|
||||
qDebug() << results;
|
||||
emitFailed(tr("Resolving failed."));
|
||||
return;
|
||||
}
|
||||
auto packageNode = doc.namedItem("package");
|
||||
if(!packageNode.isElement()) {
|
||||
emitFailed(tr("Resolving failed: missing package root element."));
|
||||
return;
|
||||
}
|
||||
auto projectNode = packageNode.namedItem("project");
|
||||
if(!projectNode.isElement()) {
|
||||
emitFailed(tr("Resolving failed: missing project element."));
|
||||
return;
|
||||
}
|
||||
auto attribs = projectNode.attributes();
|
||||
|
||||
auto projectIdNode = attribs.namedItem("id");
|
||||
if(!projectIdNode.isAttr()) {
|
||||
emitFailed(tr("Resolving failed: missing id attribute."));
|
||||
return;
|
||||
}
|
||||
auto fileIdNode = attribs.namedItem("file");
|
||||
if(!fileIdNode.isAttr()) {
|
||||
emitFailed(tr("Resolving failed: missing file attribute."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto projectId = projectIdNode.nodeValue();
|
||||
auto fileId = fileIdNode.nodeValue();
|
||||
bool success = true;
|
||||
m_result.projectId = projectId.toInt(&success);
|
||||
if(!success) {
|
||||
emitFailed(tr("Failed to resolve projectId as a number."));
|
||||
return;
|
||||
}
|
||||
m_result.fileId = fileId.toInt(&success);
|
||||
if(!success) {
|
||||
emitFailed(tr("Failed to resolve fileId as a number."));
|
||||
return;
|
||||
}
|
||||
qDebug() << "Resolved" << m_url << "as" << m_result.projectId << "/" << m_result.fileId;
|
||||
resolveIDs();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::resolveIDs()
|
||||
{
|
||||
setStatus(tr("Resolving mod IDs..."));
|
||||
m_dljob.reset(new NetJob("Mod id resolver"));
|
||||
auto projectIdStr = QString::number(m_result.projectId);
|
||||
auto fileIdStr = QString::number(m_result.fileId);
|
||||
QString metaurl = QString("%1/%2/%3.json").arg(metabase, projectIdStr, fileIdStr);
|
||||
auto dl = Net::Download::makeByteArray(QUrl(metaurl), &results);
|
||||
m_dljob->addNetAction(dl);
|
||||
connect(m_dljob.get(), &NetJob::finished, this, &Flame::UrlResolvingTask::processCursemeta);
|
||||
m_dljob->start();
|
||||
}
|
||||
|
||||
void Flame::UrlResolvingTask::processCursemeta()
|
||||
{
|
||||
try {
|
||||
if(m_result.parseFromBytes(results)) {
|
||||
emitSucceeded();
|
||||
qDebug() << results;
|
||||
return;
|
||||
}
|
||||
} catch (const JSONValidationError &e) {
|
||||
|
||||
qCritical() << "Resolving of" << m_result.projectId << m_result.fileId << "failed because of a parsing error:";
|
||||
qCritical() << e.cause();
|
||||
qCritical() << "JSON:";
|
||||
qCritical() << results;
|
||||
}
|
||||
emitFailed(tr("Failed to resolve the modpack file."));
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "tasks/Task.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "PackManifest.h"
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
namespace Flame
|
||||
{
|
||||
class MULTIMC_LOGIC_EXPORT UrlResolvingTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UrlResolvingTask(const QString &toProcess);
|
||||
virtual ~UrlResolvingTask() {};
|
||||
|
||||
const Flame::File &getResults() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void executeTask() override;
|
||||
|
||||
protected slots:
|
||||
void processCCIP();
|
||||
void processHTML();
|
||||
void processCursemeta();
|
||||
|
||||
private:
|
||||
void resolveUrl();
|
||||
void resolveIDs();
|
||||
|
||||
private: /* data */
|
||||
QString m_url;
|
||||
QString needle;
|
||||
Flame::File m_result;
|
||||
QByteArray results;
|
||||
NetJobPtr m_dljob;
|
||||
};
|
||||
}
|
||||
|
@ -133,8 +133,6 @@ SET(MULTIMC_SOURCES
|
||||
pages/modplatform/legacy_ftb/Page.h
|
||||
pages/modplatform/legacy_ftb/ListModel.h
|
||||
pages/modplatform/legacy_ftb/ListModel.cpp
|
||||
pages/modplatform/TwitchPage.cpp
|
||||
pages/modplatform/TwitchPage.h
|
||||
pages/modplatform/ImportPage.cpp
|
||||
pages/modplatform/ImportPage.h
|
||||
|
||||
@ -277,7 +275,6 @@ SET(MULTIMC_UIS
|
||||
)
|
||||
|
||||
set(MULTIMC_QRCS
|
||||
resources/assets/assets.qrc
|
||||
resources/backgrounds/backgrounds.qrc
|
||||
resources/multimc/multimc.qrc
|
||||
resources/pe_dark/pe_dark.qrc
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include "widgets/PageContainer.h"
|
||||
#include <pages/modplatform/VanillaPage.h>
|
||||
#include <pages/modplatform/legacy_ftb/Page.h>
|
||||
#include <pages/modplatform/TwitchPage.h>
|
||||
#include <pages/modplatform/ImportPage.h>
|
||||
|
||||
|
||||
@ -95,14 +94,8 @@ NewInstanceDialog::NewInstanceDialog(const QString & initialGroup, const QString
|
||||
if(!url.isEmpty())
|
||||
{
|
||||
QUrl actualUrl(url);
|
||||
if(actualUrl.host() == "www.curseforge.com") {
|
||||
m_container->selectPage("twitch");
|
||||
twitchPage->setUrl(url);
|
||||
}
|
||||
else {
|
||||
m_container->selectPage("import");
|
||||
importPage->setUrl(url);
|
||||
}
|
||||
m_container->selectPage("import");
|
||||
importPage->setUrl(url);
|
||||
}
|
||||
|
||||
updateDialogState();
|
||||
@ -126,13 +119,11 @@ void NewInstanceDialog::accept()
|
||||
QList<BasePage *> NewInstanceDialog::getPages()
|
||||
{
|
||||
importPage = new ImportPage(this);
|
||||
twitchPage = new TwitchPage(this);
|
||||
return
|
||||
{
|
||||
new VanillaPage(this),
|
||||
importPage,
|
||||
new LegacyFTB::Page(this),
|
||||
twitchPage
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,6 @@ class NewInstanceDialog;
|
||||
class PageContainer;
|
||||
class QDialogButtonBox;
|
||||
class ImportPage;
|
||||
class TwitchPage;
|
||||
|
||||
class NewInstanceDialog : public QDialog, public BasePageProvider
|
||||
{
|
||||
@ -68,7 +67,6 @@ private:
|
||||
|
||||
QString InstIconKey;
|
||||
ImportPage *importPage = nullptr;
|
||||
TwitchPage *twitchPage = nullptr;
|
||||
std::unique_ptr<InstanceTask> creationTask;
|
||||
|
||||
bool importIcon = false;
|
||||
|
@ -43,7 +43,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
Q_INIT_RESOURCE(multimc);
|
||||
Q_INIT_RESOURCE(backgrounds);
|
||||
Q_INIT_RESOURCE(assets);
|
||||
|
||||
Q_INIT_RESOURCE(pe_dark);
|
||||
Q_INIT_RESOURCE(pe_light);
|
||||
|
@ -1,65 +0,0 @@
|
||||
#include "TwitchPage.h"
|
||||
#include "ui_TwitchPage.h"
|
||||
|
||||
#include "MultiMC.h"
|
||||
#include "dialogs/NewInstanceDialog.h"
|
||||
#include <InstanceImportTask.h>
|
||||
|
||||
TwitchPage::TwitchPage(NewInstanceDialog* dialog, QWidget *parent)
|
||||
: QWidget(parent), ui(new Ui::TwitchPage), dialog(dialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->checkButton, &QPushButton::clicked, this, &TwitchPage::triggerCheck);
|
||||
connect(ui->twitchLabel, &DropLabel::droppedURLs, [this](QList<QUrl> urls){
|
||||
if(urls.size()) {
|
||||
setUrl(urls[0].toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
TwitchPage::~TwitchPage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
bool TwitchPage::shouldDisplay() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void TwitchPage::openedImpl()
|
||||
{
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
|
||||
void TwitchPage::triggerCheck(bool)
|
||||
{
|
||||
if(m_modIdResolver) {
|
||||
return;
|
||||
}
|
||||
auto task = new Flame::UrlResolvingTask(ui->lineEdit->text());
|
||||
connect(task, &Task::finished, this, &TwitchPage::checkDone);
|
||||
m_modIdResolver.reset(task);
|
||||
task->start();
|
||||
}
|
||||
|
||||
void TwitchPage::setUrl(const QString& url)
|
||||
{
|
||||
ui->lineEdit->setText(url);
|
||||
triggerCheck(true);
|
||||
}
|
||||
|
||||
void TwitchPage::checkDone()
|
||||
{
|
||||
auto result = m_modIdResolver->getResults();
|
||||
auto formatted = QString("Project %1, File %2").arg(result.projectId).arg(result.fileId);
|
||||
if(result.resolved && result.type == Flame::File::Type::Modpack) {
|
||||
ui->twitchLabel->setText(formatted);
|
||||
QFileInfo fi(result.fileName);
|
||||
dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(result.url));
|
||||
} else {
|
||||
ui->twitchLabel->setPixmap(QPixmap(QString::fromUtf8(":/assets/deadglitch")));
|
||||
dialog->setSuggestedPack();
|
||||
}
|
||||
m_modIdResolver.reset();
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
/* Copyright 2013-2019 MultiMC Contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "pages/BasePage.h"
|
||||
#include <MultiMC.h>
|
||||
#include "tasks/Task.h"
|
||||
#include "modplatform/flame/UrlResolvingTask.h"
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class TwitchPage;
|
||||
}
|
||||
|
||||
class NewInstanceDialog;
|
||||
|
||||
class TwitchPage : public QWidget, public BasePage
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TwitchPage(NewInstanceDialog* dialog, QWidget *parent = 0);
|
||||
virtual ~TwitchPage();
|
||||
virtual QString displayName() const override
|
||||
{
|
||||
return tr("Twitch URL");
|
||||
}
|
||||
virtual QIcon icon() const override
|
||||
{
|
||||
return MMC->getThemedIcon("twitch");
|
||||
}
|
||||
virtual QString id() const override
|
||||
{
|
||||
return "twitch";
|
||||
}
|
||||
virtual QString helpPage() const override
|
||||
{
|
||||
return "Twitch-platform";
|
||||
}
|
||||
virtual bool shouldDisplay() const override;
|
||||
|
||||
void openedImpl() override;
|
||||
|
||||
void setUrl(const QString & url);
|
||||
|
||||
private slots:
|
||||
void triggerCheck(bool checked);
|
||||
void checkDone();
|
||||
|
||||
private:
|
||||
Ui::TwitchPage *ui = nullptr;
|
||||
NewInstanceDialog* dialog = nullptr;
|
||||
shared_qobject_ptr<Flame::UrlResolvingTask> m_modIdResolver;
|
||||
};
|
@ -1,79 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>TwitchPage</class>
|
||||
<widget class="QWidget" name="TwitchPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>666</width>
|
||||
<height>424</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0" colspan="3">
|
||||
<widget class="DropLabel" name="twitchLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>40</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../../resources/assets/assets.qrc">:/assets/deadglitch</pixmap>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QPushButton" name="checkButton">
|
||||
<property name="text">
|
||||
<string>Check</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Twitch URL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="lineEdit"/>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Drag and drop an Install button from CurseForge into the area above.</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>DropLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>widgets/DropLabel.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>lineEdit</tabstop>
|
||||
<tabstop>checkButton</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../resources/assets/assets.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
@ -1,6 +0,0 @@
|
||||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/assets">
|
||||
<file alias="deadglitch">deadglitch.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
class="tw-svg__asset tw-svg__asset--deadglitch tw-svg__asset--inherit"
|
||||
width="92px"
|
||||
height="96px"
|
||||
version="1.1"
|
||||
viewBox="0 0 30 30"
|
||||
x="0px"
|
||||
y="0px"
|
||||
id="svg8"
|
||||
sodipodi:docname="deadglitch.svg"
|
||||
inkscape:version="0.92.2 2405546, 2018-03-11">
|
||||
<metadata
|
||||
id="metadata14">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs12" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1353"
|
||||
inkscape:window-height="828"
|
||||
id="namedview10"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.9166667"
|
||||
inkscape:cx="44.285787"
|
||||
inkscape:cy="52.833458"
|
||||
inkscape:window-x="2958"
|
||||
inkscape:window-y="702"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg8" />
|
||||
<g
|
||||
id="g6"
|
||||
style="fill:#898395;fill-opacity:1">
|
||||
<path
|
||||
d="M26,17.4589613 L26,3 L4,3 L4,22.0601057 L10.0032868,22.0601057 L10.0032868,26 L14.0004537,22.0601057 L21.3322933,22.0601057 L26,17.4589613 L26,17.4589613 Z M21.0896458,26.0850335 L15.1583403,26.0850335 L11.2051771,30 L7.24798611,30 L7.24798611,26.0850335 L0,26.0850335 L0,5.21746493 L1.97773958,0 L29,0 L29,18.2620736 L21.0896458,26.0850335 L21.0896458,26.0850335 Z"
|
||||
id="path2"
|
||||
style="fill:#898395;fill-opacity:1" />
|
||||
<path
|
||||
d="M20.8587626,12.1710126 L22.4052753,13.7175252 L23.7175252,12.4052753 L22.1710126,10.8587626 L23.7175252,9.31224999 L22.4052753,8 L20.8587626,9.54651264 L19.31225,8 L18,9.31224999 L19.5465126,10.8587626 L18,12.4052753 L19.31225,13.7175252 L20.8587626,12.1710126 Z M11.8587626,12.1710126 L13.4052753,13.7175252 L14.7175252,12.4052753 L13.1710126,10.8587626 L14.7175252,9.31224999 L13.4052753,8 L11.8587626,9.54651264 L10.31225,8 L9,9.31224999 L10.5465126,10.8587626 L9,12.4052753 L10.31225,13.7175252 L11.8587626,12.1710126 Z"
|
||||
id="path4"
|
||||
style="fill:#898395;fill-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in New Issue
Block a user