diff options
-rw-r--r-- | LayoutStudio.pro | 15 | ||||
-rw-r--r-- | README.markdown | 16 | ||||
-rw-r--r-- | layoutgl/texturemanager.cpp | 31 | ||||
-rw-r--r-- | layoutgl/texturemanager.h | 33 | ||||
-rw-r--r-- | layoutgl/widget.cpp | 389 | ||||
-rw-r--r-- | layoutgl/widget.h | 53 | ||||
-rw-r--r-- | lsmainwindow.cpp | 217 | ||||
-rw-r--r-- | lsmainwindow.h | 40 | ||||
-rw-r--r-- | lsmainwindow.ui | 24 | ||||
-rw-r--r-- | lspackagemodel.cpp | 150 | ||||
-rw-r--r-- | lspackagemodel.h | 42 | ||||
-rw-r--r-- | lyt/archivepackage.cpp | 129 | ||||
-rw-r--r-- | lyt/archivepackage.h | 30 | ||||
-rw-r--r-- | lyt/bounding.cpp | 1 | ||||
-rw-r--r-- | lyt/directorypackage.cpp | 99 | ||||
-rw-r--r-- | lyt/directorypackage.h | 33 | ||||
-rw-r--r-- | lyt/layout.h | 2 | ||||
-rw-r--r-- | lyt/packagebase.cpp | 24 | ||||
-rw-r--r-- | lyt/packagebase.h | 62 | ||||
-rw-r--r-- | lyt/pane.cpp | 33 | ||||
-rw-r--r-- | lyt/pane.h | 20 | ||||
-rw-r--r-- | lyt/picture.cpp | 1 | ||||
-rw-r--r-- | lyt/textbox.cpp | 1 | ||||
-rw-r--r-- | lyt/window.cpp | 1 | ||||
-rw-r--r-- | main.cpp | 41 | ||||
-rw-r--r-- | wii/filesystem.cpp | 9 | ||||
-rw-r--r-- | wii/filesystem.h | 1 | ||||
-rw-r--r-- | wii/gx.h | 35 | ||||
-rw-r--r-- | wii/texpalette.cpp | 284 | ||||
-rw-r--r-- | wii/texpalette.h | 37 |
30 files changed, 1625 insertions, 228 deletions
diff --git a/LayoutStudio.pro b/LayoutStudio.pro index 49f2d40..815dcdc 100644 --- a/LayoutStudio.pro +++ b/LayoutStudio.pro @@ -32,7 +32,11 @@ SOURCES += main.cpp \ wii/archiveu8.cpp \ wii/filesystem.cpp \ lyt/archivepackage.cpp \ - wii/stringtablebuilder.cpp + wii/stringtablebuilder.cpp \ + layoutgl/texturemanager.cpp \ + layoutgl/widget.cpp \ + wii/texpalette.cpp \ + lspackagemodel.cpp HEADERS += lsmainwindow.h \ lsglobals.h \ lyt/packagebase.h \ @@ -62,8 +66,13 @@ HEADERS += lsmainwindow.h \ wii/filesystem.h \ lyt/archivepackage.h \ wii/stringtablebuilder.h \ - lyt/materials/materialcontainer.h -FORMS += lsmainwindow.ui + lyt/materials/materialcontainer.h \ + layoutgl/texturemanager.h \ + layoutgl/widget.h \ + wii/texpalette.h \ + wii/gx.h \ + lspackagemodel.h +FORMS += RESOURCES += resources.qrc OTHER_FILES += \ diff --git a/README.markdown b/README.markdown index f41ca8a..d12c1c1 100644 --- a/README.markdown +++ b/README.markdown @@ -7,10 +7,18 @@ functional, real-time previews. The application is written in C++ (with heavy usage of Nokia's Qt toolkit). -The graphical rendering will eventually use OpenGL, and build on the excellent -GX GPU emulation in the [Dolphin][dol] GameCube/Wii emulator. +I originally started this project in 2010, and wrote all the code required to +read and write layout files, but then lost interest because I didn't know where +to start with the rendering code. -[dol]: http://code.google.com/p/dolphin-emu/ +In July 2012, I've taken it back up and changed my focus. Instead of trying to +render layouts exactly the same way as they appear on the Wii, I'm going to +forget about all the fancy TEV stuff and just create something that's usable for +designing and editing layouts. I've been informed that this is actually how +Nintendo's official tool works. + +Of course, I'd still love to add accurate rendering to this some day, but it's +not going to be my #1 goal any more. Having a working editor is more important. ### Implemented Features ### @@ -22,7 +30,7 @@ GX GPU emulation in the [Dolphin][dol] GameCube/Wii emulator. ### Planned Features ### - BRLAN reading/writing - Graphical interface for editing layouts and animations -- Rendering of layouts using OpenGL and Dolphin's GX emulation +- Rendering of layouts using OpenGL - BRFNT support diff --git a/layoutgl/texturemanager.cpp b/layoutgl/texturemanager.cpp new file mode 100644 index 0000000..80a8186 --- /dev/null +++ b/layoutgl/texturemanager.cpp @@ -0,0 +1,31 @@ +#include "texturemanager.h" + +LGLTextureManager::LGLTextureManager() { +} + +void LGLTextureManager::setup(QGLWidget *gl, const LYTLayout *layout) { + // TODO: code to cleanup previous stuff + + //m_gl = gl; + m_layout = layout; + m_package = &layout->package(); + + QStringList textures = layout->generateTextureRefs(); + + foreach (const QString &texName, textures) { + qDebug() << texName; + + QByteArray tplData = m_package->getTexture(texName); + + QDataStream tplStream(tplData); + WiiTexPalette tpl(tplStream); + + const QImage &image = tpl.textures.first().image; + image.save(QString("tpl/%2__%1.png").arg(texName).arg((int)tpl.textures.first().format)); + // dirty, dirty hack, TODO: FIXME + GLuint tex = gl->bindTexture(image, GL_TEXTURE_2D); + + m_textures.insert(texName, tex); + m_images.insert(texName, image); + } +} diff --git a/layoutgl/texturemanager.h b/layoutgl/texturemanager.h new file mode 100644 index 0000000..07b7739 --- /dev/null +++ b/layoutgl/texturemanager.h @@ -0,0 +1,33 @@ +#ifndef TEXTUREMANAGER_H +#define TEXTUREMANAGER_H + +#include "wii/texpalette.h" +#include "lyt/layout.h" +#include <QGLContext> +#include <QHash> + +class LGLTextureManager { +public: + LGLTextureManager(); + + void setup(QGLWidget *gl, const LYTLayout *layout); + +private: + const QGLContext *m_gl; + const LYTLayout *m_layout; + const LYTPackageBase *m_package; + + QHash<QString, GLuint> m_textures; + QHash<QString, QImage> m_images; + +public: + GLuint glTextureForName(const QString name) const { + return m_textures.value(name); + } + + QImage imageForName(const QString name) const { + return m_images.value(name); + } +}; + +#endif // TEXTUREMANAGER_H diff --git a/layoutgl/widget.cpp b/layoutgl/widget.cpp new file mode 100644 index 0000000..0758e88 --- /dev/null +++ b/layoutgl/widget.cpp @@ -0,0 +1,389 @@ +#include "widget.h" + +LGLWidget::LGLWidget(QWidget *parent) : + QGLWidget(parent), m_layout(0) { +} + + +void LGLWidget::setLayout(LYTLayout *layout) { + // TODO: cleanup stuff for previous layout + + m_layout = layout; + setFixedSize(layout->width, layout->height); +} + + +void LGLWidget::initializeGL() { + qDebug() << "initialising GL"; + glClearColor(0.0, 0.0, 0.0, 0.0); + glEnable(GL_TEXTURE_2D); + + // this makes texture transparency work + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // makes glColor value blend with textures + glEnable(GL_COLOR_MATERIAL); + + m_texMgr.setup(this, m_layout); +} + + +void LGLWidget::resizeGL(int w, int h) { + glViewport(0, 0, (GLint)w, (GLint)h); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-304, 304, -228, 228, -100, 100); + glMatrixMode(GL_MODELVIEW); +} + + +void LGLWidget::paintGL() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (m_layout == 0) + return; + + glLoadIdentity(); + + renderPane(m_layout->rootPane); +} + +void LGLWidget::renderPane(const LYTPane *pane) { + glPushMatrix(); + + glScalef(pane->xScale, pane->yScale, 1.0f); + glRotatef(pane->xRot, 1.0f, 0.0f, 0.0f); + glRotatef(pane->yRot, 0.0f, 1.0f, 0.0f); + glRotatef(pane->zRot, 0.0f, 0.0f, 1.0f); + glTranslatef(pane->xTrans, pane->yTrans, pane->zTrans); + //qDebug() << "Translating by" << pane->xTrans << pane->yTrans << pane->zTrans; + + switch (pane->type()) { + case LYTPane::PictureType: + drawPicture((LYTPicture*)pane); + break; + case LYTPane::WindowType: + drawWindow((LYTWindow*)pane); + break; + } + + foreach (const LYTPane *childPane, pane->children) + renderPane(childPane); + + //qDebug() << "Popping"; + glPopMatrix(); +} + +void LGLWidget::drawPicture(const LYTPicture *pic) { + float dX = pic->drawnVertexX(); + float dY = pic->drawnVertexY(); + //qDebug() << "Drawing" << dX << dY << pic->width << pic->height; + + useMaterial(pic->materialName); + + drawQuad(dX, dY, pic->width, pic->height, pic->texCoords, pic->vtxColours, pic->alpha); + + glColor3ub(255, 255, 255); + renderText(dX, (dY-pic->height)+10, 0, pic->name); +} + +struct TexFlipBit { + quint8 bits[8]; + quint8 one, two; +}; + +static const TexFlipBit TextureFlipInfo[6] = { + {{0,0,1,0,0,1,1,1},0,1}, + {{1,0,0,0,1,1,0,1},0,1}, + {{0,1,1,1,0,0,1,0},0,1}, + {{0,1,0,0,1,1,1,0},1,0}, + {{1,1,0,1,1,0,0,0},0,1}, + {{1,0,1,1,0,0,0,1},1,0} +}; + +void LGLWidget::dealWithWindowFrame(LYTTexCoords &coords, const QString &materialName, + int flipType, float pieceWidth, float pieceHeight, + int rep1, int rep2, int rep3, int rep4) { + // What are you DOING Nintendo.. the code for this is a mess! + // I seriously don't get it. + // I am also using a terrible hack here. + const TexFlipBit &info = TextureFlipInfo[flipType]; + + qreal *hack = (qreal*)(&coords.coord[0]); + + float assign1 = info.bits[rep1 + info.one]; + hack[rep1 + info.one] = assign1; + hack[rep3 + info.one] = assign1; + + float assign2 = info.bits[rep1 + info.two]; + hack[rep1 + info.two] = assign2; + hack[rep2 + info.two] = assign2; + + float texSize[2]; + getTextureSize(materialName, &texSize[0], &texSize[1]); + + float assign3 = info.bits[rep1 + info.one] + (pieceWidth / ((info.bits[rep2 + info.one] - info.bits[rep1 + info.one]) * texSize[info.one])); + hack[rep2 + info.one] = assign3; + hack[rep4 + info.one] = assign3; + + float assign4 = info.bits[rep1 + info.two] + (pieceHeight / ((info.bits[rep3 + info.two] - info.bits[rep1 + info.two]) * texSize[info.two])); + hack[rep3 + info.two] = assign4; + hack[rep4 + info.two] = assign4; +} + +void LGLWidget::drawWindow(const LYTWindow *wnd) { + float dX = wnd->drawnVertexX(); + float dY = wnd->drawnVertexY(); + + // get the frame size + float frameLeft, frameTop, frameRight, frameBottom; + + switch (wnd->frames.count()) { + case 1: + float oneW, oneH; + getTextureSize(wnd->frames.at(0)->materialName, &oneW, &oneH); + frameLeft = frameRight = oneW; + frameTop = frameBottom = oneH; + break; + case 4: case 8: + getTextureSize(wnd->frames.at(0)->materialName, &frameLeft, &frameTop); + getTextureSize(wnd->frames.at(3)->materialName, &frameRight, &frameBottom); + break; + } + + // draw the content + useMaterial(wnd->contentMaterialName); + qDebug() << "content material:" << wnd->contentMaterialName; + drawQuad( + dX + frameLeft - wnd->contentOverflowLeft, + dY - frameTop + wnd->contentOverflowTop, + ((wnd->contentOverflowLeft + (wnd->width - frameLeft)) - frameRight) + wnd->contentOverflowRight, + ((wnd->contentOverflowTop + (wnd->height - frameTop)) - frameBottom) + wnd->contentOverflowBottom, + wnd->contentTexCoords, wnd->contentVtxColours, wnd->alpha); + + // deal with the frame + LYTTexCoords texCoords; + + switch (wnd->frames.count()) { + case 1: + { + const LYTWindowFrame &frame = *wnd->frames.first(); + const LYTMaterial &mat = getMaterial(frame.materialName); + + if (!mat.texMaps.empty()) { + useMaterial(mat); + + // top left + float pieceWidth = wnd->width - frameRight; + float pieceHeight = frameTop; + + dealWithWindowFrame(texCoords, frame.materialName, 0, pieceWidth, pieceHeight, 0, 2, 4, 6); + drawQuad(dX, dY, pieceWidth, pieceHeight, 1, &texCoords, 0, wnd->alpha); + + // top right + pieceWidth = frameRight; + pieceHeight = wnd->height - frameBottom; + + dealWithWindowFrame(texCoords, frame.materialName, 1, pieceWidth, pieceHeight, 2, 0, 6, 4); + + drawQuad(dX + wnd->width - frameRight, dY, pieceWidth, pieceHeight, + 1, &texCoords, 0, wnd->alpha); + + // bottom left + pieceWidth = frameLeft; + pieceHeight = wnd->height - frameTop; + + dealWithWindowFrame(texCoords, frame.materialName, 2, pieceWidth, pieceHeight, 4, 6, 0, 2); + + drawQuad(dX, dY - frameTop, pieceWidth, pieceHeight, + 1, &texCoords, 0, wnd->alpha); + + // bottom right + pieceWidth = wnd->width - frameLeft; + pieceHeight = frameBottom; + + dealWithWindowFrame(texCoords, frame.materialName, 4, pieceWidth, pieceHeight, 6, 4, 2, 0); + + drawQuad(dX + frameLeft, dY - wnd->height + frameBottom, pieceWidth, pieceHeight, + 1, &texCoords, 0, wnd->alpha); + } + } + break; + case 4: + { + // top left + const LYTWindowFrame &fTL = *wnd->frames.at(0); + const LYTMaterial &mTL = getMaterial(fTL.materialName); + + if (!mTL.texMaps.empty()) { + useMaterial(mTL); + + float pieceWidth = wnd->width - frameRight; + float pieceHeight = frameTop; + + dealWithWindowFrame(texCoords, fTL.materialName, fTL.type, pieceWidth, pieceHeight, 0, 2, 4, 6); + drawQuad(dX, dY, pieceWidth, pieceHeight, 1, &texCoords, 0, wnd->alpha); + } + + // top right + const LYTWindowFrame &fTR = *wnd->frames.at(1); + const LYTMaterial &mTR = getMaterial(fTR.materialName); + + if (!mTR.texMaps.empty()) { + useMaterial(mTR); + + float pieceWidth = frameRight; + float pieceHeight = wnd->height - frameBottom; + + dealWithWindowFrame(texCoords, fTR.materialName, fTR.type, pieceWidth, pieceHeight, 2, 0, 6, 4); + + drawQuad(dX + wnd->width - frameRight, dY, pieceWidth, pieceHeight, + 1, &texCoords, 0, wnd->alpha); + } + + // bottom left + const LYTWindowFrame &fBL = *wnd->frames.at(2); + const LYTMaterial &mBL = getMaterial(fBL.materialName); + + if (!mBL.texMaps.empty()) { + useMaterial(mBL); + + float pieceWidth = frameLeft; + float pieceHeight = wnd->height - frameTop; + + dealWithWindowFrame(texCoords, fBL.materialName, fBL.type, pieceWidth, pieceHeight, 4, 6, 0, 2); + + drawQuad(dX, dY - frameTop, pieceWidth, pieceHeight, + 1, &texCoords, 0, wnd->alpha); + } + + const LYTWindowFrame &fBR = *wnd->frames.at(3); + const LYTMaterial &mBR = getMaterial(fBR.materialName); + + // bottom right + if (!mBR.texMaps.empty()) { + useMaterial(mBR); + + float pieceWidth = frameRight; + float pieceHeight = wnd->height - frameBottom; + + dealWithWindowFrame(texCoords, fBR.materialName, fBR.type, pieceWidth, pieceHeight, 6, 4, 0, 2); + + drawQuad(dX + frameLeft, dY - wnd->height + frameBottom, pieceWidth, pieceHeight, + 1, &texCoords, 0, wnd->alpha); + } + } + break; + default: + qDebug() << "unhandled window frame count" << wnd->frames.count(); + } +} + +static GLint GLWrapModes[] = { + GL_CLAMP_TO_EDGE, // Clamp + GL_REPEAT, // Repeat + GL_MIRRORED_REPEAT, // Mirror +}; + +void LGLWidget::useMaterial(const QString &materialName) { + const LYTMaterial *mat = m_layout->materials.getMaterialByName(materialName); + + useMaterial(*mat); +} + +void LGLWidget::useMaterial(const LYTMaterial &mat) { + int cutAt = mat.texMaps.count(); + + for (int i = 0; i < cutAt; i++) { + const LYTTexMap &texMap = mat.texMaps.at(i); + + const QString &texName = texMap.textureName; + GLuint texID = m_texMgr.glTextureForName(texName); + + glActiveTexture(GL_TEXTURE0 + i); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GLWrapModes[texMap.wrap_s]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GLWrapModes[texMap.wrap_t]); + + // is there a matching TexSRT? + // TODO: fix this + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + if (mat.texSRTs.count() > i) { + const LYTTexSRT &srt = mat.texSRTs.at(i); + + glScalef(srt.xScale, srt.yScale, 1.0f); + // rotate? + glTranslatef(srt.xTrans, -srt.yTrans, 0.0f); + } + + glMatrixMode(GL_MODELVIEW); + } + + for (int i = cutAt; i < 8; i++) { + glActiveTexture(GL_TEXTURE0 + i); + glDisable(GL_TEXTURE_2D); + } + + glActiveTexture(GL_TEXTURE0); +} + +void LGLWidget::drawQuad(float x, float y, float w, float h, const QVector<LYTTexCoords> &texCoords, const QColor *colours, uchar alpha) { + drawQuad(x, y, w, h, texCoords.count(), texCoords.constData(), colours, alpha); +} + +void LGLWidget::drawQuad(float x, float y, float w, float h, int texCoordCount, const LYTTexCoords *texCoords, const QColor *colours, uchar alpha) { + if (!colours) + glColor4ub(255, 255, 255, 255); + + glBegin(GL_QUADS); + + for (int i = 0; i < texCoordCount; i++) + glMultiTexCoord2f(GL_TEXTURE0_ARB+i, texCoords[i].coord[0].x(), 1.0f-texCoords[i].coord[0].y()); + + if (colours) + qglColor(colours[0]); + glVertex2f(x, y); + + for (int i = 0; i < texCoordCount; i++) + glMultiTexCoord2f(GL_TEXTURE0_ARB+i, texCoords[i].coord[1].x(), 1.0f-texCoords[i].coord[1].y()); + + if (colours) + qglColor(colours[1]); + glVertex2f(x + w, y); + + for (int i = 0; i < texCoordCount; i++) + glMultiTexCoord2f(GL_TEXTURE0_ARB+i, texCoords[i].coord[3].x(), 1.0f-texCoords[i].coord[3].y()); + + if (colours) + qglColor(colours[2]); + glVertex2f(x + w, y - h); + + for (int i = 0; i < texCoordCount; i++) + glMultiTexCoord2f(GL_TEXTURE0_ARB+i, texCoords[i].coord[2].x(), 1.0f-texCoords[i].coord[2].y()); + + if (colours) + qglColor(colours[3]); + glVertex2f(x, y - h); + + glEnd(); +} + + +void LGLWidget::getTextureSize(const QString &materialName, float *w, float *h) { + const LYTMaterial *mat = m_layout->materials.getMaterialByName(materialName); + + if (mat->texMaps.count() == 0) { + *w = 0.0f; + *h = 0.0f; + } else { + const LYTTexMap &tm = mat->texMaps.first(); + + QImage img = m_texMgr.imageForName(tm.textureName); + *w = img.width(); + *h = img.height(); + } +} + diff --git a/layoutgl/widget.h b/layoutgl/widget.h new file mode 100644 index 0000000..e6abdfc --- /dev/null +++ b/layoutgl/widget.h @@ -0,0 +1,53 @@ +#ifndef WIDGET_H +#define WIDGET_H + +#include <QGLWidget> + +#include "lyt/layout.h" +#include "layoutgl/texturemanager.h" + +class LGLWidget : public QGLWidget +{ + Q_OBJECT +public: + explicit LGLWidget(QWidget *parent = 0); + + void setLayout(LYTLayout *layout); + +protected: + void initializeGL(); + void resizeGL(int w, int h); + void paintGL(); + + LYTLayout *m_layout; + + void renderPane(const LYTPane *pane); + + void drawPicture(const LYTPicture *pic); + void drawWindow(const LYTWindow *wnd); + + const LYTMaterial &getMaterial(const QString &materialName) { + return *m_layout->materials.getMaterialByName(materialName); + } + + void useMaterial(const QString &materialName); + void useMaterial(const LYTMaterial &material); + + void drawQuad(float x, float y, float w, float h, const QVector<LYTTexCoords> &texCoords, const QColor *colours, uchar alpha); + void drawQuad(float x, float y, float w, float h, int texCoordCount, const LYTTexCoords *texCoords, const QColor *colours, uchar alpha); + + void getTextureSize(const QString &materialName, float *w, float *h); + + void dealWithWindowFrame(LYTTexCoords &coords, const QString &materialName, int flipType, + float pieceWidth, float pieceHeight, + int rep1, int rep2, int rep3, int rep4); + + LGLTextureManager m_texMgr; + +signals: + +public slots: + +}; + +#endif // WIDGET_H diff --git a/lsmainwindow.cpp b/lsmainwindow.cpp index 82c82dc..735d7a3 100644 --- a/lsmainwindow.cpp +++ b/lsmainwindow.cpp @@ -16,18 +16,217 @@ *******************************************************************************/ #include "lsmainwindow.h" +#include <QAction> +#include <QMenuBar> +#include <QFileDialog> +#include <QInputDialog> +#include "lyt/archivepackage.h" LSMainWindow::LSMainWindow(QWidget *parent) : QMainWindow(parent) { - ui.setupUi(this); + m_package = 0; + m_dirty = false; + m_isSaved = false; + + createActions(); + + m_view = new QTreeView(this); + setCentralWidget(m_view); + + newArchive(); +} + +LSMainWindow::~LSMainWindow() { + if (m_package) + delete m_package; +} + + +void LSMainWindow::createActions() { + m_newArchiveAction = new QAction("&New Archive", this); + m_openArchiveAction = new QAction("&Open Archive...", this); + m_saveAction = new QAction("&Save", this); + m_saveArchiveAsAction = new QAction("Save Archive &As...", this); + + connect(m_newArchiveAction, SIGNAL(triggered()), SLOT(newArchive())); + connect(m_openArchiveAction, SIGNAL(triggered()), SLOT(openArchive())); + connect(m_saveAction, SIGNAL(triggered()), SLOT(save())); + connect(m_saveArchiveAsAction, SIGNAL(triggered()), SLOT(saveArchiveAs())); + + m_addLayoutAction = new QAction("New &Layout...", this); + m_addAnimationAction = new QAction("New &Animation...", this); + m_addTextureAction = new QAction("New &Texture...", this); + m_importNewAction = new QAction("&New Item...", this); + m_importAction = new QAction("&Replace This Item...", this); + m_exportAction = new QAction("&Export to File...", this); + m_renameAction = new QAction("Re&name Item...", this); + m_removeAction = new QAction("&Remove Item...", this); + + m_addActionMapper = new QSignalMapper(this); + m_addActionMapper->setMapping(m_addLayoutAction, (int)LYTPackageBase::Layout); + m_addActionMapper->setMapping(m_addAnimationAction, (int)LYTPackageBase::Animation); + m_addActionMapper->setMapping(m_addTextureAction, (int)LYTPackageBase::Texture); + + connect(m_addLayoutAction, SIGNAL(triggered()), m_addActionMapper, SLOT(map())); + connect(m_addAnimationAction, SIGNAL(triggered()), m_addActionMapper, SLOT(map())); + connect(m_addTextureAction, SIGNAL(triggered()), m_addActionMapper, SLOT(map())); + connect(m_addActionMapper, SIGNAL(mapped(int)), SLOT(handleAddSomething(int))); + + QMenuBar *bar = menuBar(); + QMenu *m; + + m = bar->addMenu("&File"); + m->addAction(m_newArchiveAction); + m->addAction(m_openArchiveAction); + m->addAction(m_saveAction); + m->addAction(m_saveArchiveAsAction); + + m = bar->addMenu("&Edit"); + m->addAction(m_addLayoutAction); + m->addAction(m_addAnimationAction); + m->addAction(m_addTextureAction); + m->addSeparator(); + QMenu *importMenu = m->addMenu("&Import from File"); + importMenu->addAction(m_importNewAction); + importMenu->addAction(m_importAction); + m->addAction(m_exportAction); + m->addSeparator(); + m->addAction(m_renameAction); + m->addAction(m_removeAction); +} + + +void LSMainWindow::handleAddSomething(int whatToAdd) { + LYTPackageBase::ItemType what = (LYTPackageBase::ItemType)whatToAdd; + + const char *typeName; + QString extension; + switch (what) { + case LYTPackageBase::Layout: + typeName = "layout"; + extension = ".brlyt"; + break; + case LYTPackageBase::Animation: + typeName = "animation"; + extension = ".brlan"; + break; + case LYTPackageBase::Texture: + typeName = "texture"; + extension = ".tpl"; + break; + default: return; + } + + QString name = QInputDialog::getText(this, + "Add Item", + QString("Name this %1:").arg(typeName)); + + if (!name.isEmpty()) { + QByteArray data = LYTPackageBase::createSkeletonItem(what); + if (!name.endsWith(extension)) + name.append(extension); + + m_package->write(what, name, data); + } +} + + + +bool LSMainWindow::ensureSaved() { + // TODO + return false; } -void LSMainWindow::changeEvent(QEvent *e) { - QMainWindow::changeEvent(e); - switch (e->type()) { - case QEvent::LanguageChange: - ui.retranslateUi(this); - break; - default: - break; +void LSMainWindow::updateTitleBar() { + QString title; + if (m_isSaved) + title = m_package->description(); + else + title = "[Unsaved]"; + + if (m_dirty) + title.append('*'); + title.append(" - LayoutStudio"); + + setWindowTitle(title); + setWindowModified(m_dirty); +} + +void LSMainWindow::newArchive() { + if (ensureSaved()) + return; + + LYTArchivePackage *pkg = new LYTArchivePackage; + setCurrentPackage(pkg); + + m_dirty = false; + m_isSaved = false; + updateTitleBar(); +} + +void LSMainWindow::openArchive() { + if (ensureSaved()) + return; + + QString path = QFileDialog::getOpenFileName(this, + "Open a Layout Archive", + QString(), + "Wii Archives (*.arc)" + ); + + if (path.isEmpty()) + return; + + LYTArchivePackage *pkg = new LYTArchivePackage(path); + setCurrentPackage(pkg); + + m_dirty = false; + m_isSaved = false; + updateTitleBar(); +} + +void LSMainWindow::save() { + // TODO: check that m_package is an arc + if (m_isSaved) { + m_dirty = false; + m_package->savePackage(); + updateTitleBar(); + } else { + saveArchiveAs(); } } + +void LSMainWindow::saveArchiveAs() { + // TODO: check that m_package is an arc + QString newPath = QFileDialog::getSaveFileName(this, + "Save Layout Archive", + QString(), + "Wii Archives (*.arc)" + ); + + if (newPath.isEmpty()) + return; + + LYTArchivePackage *pkg = (LYTArchivePackage*)m_package; + pkg->setFilename(newPath); + pkg->savePackage(); + + m_dirty = false; + m_isSaved = true; + updateTitleBar(); +} + + +void LSMainWindow::setCurrentPackage(LYTPackageBase *pkg) { + m_package = pkg; + + QItemSelectionModel *oldSel = m_view->selectionModel(); + QAbstractItemModel *model = m_view->model(); + + m_model = new LSPackageModel(pkg, this); + m_view->setModel(m_model); + + if (model) + delete model; + if (oldSel) + delete oldSel; +} diff --git a/lsmainwindow.h b/lsmainwindow.h index 18d694d..f989e7a 100644 --- a/lsmainwindow.h +++ b/lsmainwindow.h @@ -18,18 +18,52 @@ #ifndef LSMAINWINDOW_H #define LSMAINWINDOW_H -#include "ui_lsmainwindow.h" +#include "lyt/packagebase.h" +#include "lspackagemodel.h" +#include <QMainWindow> +#include <QTreeView> +#include <QSignalMapper> class LSMainWindow : public QMainWindow { Q_OBJECT public: LSMainWindow(QWidget *parent = 0); + ~LSMainWindow(); protected: - void changeEvent(QEvent *e); + bool ensureSaved(); + +public: + void updateTitleBar(); + +public slots: + void newArchive(); + void openArchive(); + void save(); + void saveArchiveAs(); + +private slots: + void handleAddSomething(int whatToAdd); private: - Ui::LSMainWindow ui; + LYTPackageBase *m_package; + void setCurrentPackage(LYTPackageBase *pkg); + + void createActions(); + QAction *m_newArchiveAction, *m_openArchiveAction, *m_saveArchiveAsAction; + QAction *m_saveAction; + + QAction *m_addLayoutAction, *m_addAnimationAction; + QAction *m_addTextureAction; + QSignalMapper *m_addActionMapper; + QAction *m_importNewAction, *m_importAction; + QAction *m_renameAction, *m_removeAction, *m_exportAction; + + LSPackageModel *m_model; + QTreeView *m_view; + + bool m_dirty; + bool m_isSaved; }; #endif // LSMAINWINDOW_H diff --git a/lsmainwindow.ui b/lsmainwindow.ui deleted file mode 100644 index a7432f2..0000000 --- a/lsmainwindow.ui +++ /dev/null @@ -1,24 +0,0 @@ -<ui version="4.0"> - <class>LSMainWindow</class> - <widget class="QMainWindow" name="LSMainWindow" > - <property name="geometry" > - <rect> - <x>0</x> - <y>0</y> - <width>600</width> - <height>400</height> - </rect> - </property> - <property name="windowTitle" > - <string>LSMainWindow</string> - </property> - <widget class="QMenuBar" name="menuBar" /> - <widget class="QToolBar" name="mainToolBar" /> - <widget class="QWidget" name="centralWidget" /> - <widget class="QStatusBar" name="statusBar" /> - </widget> - <layoutDefault spacing="6" margin="11" /> - <pixmapfunction></pixmapfunction> - <resources/> - <connections/> -</ui> diff --git a/lspackagemodel.cpp b/lspackagemodel.cpp new file mode 100644 index 0000000..d39bb89 --- /dev/null +++ b/lspackagemodel.cpp @@ -0,0 +1,150 @@ +#include "lspackagemodel.h" + +struct ContentKind { + LYTPackageBase::ItemType type; + QString name; +}; + +static const ContentKind ContentKinds[4] = { + {LYTPackageBase::Layout, "Layouts"}, + {LYTPackageBase::Animation, "Animations"}, + {LYTPackageBase::Texture, "Textures"}, + {LYTPackageBase::Font, "Fonts"}, +}; +const int ContentKindCount = 4; + +static int ContentKindForType(LYTPackageBase::ItemType type) { + switch (type) { + case LYTPackageBase::Layout: return 0; + case LYTPackageBase::Animation: return 1; + case LYTPackageBase::Texture: return 2; + case LYTPackageBase::Font: return 3; + } + return -1; +} + +LSPackageModel::LSPackageModel(LYTPackageBase *pkg, QObject *parent) : + QAbstractItemModel(parent) +{ + m_package = pkg; + + m_caches = new QStringList[ContentKindCount]; + for (int i = 0; i < ContentKindCount; i++) { + m_caches[i] = pkg->list(ContentKinds[i].type); + m_caches[i].sort(); + } + + connect(pkg, SIGNAL(fileWasAdded(LYTPackageBase::ItemType,QString)), SLOT(handleFileWasAdded(LYTPackageBase::ItemType,QString))); + //connect(pkg, SIGNAL(fileWasModified(LYTPackageBase::ItemType,QString)), SLOT(handleFileWasModified(LYTPackageBase::ItemType,QString))); + connect(pkg, SIGNAL(fileWasRemoved(LYTPackageBase::ItemType,QString)), SLOT(handleFileWasRemoved(LYTPackageBase::ItemType,QString))); + connect(pkg, SIGNAL(fileWasRenamed(LYTPackageBase::ItemType,QString,QString)), SLOT(handleFileWasRenamed(LYTPackageBase::ItemType,QString,QString))); +} + +LSPackageModel::~LSPackageModel() { + delete[] m_caches; +} + + +QModelIndex LSPackageModel::index(int row, int column, const QModelIndex &parent) const { + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + if (!parent.isValid()) + return createIndex(row, column, 0); + + return createIndex(row, column, parent.row() + 1); +} + +QModelIndex LSPackageModel::parent(const QModelIndex &child) const { + if (!child.isValid()) + return QModelIndex(); + + if (child.internalId() > 0) + return createIndex(child.internalId() - 1, 0, 0); + else + return QModelIndex(); +} + +int LSPackageModel::rowCount(const QModelIndex &parent) const { + if (!parent.isValid()) + return ContentKindCount; + else if (parent.internalId() > 0) + return 0; // an actual item + else + return m_caches[parent.row()].count(); +} + +int LSPackageModel::columnCount(const QModelIndex &parent) const { + return 1; +} + +QVariant LSPackageModel::data(const QModelIndex &index, int role) const { + if (!index.isValid()) + return QVariant(); + if (role != Qt::DisplayRole) + return QVariant(); + + int whatIs = index.internalId(); + if (whatIs == 0) + return ContentKinds[index.row()].name; + else + return m_caches[whatIs - 1].at(index.row()); +} + + +void LSPackageModel::handleFileWasAdded(LYTPackageBase::ItemType type, QString name) { + int kind = ContentKindForType(type); + + QStringList newCache = m_caches[kind]; + newCache.append(name); + newCache.sort(); + + // where was this added? + int idx = newCache.indexOf(name); + beginInsertRows(createIndex(kind, 0, 0), idx, idx); + m_caches[kind] = newCache; + endInsertRows(); +} + +void LSPackageModel::handleFileWasRemoved(LYTPackageBase::ItemType type, QString name) { + int kind = ContentKindForType(type); + + int idx = m_caches[kind].indexOf(name); + beginRemoveRows(createIndex(kind, 0, 0), idx, idx); + m_caches[kind].removeAt(idx); + endRemoveRows(); +} + +void LSPackageModel::handleFileWasRenamed(LYTPackageBase::ItemType type, QString from, QString to) { + int kind = ContentKindForType(type); + + QStringList newCache = m_caches[kind]; + int fromIdx = newCache.indexOf(from); + + // this is really a mess, but I cannot think of a better way to do it + // first, make a new cache, but don't store it to m_caches yet... + newCache[fromIdx] = to; + newCache.sort(); + + // and now, get the to Index... + int toIdx = newCache.indexOf(to); + // if the new index is less than the old one, then leave it that way + // if the new index is the same as the old one, that's fine, do nothing + // if the new index is more than the old one... add 1 to it to take into + // account that the old one is no longer around when it was computed BUT Qt + // expects it to be + // did that make sense? probably not, oh well + if (toIdx > fromIdx) + toIdx++; + + QModelIndex whatChanged = createIndex(fromIdx, 0, kind + 1); + emit dataChanged(whatChanged, whatChanged); + + QModelIndex parent = createIndex(kind, 0, 0); + + if (toIdx != fromIdx) + beginMoveRows(parent, fromIdx, fromIdx, parent, toIdx); + m_caches[kind] = newCache; + if (toIdx != fromIdx) + endMoveRows(); +} diff --git a/lspackagemodel.h b/lspackagemodel.h new file mode 100644 index 0000000..18911de --- /dev/null +++ b/lspackagemodel.h @@ -0,0 +1,42 @@ +#ifndef LSPACKAGEMODEL_H +#define LSPACKAGEMODEL_H + +#include <QAbstractItemModel> +#include "lyt/packagebase.h" + +class LSPackageModel : public QAbstractItemModel { + Q_OBJECT +public: + explicit LSPackageModel(LYTPackageBase *pkg, QObject *parent = 0); + ~LSPackageModel(); + + LYTPackageBase *package() const { return m_package; } + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + +signals: + +private slots: + //void handleAboutToAddFile(LYTPackageBase::ItemType type, QString name); + //void handleAboutToRemoveFile(LYTPackageBase::ItemType type, QString name); + //void handleAboutToRenameFile(LYTPackageBase::ItemType type, QString from, QString to); + //void handleAboutToModifyFile(LYTPackageBase::ItemType type, QString name); + + void handleFileWasAdded(LYTPackageBase::ItemType type, QString name); + void handleFileWasRemoved(LYTPackageBase::ItemType type, QString name); + void handleFileWasRenamed(LYTPackageBase::ItemType type, QString from, QString to); + //void handleFileWasModified(LYTPackageBase::ItemType type, QString name); + +public slots: + +protected: + LYTPackageBase *m_package; + + QStringList *m_caches; +}; + +#endif // LSPACKAGEMODEL_H diff --git a/lyt/archivepackage.cpp b/lyt/archivepackage.cpp index eaabbf2..acb92d2 100644 --- a/lyt/archivepackage.cpp +++ b/lyt/archivepackage.cpp @@ -19,11 +19,11 @@ #include <QtCore/QFile> -LYTArchivePackage::LYTArchivePackage() : LYTPackageBase() { +LYTArchivePackage::LYTArchivePackage(QObject *parent) : LYTPackageBase(parent) { m_archive = new WiiArchiveU8; } -LYTArchivePackage::LYTArchivePackage(QString filename) : LYTPackageBase() { +LYTArchivePackage::LYTArchivePackage(QString filename, QObject *parent) : LYTPackageBase(parent) { m_filename = filename; QFile file(filename); @@ -47,11 +47,14 @@ WiiArchiveU8 *LYTArchivePackage::archive() const { QString LYTArchivePackage::filename() const { return m_filename; } +void LYTArchivePackage::setFilename(QString path) { + m_filename = path; +} -QStringList LYTArchivePackage::listSubDirIfExists(QString dirName) const { - WiiFSObject *obj = this->m_archive->root.resolvePath(dirName); +QStringList LYTArchivePackage::list(ItemType type) const { + WiiFSObject *obj = this->m_archive->root.resolvePath(defaultPathForItemType(type, true)); if (obj && obj->isDirectory()) { QStringList output; @@ -66,9 +69,9 @@ QStringList LYTArchivePackage::listSubDirIfExists(QString dirName) const { return QStringList(); } - -QByteArray LYTArchivePackage::getFileFromSubDirIfExists(QString dirName, QString fileName) const { - WiiFSObject *obj = this->m_archive->root.resolvePath(QString("%1/%2").arg(dirName, fileName)); +QByteArray LYTArchivePackage::get(ItemType type, const QString &name) const { + QString dirName = defaultPathForItemType(type, true); + WiiFSObject *obj = this->m_archive->root.resolvePath(QString("%1/%2").arg(dirName, name)); if (obj && obj->isFile()) { return ((WiiFile*)obj)->data; @@ -77,71 +80,97 @@ QByteArray LYTArchivePackage::getFileFromSubDirIfExists(QString dirName, QString return QByteArray(); } +bool LYTArchivePackage::write(ItemType type, const QString &name, const QByteArray &data) { + if (name.isEmpty()) + return false; -bool LYTArchivePackage::writeFileToSubDir(QString dirName, QString fileName, QByteArray data) { - WiiFSObject *obj = this->m_archive->root.resolvePath(QString("%1/%2").arg(dirName, fileName)); + WiiFSObject *rootDir = this->m_archive->root.findByName("arc", false); + if (!rootDir) { + rootDir = new WiiDirectory; + rootDir->name = "arc"; + m_archive->root.addChild(rootDir); + } + if (!rootDir->isDirectory()) + return false; - if (obj && obj->isFile()) { - ((WiiFile*)obj)->data = data; - return true; - } + QString dirName = defaultPathForItemType(type, false); + WiiFSObject *dir = ((WiiDirectory*)rootDir)->findByName(dirName, false); - return false; -} + if (!dir) { + dir = new WiiDirectory; + dir->name = dirName; + ((WiiDirectory*)rootDir)->addChild(dir); + } + if (!dir->isDirectory()) + return false; + WiiFSObject *obj = ((WiiDirectory*)dir)->findByName(name, false); + if (obj && obj->isFile()) { + emit aboutToModifyFile(type, name); + ((WiiFile*)obj)->data = data; -QStringList LYTArchivePackage::listAnims() const { - return this->listSubDirIfExists("arc/anim"); -} + emit fileWasModified(type, name); -QStringList LYTArchivePackage::listLayouts() const { - return this->listSubDirIfExists("arc/blyt"); -} + return true; -QStringList LYTArchivePackage::listTextures() const { - return this->listSubDirIfExists("arc/timg"); -} + } else if (!obj) { + emit aboutToAddFile(type, name); -QStringList LYTArchivePackage::listFonts() const { - return this->listSubDirIfExists("arc/font"); -} + WiiFile *newFile = new WiiFile; + newFile->name = name; + newFile->data = data; + ((WiiDirectory*)dir)->addChild(newFile); + emit fileWasAdded(type, name); + return true; + } -QByteArray LYTArchivePackage::getAnim(QString name) const { - return this->getFileFromSubDirIfExists("arc/anim", name); + return false; } -QByteArray LYTArchivePackage::getLayout(QString name) const { - return this->getFileFromSubDirIfExists("arc/blyt", name); -} +bool LYTArchivePackage::remove(ItemType type, const QString &name) { + WiiFSObject *obj = this->m_archive->root.resolvePath(defaultPathForItemType(type, true)); -QByteArray LYTArchivePackage::getTexture(QString name) const { - return this->getFileFromSubDirIfExists("arc/timg", name); -} + if (obj && obj->isDirectory()) { + WiiDirectory *dir = (WiiDirectory*)obj; + + WiiFSObject *what = dir->findByName(name, false); + if (what && what->isFile()) { + emit aboutToRemoveFile(type, name); + dir->removeChild(what); + emit fileWasRemoved(type, name); + return true; + } + } -QByteArray LYTArchivePackage::getFont(QString name) const { - return this->getFileFromSubDirIfExists("arc/font", name); + return false; } +bool LYTArchivePackage::rename(ItemType type, const QString &from, const QString &to) { + if (to.isEmpty()) + return false; + WiiFSObject *obj = this->m_archive->root.resolvePath(defaultPathForItemType(type, true)); -bool LYTArchivePackage::writeAnim(QString name, QByteArray data) { - return this->writeFileToSubDir("arc/anim", name, data); -} - -bool LYTArchivePackage::writeLayout(QString name, QByteArray data) { - return this->writeFileToSubDir("arc/blyt", name, data); -} - -bool LYTArchivePackage::writeTexture(QString name, QByteArray data) { - return this->writeFileToSubDir("arc/timg", name, data); -} + if (obj && obj->isDirectory()) { + WiiDirectory *dir = (WiiDirectory*)obj; + + WiiFSObject *what = dir->findByName(from, false); + if (what && what->isFile()) { + WiiFSObject *conflict = dir->findByName(to, false); + if (!conflict) { + emit aboutToRenameFile(type, from, to); + what->name = to; + emit fileWasRenamed(type, from, to); + return true; + } + } + } -bool LYTArchivePackage::writeFont(QString name, QByteArray data) { - return this->writeFileToSubDir("arc/font", name, data); + return false; } diff --git a/lyt/archivepackage.h b/lyt/archivepackage.h index e2d5f25..786d962 100644 --- a/lyt/archivepackage.h +++ b/lyt/archivepackage.h @@ -22,39 +22,29 @@ #include "wii/archiveu8.h" class LYTArchivePackage : public LYTPackageBase { + Q_OBJECT public: - LYTArchivePackage(); - LYTArchivePackage(QString filename); + LYTArchivePackage(QObject *parent = 0); + LYTArchivePackage(QString filename, QObject *parent = 0); ~LYTArchivePackage(); - QStringList listAnims() const; - QStringList listLayouts() const; - QStringList listTextures() const; - QStringList listFonts() const; - - QByteArray getAnim(QString name) const; - QByteArray getLayout(QString name) const; - QByteArray getTexture(QString name) const; - QByteArray getFont(QString name) const; - - bool writeAnim(QString name, QByteArray data); - bool writeLayout(QString name, QByteArray data); - bool writeTexture(QString name, QByteArray data); - bool writeFont(QString name, QByteArray data); + QStringList list(ItemType type) const; + QByteArray get(ItemType type, const QString &name) const; + bool write(ItemType type, const QString &name, const QByteArray &data); + bool rename(ItemType type, const QString &from, const QString &to); + bool remove(ItemType type, const QString &name); + bool needsExplicitSave() const { return true; } bool savePackage(); QString description() const; WiiArchiveU8 *archive() const; QString filename() const; + void setFilename(QString path); protected: - QStringList listSubDirIfExists(QString dirName) const; - QByteArray getFileFromSubDirIfExists(QString dirName, QString fileName) const; - bool writeFileToSubDir(QString dirName, QString fileName, QByteArray data); - WiiArchiveU8 *m_archive; QString m_filename; }; diff --git a/lyt/bounding.cpp b/lyt/bounding.cpp index ce506b3..de61840 100644 --- a/lyt/bounding.cpp +++ b/lyt/bounding.cpp @@ -21,6 +21,7 @@ LYTBounding::LYTBounding(LYTLayout &layout) : LYTPane(layout) { + m_type = BoundingType; } diff --git a/lyt/directorypackage.cpp b/lyt/directorypackage.cpp index dd8c39d..803cb80 100644 --- a/lyt/directorypackage.cpp +++ b/lyt/directorypackage.cpp @@ -19,29 +19,30 @@ #include <QtCore/QDir> -LYTDirectoryPackage::LYTDirectoryPackage(QString path) : LYTPackageBase() { +LYTDirectoryPackage::LYTDirectoryPackage(QString path, QObject *parent) : LYTPackageBase(parent) { + qWarning("LYTDirectoryPackage is currently unmaintained, you probably shouldn't use it"); + QDir fix_path(path); this->m_path = fix_path.absolutePath(); } -QStringList LYTDirectoryPackage::listSubDirIfExists(QString dirName) const { +QStringList LYTDirectoryPackage::list(ItemType type) const { QDir search(m_path); - if (search.cd(dirName)) { + if (search.cd(defaultPathForItemType(type))) { return search.entryList(); } return QStringList(); } - -QByteArray LYTDirectoryPackage::getFileFromSubDirIfExists(QString dirName, QString fileName) const { +QByteArray LYTDirectoryPackage::get(ItemType type, const QString &name) const { QDir search(m_path); - if (search.cd(dirName)) { - QFile file(search.absoluteFilePath(fileName)); + if (search.cd(defaultPathForItemType(type))) { + QFile file(search.absoluteFilePath(name)); if (file.open(QFile::ReadOnly)) { return file.readAll(); @@ -51,76 +52,40 @@ QByteArray LYTDirectoryPackage::getFileFromSubDirIfExists(QString dirName, QStri return QByteArray(); } - -bool LYTDirectoryPackage::writeFileToSubDir(QString dirName, QString fileName, QByteArray data) { +bool LYTDirectoryPackage::write(ItemType type, const QString &name, const QByteArray &data) { QDir search(m_path); + QString dirName = defaultPathForItemType(type); - if (search.cd(dirName)) { - QFile file(search.absoluteFilePath(fileName)); - - if (file.open(QFile::WriteOnly)) { - if (file.write(data) != -1) { - return true; - } - } - } - - return false; -} + if (!search.cd(dirName)) { + if (!search.mkdir(dirName)) + return false; + if (!search.cd(dirName)) + return false; + } + QFile file(search.absoluteFilePath(name)); + if (file.open(QFile::WriteOnly)) { + if (file.write(data) != -1) { + return true; + } + } - -QStringList LYTDirectoryPackage::listAnims() const { - return this->listSubDirIfExists("anim"); -} - -QStringList LYTDirectoryPackage::listLayouts() const { - return this->listSubDirIfExists("blyt"); -} - -QStringList LYTDirectoryPackage::listTextures() const { - return this->listSubDirIfExists("timg"); + return false; } -QStringList LYTDirectoryPackage::listFonts() const { - return this->listSubDirIfExists("font"); -} - - - -QByteArray LYTDirectoryPackage::getAnim(QString name) const { - return this->getFileFromSubDirIfExists("anim", name); -} - -QByteArray LYTDirectoryPackage::getLayout(QString name) const { - return this->getFileFromSubDirIfExists("blyt", name); -} - -QByteArray LYTDirectoryPackage::getTexture(QString name) const { - return this->getFileFromSubDirIfExists("timg", name); -} - -QByteArray LYTDirectoryPackage::getFont(QString name) const { - return this->getFileFromSubDirIfExists("font", name); -} - - +bool LYTDirectoryPackage::remove(ItemType type, const QString &name) { + QDir search(m_path); -bool LYTDirectoryPackage::writeAnim(QString name, QByteArray data) { - return this->writeFileToSubDir("anim", name, data); -} + if (search.cd(defaultPathForItemType(type))) { + QFile file(search.absoluteFilePath(name)); -bool LYTDirectoryPackage::writeLayout(QString name, QByteArray data) { - return this->writeFileToSubDir("blyt", name, data); -} - -bool LYTDirectoryPackage::writeTexture(QString name, QByteArray data) { - return this->writeFileToSubDir("timg", name, data); -} + if (file.open(QFile::WriteOnly)) { + return file.remove(); + } + } -bool LYTDirectoryPackage::writeFont(QString name, QByteArray data) { - return this->writeFileToSubDir("font", name, data); + return false; } diff --git a/lyt/directorypackage.h b/lyt/directorypackage.h index df8a50e..0d6ea19 100644 --- a/lyt/directorypackage.h +++ b/lyt/directorypackage.h @@ -15,40 +15,33 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ +// Currently unmaintained. +// What this needs to be fixed up: +// -- Implement rename() +// -- Make it emit signals +// -- Add support to the LayoutStudio UI + #ifndef LYTDIRECTORYPACKAGE_H #define LYTDIRECTORYPACKAGE_H #include "packagebase.h" class LYTDirectoryPackage : public LYTPackageBase { + Q_OBJECT public: - LYTDirectoryPackage(QString path); - - QStringList listAnims() const; - QStringList listLayouts() const; - QStringList listTextures() const; - QStringList listFonts() const; - - QByteArray getAnim(QString name) const; - QByteArray getLayout(QString name) const; - QByteArray getTexture(QString name) const; - QByteArray getFont(QString name) const; + LYTDirectoryPackage(QString path, QObject *parent = 0); - bool writeAnim(QString name, QByteArray data); - bool writeLayout(QString name, QByteArray data); - bool writeTexture(QString name, QByteArray data); - bool writeFont(QString name, QByteArray data); + QStringList list(ItemType type) const; + QByteArray get(ItemType type, const QString &name) const; + bool write(ItemType type, const QString &name, const QByteArray &data); + bool remove(ItemType type, const QString &name); + bool needsExplicitSave() const { return false; } bool savePackage(); QString description() const; QString path() const; protected: - QStringList listSubDirIfExists(QString dirName) const; - QByteArray getFileFromSubDirIfExists(QString dirName, QString fileName) const; - bool writeFileToSubDir(QString dirName, QString fileName, QByteArray data); - - QString m_path; }; diff --git a/lyt/layout.h b/lyt/layout.h index 092be44..c051842 100644 --- a/lyt/layout.h +++ b/lyt/layout.h @@ -57,6 +57,7 @@ public: LYTPane *rootPane; QList<LYTGroup *> groups; + QStringList generateTextureRefs() const; protected: @@ -77,7 +78,6 @@ protected: LYTPane *createPaneObj(LYTBinaryFileSection §ion); - QStringList generateTextureRefs() const; QStringList generateFontRefs() const; void writeMat1(LYTBinaryFileSection §ion) const; diff --git a/lyt/packagebase.cpp b/lyt/packagebase.cpp index e186435..5855688 100644 --- a/lyt/packagebase.cpp +++ b/lyt/packagebase.cpp @@ -17,9 +17,31 @@ #include "packagebase.h" -LYTPackageBase::LYTPackageBase() { +LYTPackageBase::LYTPackageBase(QObject *parent) : QObject(parent) { // do nothing } LYTPackageBase::~LYTPackageBase() { } + + + +QString LYTPackageBase::defaultPathForItemType(ItemType type, bool withArc) { + switch (type) { + case Layout: + return withArc ? "arc/blyt" : "blyt"; + case Animation: + return withArc ? "arc/anim" : "anim"; + case Texture: + return withArc ? "arc/timg" : "timg"; + case Font: + return withArc ? "arc/font" : "font"; + default: + return QString(); + } +} + + +QByteArray LYTPackageBase::createSkeletonItem(ItemType type) { + return QByteArray(); +} diff --git a/lyt/packagebase.h b/lyt/packagebase.h index 6129e0b..ac11b92 100644 --- a/lyt/packagebase.h +++ b/lyt/packagebase.h @@ -20,30 +20,64 @@ #include <QStringList> #include <QByteArray> +#include <QObject> -class LYTPackageBase { +class LYTPackageBase : public QObject { + Q_OBJECT public: - LYTPackageBase(); + LYTPackageBase(QObject *parent = 0); virtual ~LYTPackageBase(); - virtual QStringList listAnims() const = 0; - virtual QStringList listLayouts() const = 0; - virtual QStringList listTextures() const = 0; - virtual QStringList listFonts() const = 0; + enum ItemType { + Layout = 1, + Animation, + Texture, + Font + }; - virtual QByteArray getAnim(QString name) const = 0; - virtual QByteArray getLayout(QString name) const = 0; - virtual QByteArray getTexture(QString name) const = 0; - virtual QByteArray getFont(QString name) const = 0; + static QString defaultPathForItemType(ItemType type, bool withArc=false); + static QByteArray createSkeletonItem(ItemType type); - virtual bool writeAnim(QString name, QByteArray data) = 0; - virtual bool writeLayout(QString name, QByteArray data) = 0; - virtual bool writeTexture(QString name, QByteArray data) = 0; - virtual bool writeFont(QString name, QByteArray data) = 0; + virtual QStringList list(ItemType type) const = 0; + virtual QByteArray get(ItemType type, const QString &name) const = 0; + virtual bool write(ItemType type, const QString &name, const QByteArray &data) = 0; + virtual bool rename(ItemType type, const QString &from, const QString &to) = 0; + virtual bool remove(ItemType type, const QString &name) = 0; + // Shortcuts +#define MakeThing(THING, ENUMVAL) \ + QStringList list##THING##s () const { return list(ENUMVAL); } \ + QByteArray get##THING (const QString &name) const \ + { return get(ENUMVAL, name); } \ + \ + bool write##THING (const QString &name, const QByteArray &data) \ + { return write(ENUMVAL, name, data); } \ + \ + bool remove##THING(const QString &name) { return remove(ENUMVAL, name); } + + // Use it + MakeThing(Layout, Layout) + MakeThing(Anim, Animation) + MakeThing(Texture, Texture) + MakeThing(Font, Font) + +#undef MakeThing + + virtual bool needsExplicitSave() const = 0; virtual bool savePackage() = 0; virtual QString description() const = 0; + +signals: + void aboutToAddFile(LYTPackageBase::ItemType type, QString name); + void aboutToRemoveFile(LYTPackageBase::ItemType type, QString name); + void aboutToRenameFile(LYTPackageBase::ItemType type, QString from, QString to); + void aboutToModifyFile(LYTPackageBase::ItemType type, QString name); + + void fileWasAdded(LYTPackageBase::ItemType type, QString name); + void fileWasRemoved(LYTPackageBase::ItemType type, QString name); + void fileWasRenamed(LYTPackageBase::ItemType type, QString from, QString to); + void fileWasModified(LYTPackageBase::ItemType type, QString name); }; #endif // LYTPACKAGEBASE_H diff --git a/lyt/pane.cpp b/lyt/pane.cpp index 7ae768a..3a4abd0 100644 --- a/lyt/pane.cpp +++ b/lyt/pane.cpp @@ -20,6 +20,7 @@ LYTPane::LYTPane(LYTLayout &layout) : m_layout(layout) { this->parent = 0; + m_type = PaneType; } LYTPane::~LYTPane() { @@ -59,7 +60,7 @@ void LYTPane::dumpToDebug(bool showHeading) const { qDebug() << "- Rotation:" << xRot << "," << yRot << "," << zRot; qDebug() << "- Scale:" << xScale << "," << yScale; qDebug() << "- Size:" << width << "x" << height; - qDebug() << "- Flags:" << flags << "- Origin:" << origin; + qDebug() << "- Flags:" << flags << "- Origin:" << horzOrigin << "," << vertOrigin; qDebug() << "- Alpha:" << alpha << "- Userdata:" << userdata; } @@ -67,7 +68,7 @@ void LYTPane::dumpToDebug(bool showHeading) const { void LYTPane::writeToDataStream(QDataStream &out) const { out << (quint8)flags; - out << (quint8)origin; + out << (quint8)((int)horzOrigin + ((int)vertOrigin * 3)); out << (quint8)alpha; WritePadding(1, out); @@ -88,7 +89,10 @@ void LYTPane::writeToDataStream(QDataStream &out) const { void LYTPane::readFromDataStream(QDataStream &in) { in >> (quint8&)flags; - in >> (quint8&)origin; + quint8 rawOrigin; + in >> rawOrigin; + horzOrigin = (OriginType)(rawOrigin % 3); + vertOrigin = (OriginType)(rawOrigin / 3); in >> (quint8&)alpha; in.skipRawData(1); // padding @@ -115,3 +119,26 @@ void LYTPane::addFontRefsToList(QStringList &list) const { p->addFontRefsToList(list); } } + + +float LYTPane::drawnVertexX() const { + switch (horzOrigin) { + case Center: + return -width / 2.0f; + case Right: + return -width; + default: + return 0.0f; + } +} + +float LYTPane::drawnVertexY() const { + switch (vertOrigin) { + case Center: + return height / 2.0f; + case Bottom: + return height; + default: + return 0.0f; + } +} @@ -35,6 +35,10 @@ public: virtual Magic magic() const; + enum PaneTypes { + PaneType = 0, PictureType, TextBoxType, WindowType, BoundingType + }; + virtual void writeToDataStream(QDataStream &out) const; virtual void readFromDataStream(QDataStream &in); @@ -50,7 +54,18 @@ public: QList<LYTPane *> children; quint8 flags; - quint8 origin; + + enum OriginType { + Left = 0, Top = 0, + Center = 1, + Right = 2, Bottom = 2 + }; + OriginType horzOrigin; + OriginType vertOrigin; + + float drawnVertexX() const; + float drawnVertexY() const; + quint8 alpha; QString name; @@ -70,8 +85,11 @@ public: float width; float height; + PaneTypes type() const { return m_type; } + protected: LYTLayout &m_layout; + PaneTypes m_type; }; diff --git a/lyt/picture.cpp b/lyt/picture.cpp index 8cd5f1f..facd5ca 100644 --- a/lyt/picture.cpp +++ b/lyt/picture.cpp @@ -21,6 +21,7 @@ LYTPicture::LYTPicture(LYTLayout &layout) : LYTPane(layout) { + m_type = PictureType; } diff --git a/lyt/textbox.cpp b/lyt/textbox.cpp index e1b640e..e943963 100644 --- a/lyt/textbox.cpp +++ b/lyt/textbox.cpp @@ -21,6 +21,7 @@ LYTTextBox::LYTTextBox(LYTLayout &layout) : LYTPane(layout) { + m_type = TextBoxType; } diff --git a/lyt/window.cpp b/lyt/window.cpp index 52d2db9..096f747 100644 --- a/lyt/window.cpp +++ b/lyt/window.cpp @@ -50,6 +50,7 @@ void LYTWindowFrame::dumpToDebug() const { LYTWindow::LYTWindow(LYTLayout &layout) : LYTPane(layout) { + m_type = WindowType; } LYTWindow::~LYTWindow() { @@ -27,24 +27,42 @@ #include "wii/archiveu8.h" +#include "layoutgl/widget.h" + int main(int argc, char *argv[]) { QApplication a(argc, argv); LSGlobals::setup(); + LSMainWindow w; + w.show(); + + return a.exec(); + /*QFile file("H:\\ISOs\\NSMBWii\\Extracted\\Layout\\continue\\continue.arc"); file.open(QFile::ReadOnly); QByteArray arc = file.readAll(); file.close();*/ - LYTArchivePackage package("H:\\ISOs\\NSMBWii\\Extracted\\Layout\\continue\\continue.arc"); - LYTLayout layout(package, "continue_05.brlyt"); - QByteArray brlyt = layout.pack(); - QFile file("H:\\ISOs\\NSMBWii\\Extracted\\Layout\\continue\\continue\\arc\\blyt\\continue_05_repack.brlyt"); - file.open(QFile::WriteOnly); - file.write(brlyt); - file.close(); + //LYTArchivePackage package("H:\\ISOs\\NSMBWii\\Extracted\\Layout\\continue\\continue.arc"); + QString blah1 = "preGame"; + //QString blah = "preGame/preGame.arc"; + QString blah = QString("%1/%1.arc").arg(blah1); + QString cpath; + if (QFile::exists("/home/me/Games/Newer/ISO/files/Layout/" + blah)) { + cpath = "/home/me/Games/Newer/ISO/files/Layout/" + blah; + } else { + cpath = "Z:\\stuff\\Games\\Newer\\ISO\\files\\Layout\\" + blah; + } + LYTArchivePackage package(cpath); + LYTLayout layout(package, package.listLayouts().first()); + //LYTLayout layout(package, "continue_05.brlyt"); + //QByteArray brlyt = layout.pack(); + //QFile file("H:\\ISOs\\NSMBWii\\Extracted\\Layout\\continue\\continue\\arc\\blyt\\continue_05_repack.brlyt"); + //file.open(QFile::WriteOnly); + //file.write(brlyt); + //file.close(); //package.writeLayout("continue_05.brlyt", brlyt); //package.savePackage(); @@ -52,10 +70,17 @@ int main(int argc, char *argv[]) { //LYTDirectoryPackage package("H:\\ISOs\\NSMBWii\\Extracted\\Layout\\continue\\continue\\arc"); //LYTLayout layout(package, "continue_05.brlyt"); //LYTDirectoryPackage package("H:\\ISOs\\TP\\banner\\arc_extr"); + //LYTDirectoryPackage package("/mnt/h/ISOs/TP/banner/arc_extr"); + //LYTDirectoryPackage package("/mnt/h/ISOs/hbm/InetChannelNew/0001000148414450/00000000_app_OUT/meta/banner_bin_OUT/arc"); + //LYTDirectoryPackage package("/mnt/h/ISOs/CSWii/BannerTools/0001000157435645/00000000_app_OUT/meta/banner_bin_OUT/arc"); //LYTLayout layout(package, "banner.brlyt"); - LSMainWindow w; + //LSMainWindow w; w.show(); + LGLWidget w2; + w2.setLayout(&layout); + w2.show(); + return a.exec(); } diff --git a/wii/filesystem.cpp b/wii/filesystem.cpp index 014b693..ef2612d 100644 --- a/wii/filesystem.cpp +++ b/wii/filesystem.cpp @@ -139,3 +139,12 @@ bool WiiDirectory::addChild(WiiFSObject *obj) { return true; } +bool WiiDirectory::removeChild(WiiFSObject *obj) { + if (obj->parent != this) + return false; + + obj->unlinkFromParent(); + delete obj; + + return true; +} diff --git a/wii/filesystem.h b/wii/filesystem.h index 7527a7d..f9e2b54 100644 --- a/wii/filesystem.h +++ b/wii/filesystem.h @@ -67,6 +67,7 @@ public: WiiFSObject *findByName(QString name, bool recursive) const; WiiFSObject *resolvePath(QString path); bool addChild(WiiFSObject *obj); + bool removeChild(WiiFSObject *obj); }; diff --git a/wii/gx.h b/wii/gx.h new file mode 100644 index 0000000..85232f3 --- /dev/null +++ b/wii/gx.h @@ -0,0 +1,35 @@ +#ifndef WII_GX_H +#define WII_GX_H + +namespace GX { + enum TextureFormat { + I4 = 0, + I8 = 1, + IA4 = 2, + IA8 = 3, + RGB565 = 4, + RGB5A3 = 5, + RGBA8 = 6, + CI4 = 8, + CI8 = 9, + CI14X2 = 10, + CMPR = 14 + }; + + enum WrapType { + Clamp = 0, + Repeat = 1, + Mirror = 2 + }; + + enum TextureFilter { + Near = 0, + Linear = 1, + NearMipNear = 2, + LinMipNear = 3, + NearMipLin = 4, + LinMipLin = 5 + }; +}; + +#endif // WII_GX_H diff --git a/wii/texpalette.cpp b/wii/texpalette.cpp new file mode 100644 index 0000000..125454f --- /dev/null +++ b/wii/texpalette.cpp @@ -0,0 +1,284 @@ +#include "texpalette.h" + +WiiTexPalette::WiiTexPalette() { } + +WiiTexPalette::WiiTexPalette(QDataStream &stream) { + quint32 magic; + stream >> (quint32&)magic; + + if (magic != 0x20AF30) + qWarning() << "WiiTexPalette: tried to load a TPL without the proper magic"; + + quint32 textureCount, headerSize; + stream >> textureCount; + stream >> headerSize; + + qDebug() << textureCount << "textures"; + textures.resize(textureCount); + + for (int i = 0; i < textureCount; i++) { + quint32 textureOffs, paletteOffs; + stream >> textureOffs; + stream >> paletteOffs; + + int savePos = stream.device()->pos(); + + readTexture(stream, textureOffs, paletteOffs, textures[i]); + + stream.device()->seek(savePos); + } +} + +static const int TexelWidths[] = { + 8, 8, 8, 4, 4, 4, 4, -1, 8, 8, 4 +}; + +static const int TexelHeights[] = { + 8, 4, 4, 4, 4, 4, 4, -1, 8, 4, 4 +}; + +static const int BitsPerPixel[] = { + 4, 8, 8, 16, 16, 16, 32, -1, 8, 16 +}; + +// This bit shamelessly stolen from Dolphin, but it didn't QUITE work right... +/*inline uchar _3to8(uchar v) { return (v << 5) | (v << 2) | (v >> 1); } +inline uchar _4to8(uchar v) { return (v << 4) | v; } +inline uchar _5to8(uchar v) { return (v << 3) | (v << 2); } +inline uchar _6to8(uchar v) { return (v << 2) | (v >> 4); }*/ +inline uchar _3to8(uchar v) { return (v << 5); } +inline uchar _4to8(uchar v) { return (v << 4); } +inline uchar _5to8(uchar v) { return (v << 3); } +inline uchar _6to8(uchar v) { return (v << 2); } + +void WiiTexPalette::readTexture(QDataStream &in, int textureOffs, int paletteOffs, WiiTPLTexture &tex) { + in.device()->seek(textureOffs); + + quint16 width, height; + quint32 rawFormat, dataOffs, rawWrapS, rawWrapT, rawMinFilter, rawMagFilter; + quint8 rawEdgeLODEnable; + + in >> height; + in >> width; + in >> rawFormat; + in >> dataOffs; + in >> rawWrapS; + in >> rawWrapT; + in >> rawMinFilter; + in >> rawMagFilter; + in >> tex.lodBias; + in >> rawEdgeLODEnable; + in >> tex.minLOD; + in >> tex.maxLOD; + + tex.format = (GX::TextureFormat)rawFormat; + tex.wrapS = (GX::WrapType)rawWrapS; + tex.wrapT = (GX::WrapType)rawWrapT; + tex.minFilter = (GX::TextureFilter)rawMinFilter; + tex.magFilter = (GX::TextureFilter)rawMagFilter; + tex.edgeLODEnable = (rawEdgeLODEnable > 0); + + bool formatValid = ( + (rawFormat <= 6) || + (rawFormat >= 8 && rawFormat <= 10) || + rawFormat == 14); + + if (!formatValid) { + qWarning("unknown texture format (%d)", rawFormat); + return; + } + + tex.image = QImage(width, height, QImage::Format_ARGB32); + + int texelWidth = TexelWidths[rawFormat]; + int texelHeight = TexelHeights[rawFormat]; + int bpp = BitsPerPixel[rawFormat]; + + // how much needs to be added on to get this texture aligned? + int padWidth = width % texelWidth; + int padHeight = height % texelHeight; + + // get the fully padded width + int paddedWidth = width + ((padWidth > 0) ? (texelWidth - padWidth) : 0); + int paddedHeight = height + ((padHeight > 0) ? (texelHeight - padHeight) : 0); + + int texDataSize = (paddedWidth * paddedHeight * bpp) / 8; + + // decode the thing + in.device()->seek(dataOffs); + + QImage &image = tex.image; + + switch (tex.format) { + case GX::I4: + { + for (int texelY = 0; texelY < paddedHeight; texelY += 8) { + for (int texelX = 0; texelX < paddedWidth; texelX += 8) { + for (int y = texelY; y < (texelY + 8); y++) { + QRgb *scanline = (QRgb*)image.scanLine(y); + + for (int x = texelX; x < (texelX + 8); x += 2) { + quint8 v; + in >> v; + + if (x < width && y < height) { + int _v = _4to8(v >> 4); + scanline[x] = qRgb(_v, _v, _v); + } + if ((x+1) < width && y < height) { + int _v = _4to8(v & 0xF); + scanline[x+1] = qRgb(_v, _v, _v); + } + } + } + } + } + } + break; + case GX::I8: + { + for (int texelY = 0; texelY < paddedHeight; texelY += 4) { + for (int texelX = 0; texelX < paddedWidth; texelX += 8) { + for (int y = texelY; y < (texelY + 4); y++) { + QRgb *scanline = (QRgb*)image.scanLine(y); + + for (int x = texelX; x < (texelX + 8); x++) { + quint8 v; + in >> v; + + if (x < width && y < height) + scanline[x] = qRgb(v, v, v); + } + } + } + } + } + break; + case GX::IA4: + { + for (int texelY = 0; texelY < paddedHeight; texelY += 4) { + for (int texelX = 0; texelX < paddedWidth; texelX += 8) { + for (int y = texelY; y < (texelY + 4); y++) { + QRgb *scanline = (QRgb*)image.scanLine(y); + + for (int x = texelX; x < (texelX + 8); x++) { + quint8 v; + in >> v; + + if (x < width && y < height) { + int _i = _4to8(v & 0xF); + int _a = _4to8(v >> 4); + scanline[x] = qRgba(_i, _i, _i, _a); + } + } + } + } + } + } + break; + case GX::IA8: + { + for (int texelY = 0; texelY < paddedHeight; texelY += 4) { + for (int texelX = 0; texelX < paddedWidth; texelX += 4) { + for (int y = texelY; y < (texelY + 4); y++) { + QRgb *scanline = (QRgb*)image.scanLine(y); + + for (int x = texelX; x < (texelX + 4); x++) { + quint16 v; + in >> v; + + if (x < width && y < height) + scanline[x] = qRgba((v&0xFF00)>>8, (v&0xFF00)>>8, (v&0xFF00)>>8, v&0xFF); + } + } + } + } + } + break; + case GX::RGB565: + { + for (int texelY = 0; texelY < paddedHeight; texelY += 4) { + for (int texelX = 0; texelX < paddedWidth; texelX += 4) { + for (int y = texelY; y < (texelY + 4); y++) { + QRgb *scanline = (QRgb*)image.scanLine(y); + + for (int x = texelX; x < (texelX + 4); x++) { + quint16 v; + in >> v; + + if (x < width && y < height) { + scanline[x] = qRgb( + _5to8((v >> 11) & 0x1F), + _6to8((v >> 5) & 0x3F), + _5to8(v & 0x1F)); + } + } + } + } + } + } + break; + case GX::RGB5A3: + { + for (int texelY = 0; texelY < paddedHeight; texelY += 4) { + for (int texelX = 0; texelX < paddedWidth; texelX += 4) { + for (int y = texelY; y < (texelY + 4); y++) { + QRgb *scanline = (QRgb*)image.scanLine(y); + + for (int x = texelX; x < (texelX + 4); x++) { + quint16 v; + in >> v; + + if (x < width && y < height) { + if (v & 0x8000) { + scanline[x] = qRgb( + _5to8((v >> 10) & 0x1F), + _5to8((v >> 5) & 0x1F), + _5to8(v & 0x1F)); + } else { + scanline[x] = qRgba( + _4to8((v >> 8) & 0xF), + _4to8((v >> 4) & 0xF), + _4to8(v & 0xF), + _3to8((v >> 12) & 0x7)); + } + } + } + } + } + } + } + break; + case GX::RGBA8: + { + QByteArray texel1(32, 0), texel2(32, 0); + + for (int texelY = 0; texelY < paddedHeight; texelY += 4) { + for (int texelX = 0; texelX < paddedWidth; texelX += 4) { + in.readRawData(texel1.data(), 32); + in.readRawData(texel2.data(), 32); + + int offs = 0; + + for (int y = texelY; y < (texelY + 4); y++) { + QRgb *scanline = (QRgb*)image.scanLine(y); + + for (int x = texelX; x < (texelX + 4); x++) { + if (x < width && y < height) { + scanline[x] = qRgba(texel1.at(offs+1), texel2.at(offs), texel2.at(offs+1), texel1.at(offs)); + } + offs += 2; + } + } + } + } + } + break; + default: + qWarning("unhandled texture format (%d)", rawFormat); + } +} + + +void WiiTexPalette::writeToDataStream(QDataStream &out) const { +} diff --git a/wii/texpalette.h b/wii/texpalette.h new file mode 100644 index 0000000..ff6a4bd --- /dev/null +++ b/wii/texpalette.h @@ -0,0 +1,37 @@ +#ifndef WIITEXPALETTE_H +#define WIITEXPALETTE_H + +#include "common.h" +#include "gx.h" +#include <QImage> + +class WiiTPLTexture { +public: + // TODO: palette stuff + + QImage image; + GX::TextureFormat format; + + GX::WrapType wrapS, wrapT; + + GX::TextureFilter minFilter, magFilter; + + float lodBias; + bool edgeLODEnable; + quint8 minLOD, maxLOD; +}; + +class WiiTexPalette { +public: + WiiTexPalette(); + WiiTexPalette(QDataStream &stream); + + void writeToDataStream(QDataStream &out) const; + + QVector<WiiTPLTexture> textures; + +private: + void readTexture(QDataStream &in, int textureOffs, int paletteOffs, WiiTPLTexture &tex); +}; + +#endif // WIITEXPALETTE_H |