Skip to content
This repository has been archived by the owner on Nov 12, 2024. It is now read-only.

Commit

Permalink
Updated to 0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
soywiz committed Sep 11, 2016
1 parent 12ca204 commit c29ce95
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 31 deletions.
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## VitaOrganizer 0.3
## VitaOrganizer 0.4

![](extra/screenshot-0.3.png)
![](extra/screenshot-0.4.png)

Desktop tool for listing and uploading games and homebrew applications to PSVITA without the size requirements
of uploading the whole VPK and extracting it later.
Expand All @@ -10,13 +10,25 @@ It is written in Kotlin/Java.
It should work on Windows, Linux and MacOS. It is a Java desktop application, packed in an executable .JAR, that
can be executed directly with double click on most cases.

In other cases, you can run it with `java -jar vitaorganizer-0.3.jar`
In other cases, you can run it with `java -jar vitaorganizer-0.4.jar`

You can download a prebuild binary here, or just build from source:
[Download VitaOrganizer 0.3 here](https://github.com/soywiz/vitaorganizer/releases/download/0.3/vitaorganizer-0.3.jar)
[Download VitaOrganizer 0.4 here](https://github.com/soywiz/vitaorganizer/releases/download/0.4/vitaorganizer-0.4.jar)

## CHANGELOG

**0.4**

* Improved row selection
* Supported translations (please, go to github if you want to translate to your own language)
* Show file in explorer/finder
* Show PSF dialog
* Version column useful for homebrew
* Repack : Compression 9 + Remove duplicates + Make it safe (for backups done with older versions of vitamin, or homebrew done with older versions of vitasdk or without -s but that do not require special permissions)
* Queue tasks (not displaying yet, but already allows to queue)
* Added menus that will provide more features in future versions
* Lots of internal improvements

**0.3**

* Fixes size of games in psvita (please delete vitaorganizer/cache folder)
Expand Down
9 changes: 7 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
group 'com.soywiz'
version new File('resources/currentVersion.txt').text
version new File('resources/com/soywiz/vitaorganizer/currentVersion.txt').text

buildscript {
ext.version = new File('resources/currentVersion.txt').text
ext.version = new File('resources/com/soywiz/vitaorganizer/currentVersion.txt').text
ext.kotlin_version = '1.0.3'
ext.proguard_version = '5.2.1'
ext.launch4j_version = '1.6.2'
Expand Down Expand Up @@ -76,6 +76,11 @@ task minimizedJar(type: proguard.gradle.ProGuardTask) {
name: 'main',
parameters: 'java.lang.String[]'
}
keep access: 'public',
name: 'kotlin.text.RegexOption', {
method access: 'public'
method access: 'private'
}
}

minimizedJar.dependsOn jar
Expand Down
Binary file added extra/screenshot-0.4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion lastVersion.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
0.3
0.4
https://github.com/soywiz/vitaorganizer
2 changes: 1 addition & 1 deletion resources/com/soywiz/vitaorganizer/currentVersion.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3
0.4
16 changes: 16 additions & 0 deletions src/com/soywiz/util/OS.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.soywiz.util

object OS {
private val OS = System.getProperty("os.name").toLowerCase()

val isWindows: Boolean get() = OS.indexOf("win") >= 0
val isMac: Boolean get() = OS.indexOf("mac") >= 0
val isUnix: Boolean get() = OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0
val isSolaris: Boolean get() = OS.indexOf("sunos") >= 0
val os: String
get() = if (isWindows) "win"
else if (isMac) "osx"
else if (isUnix) "uni"
else if (isSolaris) "sol"
else "unknown"
}
4 changes: 3 additions & 1 deletion src/com/soywiz/vitaorganizer/GameEntry.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.soywiz.vitaorganizer

import com.soywiz.util.stream
import java.io.File

class GameEntry(val gameId: String) {
val entry = VitaOrganizerCache.entry(gameId)
Expand All @@ -22,7 +23,8 @@ class GameEntry(val gameId: String) {
val title by lazy { psf["TITLE"].toString() }
var inVita = false
var inPC = false
val vpkFile: String? get() = entry.pathFile.readText(Charsets.UTF_8)
val vpkLocalPath: String? get() = entry.pathFile.readText(Charsets.UTF_8)
val vpkLocalFile: File? get() = if (vpkLocalPath != null) File(vpkLocalPath!!) else null
val size: Long by lazy {
try {
entry.sizeFile.readText().toLong()
Expand Down
26 changes: 18 additions & 8 deletions src/com/soywiz/vitaorganizer/VitaOrganizer.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.soywiz.vitaorganizer

import com.soywiz.util.OS
import com.soywiz.util.open2
import com.soywiz.vitaorganizer.ext.action
import com.soywiz.vitaorganizer.ext.getResourceString
Expand Down Expand Up @@ -82,6 +83,14 @@ object VitaOrganizer : JPanel(BorderLayout()), StatusUpdater {
table.setEntries(ALL_GAME_IDS.values.toList())
}

fun showFileInExplorerOrFinder(file: File) {
if (OS.isWindows) {
ProcessBuilder("explorer.exe", "/select," + file.absolutePath).start().waitFor()
} else {
ProcessBuilder("open", "-R", file.absolutePath).start().waitFor()
}
}

val table = object : GameListTable() {
val dialog = this@VitaOrganizer
val gameTitlePopup = JMenuItem("").apply {
Expand Down Expand Up @@ -120,11 +129,20 @@ object VitaOrganizer : JPanel(BorderLayout()), StatusUpdater {
init {
add(gameTitlePopup)
add(JSeparator())
add(JMenuItem(if (OS.isWindows) "Show file in explorer" else "Show file in finder").action {
if (entry != null) {
showFileInExplorerOrFinder(entry!!.vpkLocalFile!!)
}
})
add(JMenuItem(Texts.format("MENU_SHOW_PSF")).action {
if (entry != null) {
frame.showDialog(KeyValueViewerFrame(Texts.format("PSF_VIEWER_TITLE", "id" to entry!!.id, "title" to entry!!.title), entry!!.psf))
}
})
add(JMenuItem("Repack : Compression 9 + Remove duplicates + Make is safe").action {
if (entry != null) remoteTasks.queue(RepackVpkTask(entry!!, setSecure = true))
})

add(JSeparator())
//add(deleteFromVita)
add(sendVpkToVita)
Expand Down Expand Up @@ -373,16 +391,8 @@ object VitaOrganizer : JPanel(BorderLayout()), StatusUpdater {
}
}

val checkUpdatesButton = JButton(Texts.format("MENU_CHECK_FOR_UPDATES")).apply {
addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
checkForUpdates()
}
})
}
add(connectButton)
add(connectAddress)
add(checkUpdatesButton)
}

add(header, SpringLayout.NORTH)
Expand Down
8 changes: 8 additions & 0 deletions src/com/soywiz/vitaorganizer/VitaOrganizerCache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ object VitaOrganizerCache {
val pathFile = cacheFolder["$gameId.path"]
val sizeFile = cacheFolder["$gameId.size"]
val permissionsFile = cacheFolder["$gameId.extperm"]

fun delete() {
icon0File.delete()
paramSfoFile.delete()
pathFile.delete()
sizeFile.delete()
permissionsFile.delete()
}
}

fun entry(gameId: String) = Entry(gameId)
Expand Down
2 changes: 1 addition & 1 deletion src/com/soywiz/vitaorganizer/tasks/OneStepToVitaTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class OneStepToVitaTask(val entry: GameEntry) : VitaTask() {
override fun perform() {
sendPromotingVpkTask.perform()

updateStatus("Promoting VPK (this could take a while)...")
status("Promoting VPK (this could take a while)...")
PsvitaDevice.promoteVpk(sendPromotingVpkTask.vpkPath)
PsvitaDevice.removeFile(sendPromotingVpkTask.vpkPath)

Expand Down
79 changes: 79 additions & 0 deletions src/com/soywiz/vitaorganizer/tasks/RepackVpkTask.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.soywiz.vitaorganizer.tasks

import com.soywiz.vitaorganizer.FileSize
import com.soywiz.vitaorganizer.GameEntry
import com.soywiz.vitaorganizer.VitaOrganizer
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.zip.*

class RepackVpkTask(val entry: GameEntry, val compression: Int = Deflater.BEST_COMPRESSION, val setSecure: Boolean? = null) : VitaTask() {
override fun perform() {
status("Repacking vpk...")
val file = entry.vpkLocalFile!!
val tempFile = File("${file.absolutePath}.temp")
val tempFile2 = File("${file.absolutePath}.temp2")

val temp = ByteArray(10 * 1024 * 1024)

ZipFile(file).use { zip ->
FileOutputStream(tempFile).use { out ->
ZipOutputStream(out).use { zout ->
zout.setLevel(compression)
val entries = zip.entries().toList().distinctBy { it.name }
var currentSize = 0L
val totalSize = entries.map { it.size }.sum()

for ((index, e) in entries.withIndex()) {

fun updateStatus() {
status("Repacking ${index}/${entries.size} :: ${FileSize.toString(currentSize)}/${FileSize.toString(totalSize)}")
progress(index, entries.size)
}

updateStatus()
zout.putNextEntry(ZipEntry(e.name))
if (e.name == "eboot.bin" && setSecure != null) {
val full = zip.getInputStream(e).readBytes()
if (setSecure) {
full[0x80] = (full[0x80].toInt() and 1.inv()).toByte() // Remove bit 0
} else {
full[0x80] = (full[0x80].toInt() or 1).toByte() // Set bit 0
}
zout.write(full)
currentSize += full.size
} else {
zip.getInputStream(e).use {
var localSize = 0L
while (it.available() > 0) {
val bytes = it.read(temp)
if (bytes <= 0) break
zout.write(temp, 0, bytes)
currentSize += bytes
localSize += bytes
if (localSize >= 1 * 1024 * 1024) {
localSize -= 1 * 1024 * 1024
updateStatus()
}
}
}
updateStatus()
}

zout.closeEntry()
}
}
}
}
status("Done...")

file.renameTo(tempFile2)
tempFile.renameTo(file)
tempFile2.delete()
entry.entry.delete() // flush this info!

VitaOrganizer.updateFileList()
}
}
8 changes: 4 additions & 4 deletions src/com/soywiz/vitaorganizer/tasks/SendDataToVitaTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import javax.swing.JOptionPane

class SendDataToVitaTask(val entry: GameEntry) : VitaTask() {
override fun perform() {
updateStatus("Sending game ${entry.id}...")
status("Sending game ${entry.id}...")
//val zip = ZipFile(entry.vpkFile)
try {
PsvitaDevice.uploadGame(entry.id, ZipFile(entry.vpkFile), filter = { path ->
PsvitaDevice.uploadGame(entry.id, ZipFile(entry.vpkLocalPath), filter = { path ->
// Skip files already installed in the VPK
if (path == "eboot.bin" || path.startsWith("sce_sys/")) {
false
Expand All @@ -20,13 +20,13 @@ class SendDataToVitaTask(val entry: GameEntry) : VitaTask() {
}
}) { status ->
//println("$status")
updateStatus("Uploading ${entry.id} :: ${status.fileRange} :: ${status.sizeRange}")
status("Uploading ${entry.id} :: ${status.fileRange} :: ${status.sizeRange}")
}
//statusLabel.text = "Processing game ${vitaGameCount + 1}/${vitaGameIds.size} ($gameId)..."
} catch (e: Throwable) {
error(e.toString())
}
updateStatus("Sent game data ${entry.id}")
status("Sent game data ${entry.id}")
info("Game ${entry.id} sent successfully")
}
}
10 changes: 5 additions & 5 deletions src/com/soywiz/vitaorganizer/tasks/SendPromotingVpkToVitaTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import java.util.zip.ZipFile
import javax.swing.JOptionPane

class SendPromotingVpkToVitaTask(val entry: GameEntry) : VitaTask() {
val zip = ZipFile(entry.vpkFile)
val zip = ZipFile(entry.vpkLocalPath)
val vpkPath = "ux0:/organizer/${entry.id}.VPK"

override fun checkBeforeQueue() {
Expand All @@ -18,23 +18,23 @@ class SendPromotingVpkToVitaTask(val entry: GameEntry) : VitaTask() {
}

override fun perform() {
updateStatus(Texts.format("STEP_GENERATING_SMALL_VPK_FOR_PROMOTING"))
status(Texts.format("STEP_GENERATING_SMALL_VPK_FOR_PROMOTING"))

//val zip = ZipFile(entry.vpkFile)
try {
val vpkData = createSmallVpk(zip)

updateStatus(Texts.format("STEP_GENERATED_SMALL_VPK_FOR_PROMOTING"))
status(Texts.format("STEP_GENERATED_SMALL_VPK_FOR_PROMOTING"))

PsvitaDevice.uploadFile("/$vpkPath", vpkData) { status ->
progress(status.currentSize, status.totalSize)
updateStatus(Texts.format("STEP_UPLOADING_VPK_FOR_PROMOTING", "current" to status.currentSize, "total" to status.totalSize))
status(Texts.format("STEP_UPLOADING_VPK_FOR_PROMOTING", "current" to status.currentSize, "total" to status.totalSize))
}
} catch (e: Throwable) {
e.printStackTrace()
JOptionPane.showMessageDialog(VitaOrganizer, "${e.toString()}", "${e.message}", JOptionPane.ERROR_MESSAGE);
}
updateStatus("Sent game vpk ${entry.id}")
status("Sent game vpk ${entry.id}")
info("Now use VitaShell to install\n$vpkPath\n\nAfter that active ftp again and use this program to Send Data to PSVita")

zip.close()
Expand Down
6 changes: 3 additions & 3 deletions src/com/soywiz/vitaorganizer/tasks/UpdateFileListTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ class UpdateFileListTask : VitaTask() {
VitaOrganizer.VPK_GAME_IDS.clear()
}
val vpkFiles = File(VitaOrganizerSettings.vpkFolder).listFiles().filter { it.name.toLowerCase().endsWith(".vpk") }
updateStatus(Texts.format("STEP_ANALYZING_FILES", "folder" to VitaOrganizerSettings.vpkFolder))
status(Texts.format("STEP_ANALYZING_FILES", "folder" to VitaOrganizerSettings.vpkFolder))
var count = 0
for (vpkFile in File(VitaOrganizerSettings.vpkFolder).listFiles().filter { it.name.toLowerCase().endsWith(".vpk") }) {
//println(vpkFile)
updateStatus(Texts.format("STEP_ANALYZING_ITEM", "name" to vpkFile.name, "current" to count + 1, "total" to vpkFiles.size))
status(Texts.format("STEP_ANALYZING_ITEM", "name" to vpkFile.name, "current" to count + 1, "total" to vpkFiles.size))
try {
val zip = ZipFile(vpkFile)
val paramSfoData = zip.getBytes("sce_sys/param.sfo")
Expand Down Expand Up @@ -50,7 +50,7 @@ class UpdateFileListTask : VitaTask() {
e.printStackTrace()
}
}
updateStatus(Texts.format("STEP_DONE"))
status(Texts.format("STEP_DONE"))
VitaOrganizer.updateEntries()
}
}
6 changes: 5 additions & 1 deletion src/com/soywiz/vitaorganizer/tasks/VitaTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ open class VitaTask {
val globalProgress = Progress(0L, 0L)
val localProgress = Progress(0L, 0L)

fun updateStatus(status: String) {
fun status(status: String) {
SwingUtilities.invokeLater {
VitaOrganizer.updateStatus(status)
}
Expand All @@ -43,6 +43,10 @@ open class VitaTask {
localProgress.set(current, max)
}

fun progress(current: Int, max: Int) {
localProgress.set(current.toLong(), max.toLong())
}

open fun checkBeforeQueue() {
}

Expand Down
1 change: 1 addition & 0 deletions src/com/soywiz/vitaorganizer/tasks/createSmallVpk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ fun createSmallVpk(zip: ZipFile): ByteArray {
if (e.name == "eboot.bin" || e.name.startsWith("sce_sys/")) {
out.putNextEntry(ZipEntry(e.name))
out.write(zip.getInputStream(e).readBytes())
out.closeEntry()
}
}

Expand Down

0 comments on commit c29ce95

Please # to comment.