changes to the nandExtractor...
* add more info at the bottom of the block map including free space, key, and file details * make the blocks a bit smaller so the screen doesnt need to be quite as freakin huge * blockmap is hidable and resizable
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB |
@ -1,39 +1,18 @@
|
|||||||
#include "nandwindow.h"
|
#include "nandwindow.h"
|
||||||
#include "ui_nandwindow.h"
|
#include "ui_nandwindow.h"
|
||||||
|
#include "../WiiQt/tools.h"
|
||||||
|
|
||||||
NandWindow::NandWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::NandWindow ), nThread( this )
|
NandWindow::NandWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::NandWindow ), nThread( this )
|
||||||
{
|
{
|
||||||
ui->setupUi( this );
|
ui->setupUi( this );
|
||||||
freeSpace = 0;
|
|
||||||
|
|
||||||
//setup the block map
|
//setup the block map
|
||||||
ui->graphicsView_blocks->setScene( &gv );
|
SetUpBlockMap();
|
||||||
ui->graphicsView_blocks->setAlignment( Qt::AlignRight );
|
|
||||||
ui->graphicsView_blocks->setRenderHint( QPainter::Antialiasing );
|
|
||||||
|
|
||||||
QPixmap blue( ":/blue.png" );
|
|
||||||
QPixmap green( ":/green.png" );
|
|
||||||
QPixmap pink( ":/pink.png" );
|
|
||||||
QPixmap grey( ":/grey.png" );
|
|
||||||
|
|
||||||
quint16 i = 0;
|
|
||||||
for( quint16 y = 0; y < 352; y += 11 )
|
|
||||||
{
|
|
||||||
for( quint16 x = 0; x < 1408; x += 11 )
|
|
||||||
{
|
|
||||||
pmi[ i ] = new QGraphicsPixmapItem( grey );
|
|
||||||
pmi[ i ]->setPos( x, y );
|
|
||||||
gv.addItem( pmi[ i ] );//items belong to this now. no need to delete them
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//put the progressbar on the status bar
|
//put the progressbar on the status bar
|
||||||
ui->progressBar->setVisible( false );
|
ui->progressBar->setVisible( false );
|
||||||
ui->statusBar->addPermanentWidget( ui->progressBar, 0 );
|
ui->statusBar->addPermanentWidget( ui->progressBar, 0 );
|
||||||
|
|
||||||
//ui->treeWidget->header()->resizeSection( 0, 300 );//name
|
|
||||||
QFontMetrics fm( fontMetrics() );
|
QFontMetrics fm( fontMetrics() );
|
||||||
ui->treeWidget->header()->resizeSection( 0, fm.width( QString( 22, 'W' ) ) );//name
|
ui->treeWidget->header()->resizeSection( 0, fm.width( QString( 22, 'W' ) ) );//name
|
||||||
ui->treeWidget->header()->resizeSection( 1, fm.width( "WWWWW" ) );//entry #
|
ui->treeWidget->header()->resizeSection( 1, fm.width( "WWWWW" ) );//entry #
|
||||||
@ -44,13 +23,11 @@ NandWindow::NandWindow( QWidget *parent ) : QMainWindow( parent ), ui( new Ui::N
|
|||||||
ui->treeWidget->header()->resizeSection( 6, fm.width( "WWWWW" ) );//mode
|
ui->treeWidget->header()->resizeSection( 6, fm.width( "WWWWW" ) );//mode
|
||||||
ui->treeWidget->header()->resizeSection( 7, fm.width( "WWWWW" ) );//attr
|
ui->treeWidget->header()->resizeSection( 7, fm.width( "WWWWW" ) );//attr
|
||||||
|
|
||||||
|
|
||||||
//connect( &nandBin, SIGNAL( SendError( QString ) ), this, SLOT( GetError( QString ) ) );
|
|
||||||
//connect( &nandBin, SIGNAL( SendText( QString ) ), this, SLOT( GetStatusUpdate( QString ) ) );
|
|
||||||
connect( &nThread, SIGNAL( SendError( QString ) ), this, SLOT( GetError( QString ) ) );
|
connect( &nThread, SIGNAL( SendError( QString ) ), this, SLOT( GetError( QString ) ) );
|
||||||
connect( &nThread, SIGNAL( SendText( QString ) ), this, SLOT( GetStatusUpdate( QString ) ) );
|
connect( &nThread, SIGNAL( SendText( QString ) ), this, SLOT( GetStatusUpdate( QString ) ) );
|
||||||
connect( &nThread, SIGNAL( SendExtractDone() ), this, SLOT( ThreadIsDone() ) );
|
connect( &nThread, SIGNAL( SendExtractDone() ), this, SLOT( ThreadIsDone() ) );
|
||||||
connect( &nThread, SIGNAL( SendProgress( int ) ), ui->progressBar, SLOT( setValue( int ) ) );
|
connect( &nThread, SIGNAL( SendProgress( int ) ), ui->progressBar, SLOT( setValue( int ) ) );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NandWindow::~NandWindow()
|
NandWindow::~NandWindow()
|
||||||
@ -58,6 +35,80 @@ NandWindow::~NandWindow()
|
|||||||
delete ui;
|
delete ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NandWindow::SetUpBlockMap()
|
||||||
|
{
|
||||||
|
ui->graphicsView_blocks->setScene( &gv );
|
||||||
|
|
||||||
|
QPixmap grey( ":/grey.png" );
|
||||||
|
|
||||||
|
quint16 i = 0;
|
||||||
|
for( quint16 y = 0; y < 288; y += 9 ) //create all the blocks and make them grey
|
||||||
|
{
|
||||||
|
for( quint16 x = 0; x < 1152; x += 9 )
|
||||||
|
{
|
||||||
|
pmi[ i ] = new QGraphicsPixmapItem( grey );
|
||||||
|
pmi[ i ]->setPos( x, y );
|
||||||
|
gv.addItem( pmi[ i ] );//items belong to this now. no need to delete them
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QFontMetrics fm( fontMetrics() );
|
||||||
|
|
||||||
|
quint16 y = 288;
|
||||||
|
quint16 x = 10;
|
||||||
|
quint16 x2 = 200;
|
||||||
|
quint8 spacing = 5;
|
||||||
|
|
||||||
|
fileSize = new QGraphicsTextItem();
|
||||||
|
fileSize->setPos( x, y );
|
||||||
|
nandSize = new QGraphicsTextItem();
|
||||||
|
nandSize->setPos( x + 400, y );
|
||||||
|
|
||||||
|
y += fm.height() + 2;
|
||||||
|
|
||||||
|
QGraphicsTextItem *badText = new QGraphicsTextItem( tr( "Bad" ) );
|
||||||
|
QGraphicsTextItem *freeText = new QGraphicsTextItem( tr( "Free" ) );
|
||||||
|
QGraphicsTextItem *usedText = new QGraphicsTextItem( tr( "Used" ) );
|
||||||
|
QGraphicsTextItem *used2Text = new QGraphicsTextItem( tr( "Selected file" ) );
|
||||||
|
QGraphicsTextItem *resText = new QGraphicsTextItem( tr( "Reserved" ) );
|
||||||
|
|
||||||
|
QGraphicsPixmapItem *greySquare = new QGraphicsPixmapItem( grey );
|
||||||
|
QGraphicsPixmapItem *blackSquare = new QGraphicsPixmapItem( QPixmap( ":/black.png" ) );
|
||||||
|
QGraphicsPixmapItem *blueSquare = new QGraphicsPixmapItem( QPixmap( ":/blue.png" ) );
|
||||||
|
QGraphicsPixmapItem *greenSquare = new QGraphicsPixmapItem( QPixmap( ":/green.png" ) );
|
||||||
|
QGraphicsPixmapItem *pinkSquare = new QGraphicsPixmapItem( QPixmap( ":/pink.png" ) );
|
||||||
|
|
||||||
|
greySquare->setPos( x, y + fm.height() / 2 );
|
||||||
|
freeText->setPos( greySquare->pos().x() + 8 + spacing, y );
|
||||||
|
greenSquare->setPos( x2, y + fm.height() / 2 );
|
||||||
|
usedText->setPos( greenSquare->pos().x() + 8 + spacing, y );
|
||||||
|
y += fontMetrics().height() + 2;
|
||||||
|
|
||||||
|
blackSquare->setPos( x, y + fm.height() / 2 );
|
||||||
|
badText->setPos( blackSquare->pos().x() + 8 + spacing, y );
|
||||||
|
pinkSquare->setPos( x2, y + fm.height() / 2 );
|
||||||
|
used2Text->setPos( pinkSquare->pos().x() + 8 + spacing, y );
|
||||||
|
y += fontMetrics().height() + 2;
|
||||||
|
|
||||||
|
blueSquare->setPos( x, y + fm.height() / 2 );
|
||||||
|
resText->setPos( blueSquare->pos().x() + 8 + spacing, y );
|
||||||
|
|
||||||
|
gv.addItem( fileSize );
|
||||||
|
gv.addItem( nandSize );
|
||||||
|
gv.addItem( badText );
|
||||||
|
gv.addItem( freeText );
|
||||||
|
gv.addItem( usedText );
|
||||||
|
gv.addItem( used2Text );
|
||||||
|
gv.addItem( resText );
|
||||||
|
gv.addItem( greySquare );
|
||||||
|
gv.addItem( blackSquare );
|
||||||
|
gv.addItem( blueSquare );
|
||||||
|
gv.addItem( greenSquare );
|
||||||
|
gv.addItem( pinkSquare );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void NandWindow::ThreadIsDone()
|
void NandWindow::ThreadIsDone()
|
||||||
{
|
{
|
||||||
qDebug() << "NandWindow::ThreadIsDone";
|
qDebug() << "NandWindow::ThreadIsDone";
|
||||||
@ -172,7 +223,7 @@ void NandWindow::on_actionOpen_Nand_triggered()
|
|||||||
void NandWindow::GetBlocksfromNand()
|
void NandWindow::GetBlocksfromNand()
|
||||||
{
|
{
|
||||||
blocks.clear();
|
blocks.clear();
|
||||||
freeSpace = 0;
|
quint32 freeSpace = 0;
|
||||||
|
|
||||||
QList<quint16> clusters = nThread.GetFats();
|
QList<quint16> clusters = nThread.GetFats();
|
||||||
if( !clusters.size() == 0x8000 )
|
if( !clusters.size() == 0x8000 )
|
||||||
@ -180,30 +231,33 @@ void NandWindow::GetBlocksfromNand()
|
|||||||
QMessageBox::warning( this, tr( "Error" ), tr( "Expected 0x8000 clusters from the nand, but got %1 instead!" ).arg( clusters.size(), 0, 16 ), QMessageBox::Ok );
|
QMessageBox::warning( this, tr( "Error" ), tr( "Expected 0x8000 clusters from the nand, but got %1 instead!" ).arg( clusters.size(), 0, 16 ), QMessageBox::Ok );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//QString str;
|
|
||||||
for( quint16 i = 0; i < 0x8000; i += 8 )//first cluster of each block.
|
for( quint16 i = 0; i < 0x8000; i += 8 )//first cluster of each block.
|
||||||
{
|
{
|
||||||
quint16 thisBlock = clusters.at( i );
|
quint16 thisBlock = clusters.at( i );
|
||||||
|
|
||||||
//str += QString( "%1 " ).arg( thisBlock, 4, 16 );
|
|
||||||
if( thisBlock == 0xFFFC
|
if( thisBlock == 0xFFFC
|
||||||
|| thisBlock == 0xFFFD )
|
|| thisBlock == 0xFFFD )
|
||||||
{
|
{
|
||||||
//qDebug() << "adding" << hex << thisBlock;
|
|
||||||
blocks << thisBlock;
|
blocks << thisBlock;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bool used = 0;
|
bool used = false;
|
||||||
for( quint16 j = i; j < i + 8; j++ )//each individual cluster
|
for( quint16 j = i; j < i + 8; j++ )//each individual cluster
|
||||||
{
|
{
|
||||||
thisBlock = clusters.at( i );
|
if( clusters.at( j ) == 0xFFFE )
|
||||||
if( thisBlock == 0xFFFE )
|
freeSpace += 0x4000;
|
||||||
freeSpace += 0x800;
|
|
||||||
|
|
||||||
else used = true;
|
else used = true;
|
||||||
}
|
}
|
||||||
blocks << ( used ? 1 : 0xfffe ); // just put 1 for used blocks
|
blocks << ( used ? 1 : 0xfffe ); // just put 1 for used blocks
|
||||||
}
|
}
|
||||||
|
quint32 used = 0x20000000 - freeSpace;
|
||||||
|
float per = (float)((float)used/(float)0x20000000) * 100.0f;
|
||||||
|
float usedMb = (float)((float)used/(float)0x100000);
|
||||||
|
|
||||||
|
nandSize->setHtml( QString( "<b>%1Mib ( %2 % ) used</b>" ).arg( usedMb, 3, 'f', 2 ).arg( per, 3, 'f', 2 ) );
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,7 +374,34 @@ void NandWindow::on_treeWidget_currentItemChanged( QTreeWidgetItem* current, QTr
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<quint16> blocks = ToBlocks( nThread.GetFatsForFile( entry ) );
|
QList<quint16> clusters = nThread.GetFatsForFile( entry );
|
||||||
//qDebug() << "blocks for" << current->text( 0 ) << blocks;
|
QList<quint16> blocks = ToBlocks( clusters );
|
||||||
|
|
||||||
DrawBlockMap( blocks );
|
DrawBlockMap( blocks );
|
||||||
|
float size = current->text( 2 ).toInt( &ok, 16 );
|
||||||
|
if( !ok )
|
||||||
|
{
|
||||||
|
qDebug() << "error converting" << current->text( 2 ) << "to int";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString unit = "bytes";
|
||||||
|
if( size > 1024 )
|
||||||
|
{
|
||||||
|
unit = "KiB";
|
||||||
|
size /= 1024.0f;
|
||||||
|
}
|
||||||
|
if( size > 1024 )
|
||||||
|
{
|
||||||
|
unit = "MiB";
|
||||||
|
size /= 1024.0f;
|
||||||
|
}
|
||||||
|
QString clusterStr = clusters.size() == 1 ? tr( "%1 cluster" ).arg( 1 ) : tr( "%1 clusters" ).arg( clusters.size() );
|
||||||
|
QString blockStr = blocks.size() == 1 ? tr( "%1 block" ).arg( 1 ) : tr( "%1 blocks" ).arg( blocks.size() );
|
||||||
|
QString sizeStr = QString( "( %1 %2 )" ).arg( size, 0, 'f', 2 ).arg( unit );
|
||||||
|
|
||||||
|
|
||||||
|
fileSize->setHtml( QString( "%1 - %2 in %3 %4" ).arg( current->text( 0 ) ).arg( clusterStr ).arg( blockStr ).arg( sizeStr ) );
|
||||||
|
//qDebug() << "blocks for" << current->text( 0 ) << blocks;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,14 +24,21 @@ private:
|
|||||||
|
|
||||||
NandThread nThread;
|
NandThread nThread;
|
||||||
|
|
||||||
|
void SetUpBlockMap();
|
||||||
|
|
||||||
//changes the blocks in the blockmap with different colors
|
//changes the blocks in the blockmap with different colors
|
||||||
void DrawBlockMap( QList<quint16> newFile = QList<quint16>() );
|
void DrawBlockMap( QList<quint16> newFile = QList<quint16>() );
|
||||||
QGraphicsScene gv;//scene to hold the blockmap images
|
QGraphicsScene gv; //scene to hold the blockmap images
|
||||||
QGraphicsPixmapItem *pmi[ 0x1000 ];//pointers to all the blocks in the display
|
QGraphicsPixmapItem *pmi[ 0x1000 ]; //pointers to all the blocks in the display
|
||||||
//quint16 ClusterToBlock( quint16 cluster );
|
|
||||||
|
|
||||||
QList<quint16> blocks;
|
QGraphicsTextItem *nandSize; //pointers to the items to show info under the blockmap
|
||||||
quint32 freeSpace;
|
QGraphicsTextItem *fileSize;
|
||||||
|
/*QGraphicsTextItem *badText;
|
||||||
|
QGraphicsTextItem *freeText;
|
||||||
|
QGraphicsTextItem *usedText;
|
||||||
|
QGraphicsTextItem *resText;*/
|
||||||
|
|
||||||
|
QList<quint16> blocks; //hold a list of what the blocks in the nand are used for
|
||||||
void GetBlocksfromNand();
|
void GetBlocksfromNand();
|
||||||
|
|
||||||
QList<quint16> ToBlocks( QList<quint16> clusters );
|
QList<quint16> ToBlocks( QList<quint16> clusters );
|
||||||
@ -48,6 +55,10 @@ private slots:
|
|||||||
void on_actionShow_Usage_triggered();
|
void on_actionShow_Usage_triggered();
|
||||||
void on_actionOpen_Nand_triggered();
|
void on_actionOpen_Nand_triggered();
|
||||||
void on_treeWidget_customContextMenuRequested(QPoint pos);
|
void on_treeWidget_customContextMenuRequested(QPoint pos);
|
||||||
|
//void ScaleBlockMap();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//void resizeEvent( QResizeEvent* event );
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // NANDWINDOW_H
|
#endif // NANDWINDOW_H
|
||||||
|
@ -6,22 +6,26 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>930</width>
|
<width>1180</width>
|
||||||
<height>472</height>
|
<height>654</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>NandWindow</string>
|
<string>NandWindow</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralWidget">
|
<widget class="QWidget" name="centralWidget">
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<property name="margin">
|
<property name="margin">
|
||||||
<number>2</number>
|
<number>2</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="spacing">
|
<item>
|
||||||
<number>2</number>
|
<widget class="QSplitter" name="splitter">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QTreeWidget" name="treeWidget">
|
<widget class="QTreeWidget" name="treeWidget">
|
||||||
<property name="contextMenuPolicy">
|
<property name="contextMenuPolicy">
|
||||||
<enum>Qt::CustomContextMenu</enum>
|
<enum>Qt::CustomContextMenu</enum>
|
||||||
@ -67,9 +71,10 @@
|
|||||||
</property>
|
</property>
|
||||||
</column>
|
</column>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QGraphicsView" name="graphicsView_blocks">
|
<widget class="QGraphicsView" name="graphicsView_blocks">
|
||||||
|
<property name="verticalScrollBarPolicy">
|
||||||
|
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||||
|
</property>
|
||||||
<property name="interactive">
|
<property name="interactive">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -77,14 +82,15 @@
|
|||||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="renderHints">
|
<property name="renderHints">
|
||||||
<set>QPainter::Antialiasing|QPainter::HighQualityAntialiasing|QPainter::SmoothPixmapTransform|QPainter::TextAntialiasing</set>
|
<set>QPainter::HighQualityAntialiasing|QPainter::SmoothPixmapTransform|QPainter::TextAntialiasing</set>
|
||||||
</property>
|
</property>
|
||||||
<property name="cacheMode">
|
<property name="cacheMode">
|
||||||
<set>QGraphicsView::CacheBackground</set>
|
<set>QGraphicsView::CacheBackground</set>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0">
|
<item>
|
||||||
<widget class="QProgressBar" name="progressBar">
|
<widget class="QProgressBar" name="progressBar">
|
||||||
<property name="maximumSize">
|
<property name="maximumSize">
|
||||||
<size>
|
<size>
|
||||||
@ -104,7 +110,7 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>930</width>
|
<width>1180</width>
|
||||||
<height>27</height>
|
<height>27</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
@ -141,6 +147,12 @@
|
|||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="actionShow_Usage">
|
<action name="actionShow_Usage">
|
||||||
|
<property name="checkable">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show Usage...</string>
|
<string>Show Usage...</string>
|
||||||
</property>
|
</property>
|
||||||
@ -151,5 +163,22 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<layoutdefault spacing="6" margin="11"/>
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>actionShow_Usage</sender>
|
||||||
|
<signal>toggled(bool)</signal>
|
||||||
|
<receiver>graphicsView_blocks</receiver>
|
||||||
|
<slot>setVisible(bool)</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>-1</x>
|
||||||
|
<y>-1</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>309</x>
|
||||||
|
<y>351</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
</ui>
|
</ui>
|
||||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.8 KiB |