Skip to content

Commit

Permalink
LES GO
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacob Clarke committed May 14, 2022
1 parent 0dcc97d commit 0dc0192
Show file tree
Hide file tree
Showing 19 changed files with 443 additions and 14 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**_DEBUG.*
**_INJECTED.*
TouchOSC_ScriptInjector
TouchOSC_ScriptInjector*
5 changes: 5 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 120
}
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
"deno.enable": true,
"xml.server.vmargs": "-Xmx512M",
"xml.symbols.maxItemsComputed": 50000,
"xml.format.joinContentLine": true
}
44 changes: 41 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
### VSCode plugins:
[redhat.vscode-xml](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-xml)
[denoland.vscode-deno](https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno)
# TouchOSC Script Injector

A neat little CLI tool that allows you to write TouchOSC LUA scripts externally by injecting them into the TouchOSC project file directly, with some bonus smarts.

## Project setup

In the same folder as your `.tosc` project file make a folder called `scripts`, in there you can make as many `.lua` files as you want.

`_globals.lua` is a special file that is always injected into the top of all other scripts.

`_root.lua` will get applied to your document's root script.

All other `.lua` files will map directly to any controls of the same name. By pre-pending a file name with `tag_` the mapping will be done by tag instead of name.

This name/tag script copying process will find controls nested in however many groups.

## Using this thing

Head to [releases](https://github.com/jacobclarke92/TouchOSC-Script-Injector/releases) and download the version best suited for your OS.
Windows, Mac and Linux options available but I've only tested Mac so far.

Simply run the program and follow the prompts!
You will be prompted for your project file, drag your `.tosc` file into the window and press enter and you'll be away.

The program will run in its entirety after initially receiving a project file. It will creating a new `_INJECTED.tosc` file next to your original.

After this point the program will monitor your `scripts` folder and watch for any changes to `.lua` files, patching your project file as necessary.

---

## Dev stuff

Built with DENO just because I like typescript and the compile functionality seemed pretty convenient for distribution.

`.tosc` files by default are compressed with zlib, if decompressed end up just being an xml file.

Check the scripts inside `package.json` for quick access but otherwise this should be all you need to get started:
```
deno run --allow-read --allow-write src/index.ts
```
There is a `--debug` argument that adds extra logs and outputs a debug `.json` file, and you can also pass a `.tosc` file path (relative or absolute) as an argument to skip the prompting step.

Binary file added example/Project.tosc
Binary file not shown.
2 changes: 2 additions & 0 deletions example/scripts/_globals.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
NAME_COLOR = '00ff00'
TAG_COLOR = '0000ff'
1 change: 1 addition & 0 deletions example/scripts/_root.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
print('Hello from root')
1 change: 1 addition & 0 deletions example/scripts/example.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
self.color = Color.fromHexString(NAME_COLOR)
1 change: 1 addition & 0 deletions example/scripts/tag_example.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
self.color = Color.fromHexString(TAG_COLOR)
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@
"name": "touchosc-script-injector",
"version": "1.0.0",
"description": "Injects external LUA files into TouchOSC control script fields",
"main": "watch.js",
"main": "src/index.js",
"author": "Jacob Clarke",
"license": "MIT",
"scripts": {
"start": "deno run watch.ts --allow-read --allow-write"
"start": "deno run --allow-read --allow-write src/index.ts",
"dev": "deno run --allow-read --allow-write src/index.ts --debug",
"compile": "deno compile --allow-read --allow-write --output TouchOSC_ScriptInjector src/index.ts",
"compile-linux": "deno compile --allow-read --allow-write --target x86_64-unknown-linux-gnu --output TouchOSC_ScriptInjector_linux src/index.ts",
"compile-windows": "deno compile --allow-read --allow-write --target x86_64-pc-windows-msvc --output TouchOSC_ScriptInjector_windows src/index.ts",
"compile-mac": "deno compile --allow-read --allow-write --target x86_64-apple-darwin --output TouchOSC_ScriptInjector_mac src/index.ts",
"compile-macM1": "deno compile --allow-read --allow-write --target aarch64-apple-darwin --output TouchOSC_ScriptInjector_macM1 src/index.ts",
"compile-all": "npm run compile-linux && npm run compile-windows && npm run compile-mac && npm run compile-macM1"
}
}
2 changes: 0 additions & 2 deletions scripts/_globals.lua

This file was deleted.

1 change: 0 additions & 1 deletion scripts/example.lua

This file was deleted.

1 change: 0 additions & 1 deletion scripts/tag_example.lua

This file was deleted.

22 changes: 22 additions & 0 deletions src/banner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
const banner = `
_____ _ _____ _____ _____
|_ _|___ _ _ ___| |_| | __| |
| | | . | | | _| | | |__ | --|
|_| |___|___|___|_|_|_____|_____|_____|
_____ _ __ ____ _ __
/ ___/__________(_)___ / /_ / _/___ (_)__ _____/ /_____ _____
\\__ \\/ ___/ ___/ / __ \\/ __/ / // __ \\ / / _ \\/ ___/ __/ __ \\/ ___/
___/ / /__/ / / / /_/ / /_ _/ // / / / / / __/ /__/ /_/ /_/ / /
/____/\\___/_/ /_/ .___/\\__/ /___/_/ /_/_/ /\\___/\\___/\\__/\\____/_/
/_/ /___/
by Jacob Clarke
https://github.com/jacobclarke92/TouchOSC-Script-Injector
`
export const printBanner = (clearFirst = false) => {
if (clearFirst) console.clear()
console.log(banner)
}
69 changes: 69 additions & 0 deletions src/fileHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { ToscDoc, ToscNode, ToscGroupNode, ToscProperty } from './types.ts'
import { stopwatchTick } from './main.ts'
import { parse as parseXml, stringify as encodeXml } from 'https://deno.land/x/[email protected]/mod.ts'
import { XmlEntities } from 'https://deno.land/x/[email protected]/mod.js'
import { inflate } from 'https://deno.land/x/[email protected]/zlib/inflate.ts'

export async function getToscFileContent(filePath: string) {
const content = await Deno.readTextFile(filePath)
if (content.match(/^<\?xml/)) {
console.log(`✅ Project file is XML-based`)
return content
}

console.log(`Project file is not XML based, attempting to convert...`)
stopwatchTick()
const decodedFile = await decodeToscFile(filePath)
console.log(`✅ Decoded file in ${stopwatchTick()}ms`)
return decodedFile
}

export async function decodeToscFile(filePath: string) {
try {
const rawContent = await Deno.readFile(filePath)
const inflatedContent = inflate(rawContent)
return new TextDecoder('utf-8').decode(inflatedContent)
} catch (err) {
throw '❌ Failed to decode file'
}
}

export function parseToscXML(xmlString: string): ToscDoc {
stopwatchTick()
try {
const json = parseXml(xmlString) as unknown as ToscDoc
console.log(`✅ XML successfully parsed (took ${stopwatchTick()} ms)`)
return json
} catch (e) {
throw '❌ Could not parse XML file'
}
}

export async function writeDebugFiles(fileDir: string, fileName: string, parsedProject: ToscDoc) {
stopwatchTick()
await Deno.writeTextFile(fileDir + fileName + '_DEBUG.json', JSON.stringify(parsedProject, null, 2))
console.log(`✅ Wrote to JSON file for debugging (took ${stopwatchTick()} ms)`)

stopwatchTick()
await Deno.writeTextFile(
fileDir + fileName + '_DEBUG.tosc',
encodeXml(parsedProject as any, { replacer: cDataRestorer })
)
console.log(`✅ Wrote to XML file for debugging (took ${stopwatchTick()} ms)`)
}

type StringifierOptions = Exclude<Parameters<typeof encodeXml>[1], undefined>
const cDataRestorer: StringifierOptions['replacer'] = ({ key, value, tag }) =>
['key', 'value'].includes(tag) && key === '#text' && typeof value === 'string' && !!value
? `<![CDATA[${XmlEntities.decode(value)}]]>`
: value

export async function writeProjectFile(parsedProject: ToscDoc, fileDir: string, fileName: string) {
console.log('📝 Re-encoding to XML and saving file...')
stopwatchTick()
const xmlString = encodeXml(parsedProject as any, { indentSize: 0, replacer: cDataRestorer })
const newFileName = fileDir + fileName + '_INJECTED.tosc'
await Deno.writeTextFile(newFileName, xmlString)
console.log(`✅ Project file written (took ${stopwatchTick()} ms)`)
console.log(newFileName)
}
58 changes: 58 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { printBanner } from './banner.ts'
import { processToscFile, startWatcher } from './main.ts'
import Ask from 'https://deno.land/x/[email protected]/mod.ts'

const debugMode = !!(Deno.args || []).find((arg) => arg === '--debug')
const filePathArg = (Deno.args || []).find((str) => str.match(/\.tosc$/))
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))

export const fileNameAndExtRegex = /[^\\/:"*?<>|]+\.\w+$/
export const fileNameRegex = /([^\\/:"*?<>|]+)(?:\.\w+$)/
const runtimePath = new URL('', import.meta.url).pathname
const runtimeDir = runtimePath.replace(fileNameAndExtRegex, '')

const ask = new Ask()
const askForFilePath = async () =>
(
await ask.input({ name: 'filePath', message: `Drag a .tosc file into this window, then press enter\n` })
)?.filePath?.trim()

const ensureFilePath = async () => {
let answer = await askForFilePath()
while (!answer) answer = await askForFilePath()
const isAbsolutePath = answer.match(/^\//)
if (!isAbsolutePath) answer = runtimeDir + answer
return answer
}

if (!debugMode) printBanner(true)

let filePath: string = filePathArg ? filePathArg.replace(/^\.\//, '') : await ensureFilePath()
let scriptsDir: string = filePath.replace(fileNameAndExtRegex, '') + 'scripts/'

async function letsGo() {
const parsedProject = await (async () => {
try {
return await processToscFile(filePath, scriptsDir, debugMode)
} catch (e) {
console.log(e)
return false
}
})()

if (!parsedProject) {
console.log('❌ An issue occurred, will restart shortly...')
await sleep(2500)
if (!debugMode) printBanner(true)
filePath = await ensureFilePath()
scriptsDir = filePath.replace(fileNameAndExtRegex, '') + 'scripts/'
await letsGo()
return
}

console.log('\n🎉🎉🎉🎉🎉\n')

await startWatcher(parsedProject, filePath, scriptsDir)
}

letsGo()
Loading

0 comments on commit 0dc0192

Please sign in to comment.