diff --git a/examples/2048/2048.v b/examples/2048/2048.v index 902265e84c..e94f6e2ae4 100644 --- a/examples/2048/2048.v +++ b/examples/2048/2048.v @@ -1,7 +1,7 @@ import gg import gx import math -import os +import os.asset import rand import time @@ -899,10 +899,6 @@ fn init(mut app App) { fn main() { mut app := &App{} app.new_game() - mut font_path := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) - $if android { - font_path = 'fonts/RobotoMono-Regular.ttf' - } app.gg = gg.new_context( bg_color: app.theme.bg_color width: default_window_width @@ -913,7 +909,7 @@ fn main() { event_fn: on_event init_fn: init user_data: app - font_path: font_path + font_path: asset.get_path('../assets', 'fonts/RobotoMono-Regular.ttf') ) app.gg.run() } diff --git a/examples/clock/clock.v b/examples/clock/clock.v index 9c9b69ee8b..2b568519d5 100644 --- a/examples/clock/clock.v +++ b/examples/clock/clock.v @@ -2,7 +2,6 @@ // This is a small V example that was based off of the fireworks example. // Written by Stefan Schroeder in 2021 for the v project examples. // See LICENSE for license information. -import os import gg import gx import math @@ -147,13 +146,7 @@ fn on_init(mut app App) { fn main() { println("Press 'q' to quit.") - mut font_path := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) - $if android { - font_path = 'fonts/RobotoMono-Regular.ttf' - } - mut app := &App{} - app.gg = gg.new_context( width: design_size height: design_size @@ -163,8 +156,6 @@ fn main() { frame_fn: on_frame event_fn: on_event init_fn: on_init - font_path: font_path ) - app.gg.run() } diff --git a/examples/fireworks/fireworks.v b/examples/fireworks/fireworks.v index aac873339e..54a20f296b 100644 --- a/examples/fireworks/fireworks.v +++ b/examples/fireworks/fireworks.v @@ -1,4 +1,3 @@ -import os import objects import gg import gx @@ -94,14 +93,8 @@ fn (mut app App) resize() { } fn main() { - mut font_path := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) - $if android { - font_path = 'fonts/RobotoMono-Regular.ttf' - } - mut app := &App{} app.ui = objects.get_params() - app.gg = gg.new_context( width: app.ui.width height: app.ui.height @@ -110,8 +103,6 @@ fn main() { user_data: app frame_fn: on_frame event_fn: on_event - font_path: font_path ) - app.gg.run() } diff --git a/examples/flappylearning/game.v b/examples/flappylearning/game.v index 900b2cb7a6..7ecb10bd6a 100644 --- a/examples/flappylearning/game.v +++ b/examples/flappylearning/game.v @@ -2,11 +2,11 @@ module main import gg import gx -import os import time import math import rand import neuroevolution +import os.asset const win_width = 500 const win_height = 512 @@ -173,10 +173,6 @@ fn (mut app App) update() { fn main() { mut app := &App{} - mut font_path := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) - $if android { - font_path = 'fonts/RobotoMono-Regular.ttf' - } app.gg = gg.new_context( bg_color: gx.white width: win_width @@ -187,7 +183,7 @@ fn main() { event_fn: on_event user_data: app init_fn: app.init_images_wrapper - font_path: font_path + font_path: asset.get_path('../assets', 'fonts/RobotoMono-Regular.ttf') ) app.nv = neuroevolution.Generations{ population: 50 @@ -210,21 +206,10 @@ fn (mut app App) init_images_wrapper() { } fn (mut app App) init_images() ! { - $if android { - background := os.read_apk_asset('img/background.png')! - app.background = app.gg.create_image_from_byte_array(background)! - bird := os.read_apk_asset('img/bird.png')! - app.bird = app.gg.create_image_from_byte_array(bird)! - pipetop := os.read_apk_asset('img/pipetop.png')! - app.pipetop = app.gg.create_image_from_byte_array(pipetop)! - pipebottom := os.read_apk_asset('img/pipebottom.png')! - app.pipebottom = app.gg.create_image_from_byte_array(pipebottom)! - } $else { - app.background = app.gg.create_image(os.resource_abs_path('assets/img/background.png'))! - app.bird = app.gg.create_image(os.resource_abs_path('assets/img/bird.png'))! - app.pipetop = app.gg.create_image(os.resource_abs_path('assets/img/pipetop.png'))! - app.pipebottom = app.gg.create_image(os.resource_abs_path('assets/img/pipebottom.png'))! - } + app.background = app.gg.create_image_from_byte_array(asset.read_bytes('assets', 'img/background.png')!)! + app.bird = app.gg.create_image_from_byte_array(asset.read_bytes('assets', 'img/bird.png')!)! + app.pipetop = app.gg.create_image_from_byte_array(asset.read_bytes('assets', 'img/pipetop.png')!)! + app.pipebottom = app.gg.create_image_from_byte_array(asset.read_bytes('assets', 'img/pipebottom.png')!)! } fn frame(app &App) { diff --git a/examples/gg/raven_text_rendering.v b/examples/gg/raven_text_rendering.v index d2a5ccccef..9e145fa382 100644 --- a/examples/gg/raven_text_rendering.v +++ b/examples/gg/raven_text_rendering.v @@ -2,7 +2,7 @@ module main import gg import gx -import os +import os.asset import math const win_width = 600 @@ -62,10 +62,6 @@ mut: fn main() { mut app := &App{} - mut font_path := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) - $if android { - font_path = 'fonts/RobotoMono-Regular.ttf' - } app.gg = gg.new_context( width: win_width height: win_height @@ -74,7 +70,8 @@ fn main() { user_data: app bg_color: bg_color frame_fn: frame - font_path: font_path // window_user_ptr: ctx + font_path: asset.get_path('../assets', 'fonts/RobotoMono-Regular.ttf') + // window_user_ptr: ctx // native_rendering: true ) app.gg.run() diff --git a/examples/gg/rectangles.v b/examples/gg/rectangles.v index b267b796f3..1211a6cea8 100644 --- a/examples/gg/rectangles.v +++ b/examples/gg/rectangles.v @@ -2,7 +2,7 @@ module main import gg import gx -import os +import os.asset const win_width = 600 const win_height = 300 @@ -23,20 +23,12 @@ fn main() { window_title: 'Rectangles' frame_fn: frame user_data: app - init_fn: init_images ) - mut logo_path := os.resource_abs_path(os.join_path('..', 'assets', 'logo.png')) - $if android { - logo_path = 'logo.png' - } + logo_path := asset.get_path('../assets', 'logo.png') app.image = app.gg.create_image(logo_path)!.id app.gg.run() } -fn init_images(mut app App) { - // app.image = gg.create_image('logo.png') -} - fn frame(app &App) { app.gg.begin() app.draw() diff --git a/examples/gg/rotating_textured_quad.v b/examples/gg/rotating_textured_quad.v index 33060035ea..9c39541a95 100644 --- a/examples/gg/rotating_textured_quad.v +++ b/examples/gg/rotating_textured_quad.v @@ -1,6 +1,6 @@ import gg import gx -import os +import os.asset import sokol.sgl pub struct Window { @@ -10,11 +10,7 @@ pub mut: } pub fn (mut window Window) init() { - image_path := $if android { - 'logo.png' - } $else { - os.resource_abs_path('../assets/logo.png') - } + image_path := asset.get_path('../assets', 'logo.png') window.img = window.ctx.create_image(image_path) or { panic(err) } } diff --git a/examples/gg/stars.v b/examples/gg/stars.v index 8babd0a9e7..0c58390990 100644 --- a/examples/gg/stars.v +++ b/examples/gg/stars.v @@ -1,6 +1,6 @@ module main -import os +import os.asset import gg import gx import rand @@ -78,10 +78,7 @@ fn main() { } fn init_images(mut app App) { - mut logo_path := os.resource_abs_path(os.join_path('..', 'assets', 'logo.png')) - $if android { - logo_path = 'logo.png' - } + logo_path := asset.get_path('../assets', 'logo.png') app.image = app.gg.create_image(logo_path) or { panic(err) } } diff --git a/examples/sokol/06_obj_viewer/modules/obj/util.v b/examples/sokol/06_obj_viewer/modules/obj/util.v index 004831c9ec..cee4f3ccab 100644 --- a/examples/sokol/06_obj_viewer/modules/obj/util.v +++ b/examples/sokol/06_obj_viewer/modules/obj/util.v @@ -1,52 +1,24 @@ module obj import os +import os.asset // read a file as single lines pub fn read_lines_from_file(file_path string) []string { - mut path := '' - mut rows := []string{} - $if android { - path = 'models/' + file_path - bts := os.read_apk_asset(path) or { - eprintln('File [${path}] NOT FOUND!') - return rows - } - rows = bts.bytestr().split_into_lines() - } $else { - path = if os.exists(file_path) { - file_path - } else { - os.resource_abs_path('assets/models/${file_path}') - } - rows = os.read_lines(path) or { - eprintln('File [${path}] NOT FOUND! file_path: ${file_path}') - return rows - } + if os.exists(file_path) { + return os.read_lines(file_path) or { [] } } - return rows + return read_bytes_from_file(file_path).bytestr().split_into_lines() } // read a file as []u8 pub fn read_bytes_from_file(file_path string) []u8 { - mut path := '' - mut buffer := []u8{} - $if android { - path = 'models/' + file_path - buffer = os.read_apk_asset(path) or { - eprintln('Texture file: [${path}] NOT FOUND!') - exit(0) - } - } $else { - path = if os.exists(file_path) { - file_path - } else { - os.resource_abs_path('assets/models/${file_path}') - } - buffer = os.read_bytes(path) or { - eprintln('Texture file: [${path}] NOT FOUND!') - exit(0) - } + if os.exists(file_path) { + return os.read_bytes(file_path) or { [] } + } + mpath := 'models/${file_path}' + return asset.read_bytes('assets', mpath) or { + eprintln('Model file NOT FOUND: `${mpath}`') + exit(0) } - return buffer } diff --git a/examples/tetris/tetris.v b/examples/tetris/tetris.v index 458b7fa293..ad549f9207 100644 --- a/examples/tetris/tetris.v +++ b/examples/tetris/tetris.v @@ -3,7 +3,7 @@ // that can be found in the LICENSE file. module main -import os +import os.asset import rand import time import gx @@ -157,10 +157,6 @@ fn frame(mut game Game) { fn main() { mut game := &Game{} - mut fpath := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) - $if android { - fpath = 'fonts/RobotoMono-Regular.ttf' - } game.gg = gg.new_context( bg_color: gx.white width: win_width @@ -170,7 +166,8 @@ fn main() { user_data: game frame_fn: frame event_fn: on_event - font_path: fpath // wait_events: true + font_path: asset.get_path('../assets', 'fonts/RobotoMono-Regular.ttf') + // wait_events: true ) game.init_game() game.gg.run() // Run the render loop in the main thread diff --git a/vlib/os/asset/README.md b/vlib/os/asset/README.md new file mode 100644 index 0000000000..0a8fe5dc04 --- /dev/null +++ b/vlib/os/asset/README.md @@ -0,0 +1,11 @@ +# Asset + +The `asset` module provides a cross platform way to read assets, without cluttering +the user code with comptime conditionals, like `$if ios {` or `$if android {` etc. + +Currently it supports Android assets, and desktop applications, that do not use +archived/zipped files, but could be extended to support archived files too in the future. + +It relies on the assumption that each platform has a way to either read files, relative +to the location of the executable, or a platform specific way, to package all asset files +into the application archive/package/executable, that is then distributed to the end users. diff --git a/vlib/os/asset/asset.v b/vlib/os/asset/asset.v new file mode 100644 index 0000000000..4f21f8a760 --- /dev/null +++ b/vlib/os/asset/asset.v @@ -0,0 +1,27 @@ +module asset + +import os + +// get_path returns a platform specific path, based on the given asset base folder and relative path +// of a resource in it. On desktop systems, it returns a path relative to the location of the executable. +// On Android, it will return just the relative_path segment, allowing you to later use os.read_apk_asset +// to read from it. +pub fn get_path(base_folder string, relative_path string) string { + $if android { + return relative_path + } $else { + return os.resource_abs_path(os.join_path(base_folder, relative_path)) + } +} + +// read_bytes will read all of the given asset, specified by its base_folder and relative path in it. +// On Android, it will use os.read_apk_asset, relying that the asset base folder has been prepared, and +// prepackaged inside your APK. On desktop systems, it will use the base_folder and relative_path, to +// locate the file, in a way, that is relative to the executable. +pub fn read_bytes(base_folder string, relative_path string) ![]u8 { + $if android { + return os.read_apk_asset(get_path(base_folder, relative_path)) + } $else { + return os.read_bytes(get_path(base_folder, relative_path)) + } +}