mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-09 13:51:45 +08:00
Compare commits
1 Commits
continuous
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98acbbad01 |
25
.github/workflows/c-cpp.yml
vendored
25
.github/workflows/c-cpp.yml
vendored
@@ -21,22 +21,6 @@ jobs:
|
|||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
targetos: linux
|
targetos: linux
|
||||||
targetarch: i386
|
targetarch: i386
|
||||||
- os: ubuntu-20.04
|
|
||||||
targetos: linux
|
|
||||||
targetarch: arm64
|
|
||||||
cross: true
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
targetos: linux
|
|
||||||
targetarch: armhf
|
|
||||||
cross: true
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
targetos: linux
|
|
||||||
targetarch: riscv64
|
|
||||||
cross: true
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
targetos: linux
|
|
||||||
targetarch: ppc64el
|
|
||||||
cross: true
|
|
||||||
# - os: ubuntu-aarch64-20.04
|
# - os: ubuntu-aarch64-20.04
|
||||||
# targetos: linux
|
# targetos: linux
|
||||||
# targetarch: aarch64
|
# targetarch: aarch64
|
||||||
@@ -59,16 +43,15 @@ jobs:
|
|||||||
- os: windows-2019 # always use the oldest possible for 32-bit because of older compilers, and better support of certain legacy OSes
|
- os: windows-2019 # always use the oldest possible for 32-bit because of older compilers, and better support of certain legacy OSes
|
||||||
targetos: win32
|
targetos: win32
|
||||||
targetarch: i386
|
targetarch: i386
|
||||||
- os: macos-14 # arm64 as per github documentation
|
- os: macos-14 # in GitHub documentation, it's an ARM64 machine
|
||||||
targetos: apple
|
targetos: apple
|
||||||
targetarch: arm64
|
targetarch: arm64
|
||||||
- os: macos-13 # x86 as per github documentation (will they fix it before they deprecate this version?..)
|
- os: macos-12 # in GitHub documentation, it's an x86 machine
|
||||||
targetos: apple
|
targetos: apple
|
||||||
targetarch: amd64
|
targetarch: amd64
|
||||||
env:
|
env:
|
||||||
SDL_VERSION: 2.30.9
|
SDL_VERSION: 2.30.3
|
||||||
GH_CPU_ARCH: ${{ matrix.targetarch }}
|
GH_CPU_ARCH: ${{ matrix.targetarch }}
|
||||||
GH_CROSSCOMPILING: ${{ matrix.cross }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -92,7 +75,7 @@ jobs:
|
|||||||
include:
|
include:
|
||||||
- app: su.xash.Engine.Compat.i386
|
- app: su.xash.Engine.Compat.i386
|
||||||
container:
|
container:
|
||||||
image: bilelmoussaoui/flatpak-github-actions:freedesktop-24.08
|
image: bilelmoussaoui/flatpak-github-actions:freedesktop-23.08
|
||||||
options: --privileged
|
options: --privileged
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|||||||
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -25,12 +25,3 @@
|
|||||||
[submodule "3rdparty/MultiEmulator"]
|
[submodule "3rdparty/MultiEmulator"]
|
||||||
path = 3rdparty/MultiEmulator
|
path = 3rdparty/MultiEmulator
|
||||||
url = https://github.com/FWGS/MultiEmulator
|
url = https://github.com/FWGS/MultiEmulator
|
||||||
[submodule "3rdparty/libogg/libogg"]
|
|
||||||
path = 3rdparty/libogg/libogg
|
|
||||||
url = https://gitlab.xiph.org/xiph/ogg.git
|
|
||||||
[submodule "3rdparty/vorbis/vorbis-src"]
|
|
||||||
path = 3rdparty/vorbis/vorbis-src
|
|
||||||
url = https://gitlab.xiph.org/xiph/vorbis.git
|
|
||||||
[submodule "3rdparty/opusfile/opusfile"]
|
|
||||||
path = 3rdparty/opusfile/opusfile
|
|
||||||
url = https://gitlab.xiph.org/xiph/opusfile.git
|
|
||||||
|
|||||||
2
3rdparty/MultiEmulator
vendored
2
3rdparty/MultiEmulator
vendored
Submodule 3rdparty/MultiEmulator updated: 7e989e670e...2f3c3a40cd
1
3rdparty/bzip2/bz_version.h
vendored
Normal file
1
3rdparty/bzip2/bz_version.h
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#define BZ_VERSION "1.1.0-fwgs"
|
||||||
15
3rdparty/bzip2/wscript
vendored
15
3rdparty/bzip2/wscript
vendored
@@ -14,14 +14,21 @@ def configure(conf):
|
|||||||
conf.define('BZ_UNIX', 1)
|
conf.define('BZ_UNIX', 1)
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
bld(features = 'subst', source = 'bzip2/bz_version.h.in', target = 'bzip2/bz_version.h', BZ_VERSION='1.1.0-fwgs', name = 'bz_version')
|
# doesn't work with CMake generator
|
||||||
|
# bld(
|
||||||
|
# features = 'subst',
|
||||||
|
# source = 'bzip2/bz_version.h.in',
|
||||||
|
# target = 'bz_version.h',
|
||||||
|
# BZ_VERSION='1.1.0-fwgs'
|
||||||
|
# )
|
||||||
|
|
||||||
bz_sources = ['bzip2/blocksort.c', 'bzip2/huffman.c', 'bzip2/crctable.c', 'bzip2/randtable.c', 'bzip2/compress.c', 'bzip2/decompress.c', 'bzip2/bzlib.c']
|
bz_sources = ['bzip2/blocksort.c', 'bzip2/huffman.c', 'bzip2/crctable.c', 'bzip2/randtable.c', 'bzip2/compress.c', 'bzip2/decompress.c', 'bzip2/bzlib.c']
|
||||||
|
|
||||||
bld.stlib(
|
bld.stlib(
|
||||||
source = bz_sources,
|
source = bz_sources,
|
||||||
target = 'bzip2',
|
target = 'bzip2',
|
||||||
use = 'bz_version',
|
# use = 'bz_version.h',
|
||||||
includes = 'bzip2/',
|
includes = ['.', 'bzip2/'],
|
||||||
export_includes = 'bzip2/'
|
subsystem = bld.env.MSVC_SUBSYSTEM,
|
||||||
|
export_includes = ['bzip2/']
|
||||||
)
|
)
|
||||||
|
|||||||
2
3rdparty/extras/xash-extras
vendored
2
3rdparty/extras/xash-extras
vendored
Submodule 3rdparty/extras/xash-extras updated: 1bdeab2d8f...9050d05a7b
2
3rdparty/gl-wes-v2
vendored
2
3rdparty/gl-wes-v2
vendored
Submodule 3rdparty/gl-wes-v2 updated: 6b392aecbb...d4ed038d13
1
3rdparty/gl4es/wscript
vendored
1
3rdparty/gl4es/wscript
vendored
@@ -19,4 +19,5 @@ def build(bld):
|
|||||||
includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'],
|
includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'],
|
||||||
defines = ['NOX11', 'NO_GBM', 'NO_INIT_CONSTRUCTOR', 'DEFAULT_ES=2', 'NOEGL', 'NO_LOADER', 'STATICLIB'],
|
defines = ['NOX11', 'NO_GBM', 'NO_INIT_CONSTRUCTOR', 'DEFAULT_ES=2', 'NOEGL', 'NO_LOADER', 'STATICLIB'],
|
||||||
cflags = ['-w', '-fvisibility=hidden', '-std=gnu99'],
|
cflags = ['-w', '-fvisibility=hidden', '-std=gnu99'],
|
||||||
|
subsystem = bld.env.MSVC_SUBSYSTEM,
|
||||||
export_includes = '.')
|
export_includes = '.')
|
||||||
|
|||||||
1
3rdparty/libogg/libogg
vendored
1
3rdparty/libogg/libogg
vendored
Submodule 3rdparty/libogg/libogg deleted from 7cf42ea17a
50
3rdparty/libogg/wscript
vendored
50
3rdparty/libogg/wscript
vendored
@@ -1,50 +0,0 @@
|
|||||||
#! /usr/bin/env python
|
|
||||||
# encoding: utf-8
|
|
||||||
|
|
||||||
def options(opt):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def configure(conf):
|
|
||||||
if not conf.path.find_dir('libogg') or not conf.path.find_dir('libogg/src'):
|
|
||||||
conf.fatal('Can\'t find libogg submodule. Run `git submodule update --init --recursive`.')
|
|
||||||
return
|
|
||||||
|
|
||||||
conf.env.INCLUDE_INTTYPES_H = 0
|
|
||||||
conf.env.INCLUDE_SYS_TYPES_H = 0
|
|
||||||
conf.env.INCLUDE_STDINT_H = 0
|
|
||||||
|
|
||||||
if conf.check_cc(header_name='inttypes.h', mandatory = False):
|
|
||||||
conf.env.INCLUDE_INTTYPES_H = 1
|
|
||||||
elif conf.check_cc(header_name='sys/types.h', mandatory = False):
|
|
||||||
conf.env.INCLUDE_SYS_TYPES_H = 1
|
|
||||||
elif conf.check_cc(header_name='stdint.h', mandatory = False):
|
|
||||||
conf.env.INCLUDE_STDINT_H = 1
|
|
||||||
|
|
||||||
def build(bld):
|
|
||||||
sources = bld.path.ant_glob([
|
|
||||||
'libogg/src/*.c'
|
|
||||||
])
|
|
||||||
|
|
||||||
bld(
|
|
||||||
features = 'subst',
|
|
||||||
name = 'libogg_config_types',
|
|
||||||
source = 'libogg/include/ogg/config_types.h.in',
|
|
||||||
target = 'libogg/include/ogg/config_types.h',
|
|
||||||
INCLUDE_INTTYPES_H = bld.env.INCLUDE_INTTYPES_H,
|
|
||||||
INCLUDE_SYS_TYPES_H = bld.env.INCLUDE_SYS_TYPES_H,
|
|
||||||
INCLUDE_STDINT_H = bld.env.INCLUDE_STDINT_H,
|
|
||||||
SIZE16 = 'int16_t',
|
|
||||||
USIZE16 = 'uint16_t',
|
|
||||||
SIZE32 = 'int32_t',
|
|
||||||
USIZE32 = 'uint32_t',
|
|
||||||
SIZE64 = 'int64_t',
|
|
||||||
USIZE64 = 'uint64_t'
|
|
||||||
)
|
|
||||||
|
|
||||||
bld.stlib(
|
|
||||||
source = sources,
|
|
||||||
target = 'ogg',
|
|
||||||
use = 'libogg_config_types',
|
|
||||||
includes = 'libogg/include/',
|
|
||||||
export_includes = 'libogg/include/'
|
|
||||||
)
|
|
||||||
2
3rdparty/mainui
vendored
2
3rdparty/mainui
vendored
Submodule 3rdparty/mainui updated: c92a06e979...0bb921ea8d
2
3rdparty/nanogl
vendored
2
3rdparty/nanogl
vendored
Submodule 3rdparty/nanogl updated: dbfe2e75b3...6c0f341b7b
32
3rdparty/opus/wscript
vendored
32
3rdparty/opus/wscript
vendored
@@ -3,26 +3,6 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
FRAGMENT_VLA='''int main (int argc, char **argv) {
|
|
||||||
char a[argc];
|
|
||||||
a[sizeof( a ) - 1] = 0;
|
|
||||||
int N;
|
|
||||||
return a[0];
|
|
||||||
}'''
|
|
||||||
|
|
||||||
FRAGMENT_ALLOCA_H='''#include <alloca.h>
|
|
||||||
int main (void) {
|
|
||||||
int foo=10;
|
|
||||||
int * array = alloca(foo);
|
|
||||||
}'''
|
|
||||||
|
|
||||||
FRAGMENT_STDLIB_H='''#include <malloc.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
int main (void) {
|
|
||||||
int foo=10;
|
|
||||||
int * array = alloca(foo);
|
|
||||||
}'''
|
|
||||||
|
|
||||||
def options(opt):
|
def options(opt):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -31,15 +11,6 @@ def configure(conf):
|
|||||||
conf.fatal('Can\'t find opus submodule. Run `git submodule update --init --recursive`.')
|
conf.fatal('Can\'t find opus submodule. Run `git submodule update --init --recursive`.')
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check for C99 variable-size arrays, or alloca() as fallback
|
|
||||||
if conf.check_cc(fragment=FRAGMENT_VLA, msg = 'Checking for C99 VLA support', mandatory = False):
|
|
||||||
conf.define('VAR_ARRAYS', 1)
|
|
||||||
elif conf.check_cc(fragment=FRAGMENT_ALLOCA_H, msg = 'Checking for alloca in alloca.h header', mandatory = False):
|
|
||||||
conf.define('USE_ALLOCA', 1)
|
|
||||||
conf.define('HAVE_ALLOCA_H', 1)
|
|
||||||
elif conf.check_cc(fragmenmt=FRAGMENT_STDLIB_H, msg = 'Checking for alloca.h in stdlib.h', mandatory = False):
|
|
||||||
conf.define('USE_ALLOCA', 1)
|
|
||||||
|
|
||||||
# TODO: ARM/x86 intrinsics detection
|
# TODO: ARM/x86 intrinsics detection
|
||||||
# TODO: maybe call autotools/cmake/meson instead?
|
# TODO: maybe call autotools/cmake/meson instead?
|
||||||
|
|
||||||
@@ -56,7 +27,7 @@ def build(bld):
|
|||||||
'opus/celt/opus_custom_demo.c'
|
'opus/celt/opus_custom_demo.c'
|
||||||
])
|
])
|
||||||
includes = ['opus/include/', 'opus/celt/', 'opus/silk/', 'opus/silk/float/']
|
includes = ['opus/include/', 'opus/celt/', 'opus/silk/', 'opus/silk/float/']
|
||||||
defines = ['OPUS_BUILD', 'FLOAT_APPROX', 'PACKAGE_VERSION="1.4.0"', 'CUSTOM_MODES']
|
defines = ['USE_ALLOCA', 'OPUS_BUILD', 'FLOAT_APPROX', 'PACKAGE_VERSION="1.4.0"', 'CUSTOM_MODES']
|
||||||
|
|
||||||
bld.stlib(
|
bld.stlib(
|
||||||
source = sources,
|
source = sources,
|
||||||
@@ -64,5 +35,6 @@ def build(bld):
|
|||||||
features = 'c',
|
features = 'c',
|
||||||
includes = includes,
|
includes = includes,
|
||||||
defines = defines,
|
defines = defines,
|
||||||
|
subsystem = bld.env.MSVC_SUBSYSTEM,
|
||||||
export_includes = ['opus/include/']
|
export_includes = ['opus/include/']
|
||||||
)
|
)
|
||||||
|
|||||||
1
3rdparty/opusfile/opusfile
vendored
1
3rdparty/opusfile/opusfile
vendored
Submodule 3rdparty/opusfile/opusfile deleted from 3ecc22aa0a
37
3rdparty/opusfile/wscript
vendored
37
3rdparty/opusfile/wscript
vendored
@@ -1,37 +0,0 @@
|
|||||||
#! /usr/bin/env python
|
|
||||||
# encoding: utf-8
|
|
||||||
|
|
||||||
def options(opt):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def configure(conf):
|
|
||||||
if not conf.path.find_dir('opusfile') or not conf.path.find_dir('opusfile/src'):
|
|
||||||
conf.fatal('Can\'t find opusfile submodule. Run `git submodule update --init --recursive`.')
|
|
||||||
return
|
|
||||||
|
|
||||||
if conf.env.COMPILER_CC == 'msvc':
|
|
||||||
conf.define('_CRT_SECURE_NO_WARNINGS', 1)
|
|
||||||
conf.define('_CRT_SECURE_NO_DEPRECATE', 1)
|
|
||||||
conf.define('_CRT_NONSTDC_NO_DEPRECATE', 1)
|
|
||||||
|
|
||||||
if conf.env.DEST_OS == 'android':
|
|
||||||
# HACKHACK: set it to 32 here because opusfile can't be built on Android SDK < 24
|
|
||||||
# with _FILE_OFFSET_BITS 64 (which it sets automatically in src/internal.h)
|
|
||||||
# we are not (????) relying on this part of the API, so it should be harmless
|
|
||||||
conf.define('_FILE_OFFSET_BITS', 32)
|
|
||||||
|
|
||||||
def build(bld):
|
|
||||||
sources = [
|
|
||||||
'opusfile/src/info.c',
|
|
||||||
'opusfile/src/internal.c',
|
|
||||||
'opusfile/src/opusfile.c',
|
|
||||||
'opusfile/src/stream.c'
|
|
||||||
]
|
|
||||||
|
|
||||||
bld.stlib(
|
|
||||||
source = sources,
|
|
||||||
target = 'opusfile',
|
|
||||||
includes = 'opusfile/include/',
|
|
||||||
use = 'ogg opus',
|
|
||||||
export_includes = 'opusfile/include/'
|
|
||||||
)
|
|
||||||
2
3rdparty/vgui_support
vendored
2
3rdparty/vgui_support
vendored
Submodule 3rdparty/vgui_support updated: d585ab86da...e0ab95229d
1
3rdparty/vorbis/vorbis-src
vendored
1
3rdparty/vorbis/vorbis-src
vendored
Submodule 3rdparty/vorbis/vorbis-src deleted from bb4047de4c
71
3rdparty/vorbis/wscript
vendored
71
3rdparty/vorbis/wscript
vendored
@@ -1,71 +0,0 @@
|
|||||||
#! /usr/bin/env python
|
|
||||||
# encoding: utf-8
|
|
||||||
|
|
||||||
FRAGMENT_MEMORY_H='''#include <memory.h>
|
|
||||||
int main (void) {
|
|
||||||
return 0;
|
|
||||||
}'''
|
|
||||||
|
|
||||||
FRAGMENT_ALLOCA_H='''#include <alloca.h>
|
|
||||||
int main (void) {
|
|
||||||
int foo=10;
|
|
||||||
int * array = alloca(foo);
|
|
||||||
}'''
|
|
||||||
|
|
||||||
def options(opt):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def configure(conf):
|
|
||||||
if not conf.path.find_dir('vorbis-src') or not conf.path.find_dir('vorbis-src/lib'):
|
|
||||||
conf.fatal('Can\'t find Vorbis submodule. Run `git submodule update --init --recursive`.')
|
|
||||||
return
|
|
||||||
|
|
||||||
if conf.check_cc(fragment=FRAGMENT_MEMORY_H, msg = 'Checking for memory.h header', mandatory = False):
|
|
||||||
conf.define('USE_MEMORY_H', 1)
|
|
||||||
|
|
||||||
conf.check_cc(fragment=FRAGMENT_ALLOCA_H, msg = 'Checking for alloca in alloca.h header', mandatory = False)
|
|
||||||
|
|
||||||
if conf.env.COMPILER_CC == 'msvc':
|
|
||||||
conf.define('_CRT_SECURE_NO_WARNINGS', 1)
|
|
||||||
conf.define('_CRT_SECURE_NO_DEPRECATE', 1)
|
|
||||||
conf.define('_CRT_NONSTDC_NO_DEPRECATE', 1)
|
|
||||||
|
|
||||||
def build(bld):
|
|
||||||
libvorbis_sources = [
|
|
||||||
'vorbis-src/lib/mdct.c',
|
|
||||||
'vorbis-src/lib/smallft.c',
|
|
||||||
'vorbis-src/lib/block.c',
|
|
||||||
'vorbis-src/lib/envelope.c',
|
|
||||||
'vorbis-src/lib/window.c',
|
|
||||||
'vorbis-src/lib/lsp.c',
|
|
||||||
'vorbis-src/lib/lpc.c',
|
|
||||||
'vorbis-src/lib/analysis.c',
|
|
||||||
'vorbis-src/lib/synthesis.c',
|
|
||||||
'vorbis-src/lib/psy.c',
|
|
||||||
'vorbis-src/lib/info.c',
|
|
||||||
'vorbis-src/lib/floor1.c',
|
|
||||||
'vorbis-src/lib/floor0.c',
|
|
||||||
'vorbis-src/lib/res0.c',
|
|
||||||
'vorbis-src/lib/mapping0.c',
|
|
||||||
'vorbis-src/lib/registry.c',
|
|
||||||
'vorbis-src/lib/codebook.c',
|
|
||||||
'vorbis-src/lib/sharedbook.c',
|
|
||||||
'vorbis-src/lib/lookup.c',
|
|
||||||
'vorbis-src/lib/bitrate.c'
|
|
||||||
]
|
|
||||||
|
|
||||||
bld.stlib(
|
|
||||||
source = libvorbis_sources,
|
|
||||||
target = 'vorbis',
|
|
||||||
includes = 'vorbis-src/include/',
|
|
||||||
use = 'ogg',
|
|
||||||
export_includes = 'vorbis-src/include/'
|
|
||||||
)
|
|
||||||
|
|
||||||
bld.stlib(
|
|
||||||
source = 'vorbis-src/lib/vorbisfile.c',
|
|
||||||
target = 'vorbisfile',
|
|
||||||
includes = 'vorbis-src/include/',
|
|
||||||
use = 'vorbis',
|
|
||||||
export_includes = 'vorbis-src/include/'
|
|
||||||
)
|
|
||||||
@@ -13,6 +13,5 @@ When `-bugcomp` is specified with argument, it interpreted as flags separated wi
|
|||||||
| Flag | Description | Games that require this flag |
|
| Flag | Description | Games that require this flag |
|
||||||
| ------- | ----------- | ---------------------------- |
|
| ------- | ----------- | ---------------------------- |
|
||||||
| `peoei` | Reverts `pfnPEntityOfEntIndex` behavior to GoldSrc, where it returns NULL for last player due to incorrect player index comparison | * Counter-Strike: Condition Zero - Deleted Scenes |
|
| `peoei` | Reverts `pfnPEntityOfEntIndex` behavior to GoldSrc, where it returns NULL for last player due to incorrect player index comparison | * Counter-Strike: Condition Zero - Deleted Scenes |
|
||||||
| `gsmrf` | Rewrites message at the moment when Game DLL attempts to write an internal engine message, usually specific to GoldSrc protocol.<br>Right now only supports `svc_spawnstaticsound`, more messages added by request. | * MetaMod/AMXModX based mods |
|
| `gsmrf` | Rewrites message at the moment when Game DLL attempts to write an internal engine message, usually specific to GoldSrc protocol. Right now only supports `svc_spawnstaticsound`, more messages added by request. | * MetaMod/AMXModX based mods |
|
||||||
| `sp_attn_none` | Makes sounds with attenuation zero spatialized, i.e. have a stereo effect. | Possibly, every game that was made for GoldSrc. |
|
| `sp_attn_none` | Makes sounds with attenuation zero spatialized, i.e. have a stereo effect. | Possibly, every game that was made for GoldSrc. |
|
||||||
| `get_game_dir_full` | Makes server return full path in server's `pfnGetGameDir` API function | Mods targetting engine before HL 1.1.1.1, according to MetaMod [documentation](http://metamod.org/engine_notes.html#GetGameDir) |
|
|
||||||
|
|||||||
@@ -5,10 +5,5 @@ This can be useful to test engine in Wine without using virtual machines or dual
|
|||||||
0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part)
|
0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part)
|
||||||
1. Set environment variable MSVC_WINE_PATH to the path to installed MSVC toolchain
|
1. Set environment variable MSVC_WINE_PATH to the path to installed MSVC toolchain
|
||||||
2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot`
|
2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot`
|
||||||
3. Run `PKGCONFIG=/bin/false ./waf configure -T <build-type> --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual.
|
3. Run `./waf configure -T <build-type> --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual.
|
||||||
4. .. other typical steps to build from console ...
|
4. .. other typical steps to build from console ...
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> Notice the usage of PKGCONFIG=/bin/false here. We're disabling pkg-config so we don't accidentally pull
|
|
||||||
> system-wide dependencies and force building them from source. In future builds we might set custom
|
|
||||||
> directory to pull dependencies from, like ffmpeg...
|
|
||||||
|
|||||||
150
Documentation/entity-tools.md
Normal file
150
Documentation/entity-tools.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# There are few new commands availiable in xash3d fork:
|
||||||
|
|
||||||
|
## Commands:
|
||||||
|
### ent_create
|
||||||
|
Create entity with specified classname and key/values
|
||||||
|
|
||||||
|
`ent_create <classname> <key> <value> <key> <value> ...`
|
||||||
|
|
||||||
|
for example:
|
||||||
|
|
||||||
|
`ent_create monster_zombie targetname zomb1`
|
||||||
|
|
||||||
|
after creating entity, ent_last_xxx cvars are set to new entity and ent_last_cb called, look at ent_getvars description
|
||||||
|
|
||||||
|
### ent_fire
|
||||||
|
|
||||||
|
Make some actions on entity
|
||||||
|
|
||||||
|
`ent_fire <pattern> <command> <args>`
|
||||||
|
Availiavle commands:
|
||||||
|
* Set fields (Only set entity field, does not call any functions):
|
||||||
|
* health
|
||||||
|
* gravity
|
||||||
|
* movetype
|
||||||
|
* solid
|
||||||
|
* rendermode
|
||||||
|
* rendercolor (vector)
|
||||||
|
* renderfx
|
||||||
|
* renderamt
|
||||||
|
* hullmin (vector)
|
||||||
|
* hullmax (vector)
|
||||||
|
* Actions
|
||||||
|
* rename: set entity targetname
|
||||||
|
* settarget: set entity target (only targetnames)
|
||||||
|
* setmodel: set entity model (does not update)
|
||||||
|
* set: set key/value by server library
|
||||||
|
* See game FGD to get list.
|
||||||
|
* command takes two arguments
|
||||||
|
* touch: touch entity by current player.
|
||||||
|
* use: use entity by current player.
|
||||||
|
* movehere: place entity in player fov.
|
||||||
|
* drop2floor: place entity to nearest floor surface
|
||||||
|
* moveup: move entity to 25 units up
|
||||||
|
* moveup (value): move by y axis relatively to specified value
|
||||||
|
* Flags (Set/clear specified flag bit, arg is bit number):
|
||||||
|
* setflag
|
||||||
|
* clearflag
|
||||||
|
* setspawnflag
|
||||||
|
* clearspawnflag
|
||||||
|
|
||||||
|
### ent_info
|
||||||
|
Print information about entity by identificator
|
||||||
|
|
||||||
|
`ent_info <identificator>`
|
||||||
|
|
||||||
|
### ent_getvars
|
||||||
|
Set client cvars containing entity information (useful for [[Scripting]]) and call ent_last_cb
|
||||||
|
|
||||||
|
`ent_getvars <identificator>`
|
||||||
|
|
||||||
|
These cvars are set:
|
||||||
|
```
|
||||||
|
ent_last_name
|
||||||
|
ent_last_num
|
||||||
|
ent_last_inst
|
||||||
|
ent_last_origin
|
||||||
|
ent_last_class
|
||||||
|
```
|
||||||
|
|
||||||
|
### ent_list
|
||||||
|
Print short information about antities, filtered by pattern
|
||||||
|
|
||||||
|
`ent_list <pattern>`
|
||||||
|
|
||||||
|
## Syntax description
|
||||||
|
|
||||||
|
### \<identificator\>
|
||||||
|
|
||||||
|
* !cross: entity under aim
|
||||||
|
* Instance code: !\<number\>_\<seria\l>
|
||||||
|
* set by ent_getvars command
|
||||||
|
* Entity index
|
||||||
|
* targetname pattern
|
||||||
|
|
||||||
|
### \<pattern\>
|
||||||
|
|
||||||
|
Pattern is like identificator, but may filter many entities by classname
|
||||||
|
|
||||||
|
### (vector)
|
||||||
|
|
||||||
|
used by ent_fire command. vector means three float values, entered without quotes
|
||||||
|
|
||||||
|
### key/value
|
||||||
|
|
||||||
|
All entities parameters may be set by specifiing key and value strings.
|
||||||
|
|
||||||
|
Originally, this mechanizm is used in map/bsp format, but it can be used in enttools too.
|
||||||
|
|
||||||
|
Keys and values are passed to server library and processed by entity keyvalue function, setting edict and entity owns parameters.
|
||||||
|
|
||||||
|
If value contains spaces, it must be put in quotes:
|
||||||
|
|
||||||
|
`ent_fire !cross set origin "0 0 0"`
|
||||||
|
|
||||||
|
## Using with scripting
|
||||||
|
|
||||||
|
ent_create and ent_getvars commands are setting cvars on client
|
||||||
|
|
||||||
|
It can be used with ent_last_cb alias that is executed after setting cvars.
|
||||||
|
|
||||||
|
Simple example:
|
||||||
|
|
||||||
|
```
|
||||||
|
ent_create weapon_c4
|
||||||
|
alias ent_last_cb "ent_fire \$ent_last_inst use"
|
||||||
|
```
|
||||||
|
|
||||||
|
Use weapon_c4 after creating it.
|
||||||
|
|
||||||
|
Note that you cannot use many dfferent callbacks at the same time.
|
||||||
|
|
||||||
|
You can set entity name by by pattern and create special script, contatning all callbacks.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
example.cfg
|
||||||
|
```
|
||||||
|
alias ent_last_cb exec entity_cb.cfg
|
||||||
|
ent create \<class\> targetname my_ent1_$name
|
||||||
|
ent_create \<class\> targetname my_ent2_$name
|
||||||
|
```
|
||||||
|
entity_cb.cfg
|
||||||
|
```
|
||||||
|
if $ent_last_name == my_ent1_$name
|
||||||
|
:(ent1 actions)
|
||||||
|
if $ent_last_name == my_ent2_$name
|
||||||
|
:(ent2 actions)
|
||||||
|
```
|
||||||
|
Note that scripting cannot be blocking. You cannot wait for server answer and continue. But you can use small scripts, connected with ent_last_cb command. The best usage is user interaction. You can add touch buttons to screen or call user command menu actions by callbacks.
|
||||||
|
## Server side
|
||||||
|
|
||||||
|
To enable entity tools on server, set sv_enttools_enable to 1
|
||||||
|
|
||||||
|
To change maximum number of entities, touched by ent_fire, change sv_enttools_maxfire to required number.
|
||||||
|
|
||||||
|
To enable actions on players, set sv_enttools_players to 1.
|
||||||
|
|
||||||
|
To enable entity tools for player by nickname, set sv_enttools_godplayer to nickname. Useful to temporary enable from rcon.
|
||||||
|
|
||||||
|
To prevent crash on some actions, set host_mapdesign_fatal to 0
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
# Entity tools
|
|
||||||
|
|
||||||
For some features described below, you need to enable [console scripting](https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/extensions/console-scripting.md) with command `cmd_scripting 1` in console.
|
|
||||||
To get more information about it, check another according page related to console scripting.
|
|
||||||
|
|
||||||
## Commands description
|
|
||||||
|
|
||||||
### ent_create
|
|
||||||
Create entity with specified classname and key/values.
|
|
||||||
|
|
||||||
`ent_create <classname> <key> <value> <key> <value> ...`
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
`ent_create monster_zombie targetname zomb1`
|
|
||||||
|
|
||||||
After creating entity, ent_last_xxx cvars are set to new entity and ent_last_cb called, look at ent_getvars description.
|
|
||||||
|
|
||||||
### ent_fire
|
|
||||||
|
|
||||||
Makes some actions on entity.
|
|
||||||
|
|
||||||
`ent_fire <pattern> <command> <args>`
|
|
||||||
|
|
||||||
#### Available commands:
|
|
||||||
|
|
||||||
Set fields (only set entity field, does not call any functions)
|
|
||||||
* health
|
|
||||||
* gravity
|
|
||||||
* movetype
|
|
||||||
* solid
|
|
||||||
* rendermode
|
|
||||||
* rendercolor (vector)
|
|
||||||
* renderfx
|
|
||||||
* renderamt
|
|
||||||
* hullmin (vector)
|
|
||||||
* hullmax (vector)
|
|
||||||
|
|
||||||
Actions
|
|
||||||
* rename: set entity targetname
|
|
||||||
* settarget: set entity target (only targetnames)
|
|
||||||
* setmodel: set entity model (does not update)
|
|
||||||
* set: set key/value by server library
|
|
||||||
* See game FGD to get list.
|
|
||||||
* command takes two arguments
|
|
||||||
* touch: touch entity by current player.
|
|
||||||
* use: use entity by current player.
|
|
||||||
* movehere: place entity in player fov.
|
|
||||||
* drop2floor: place entity to nearest floor surface
|
|
||||||
* moveup: move entity to 25 units up
|
|
||||||
* moveup (value): move by y axis relatively to specified value
|
|
||||||
|
|
||||||
Flags (set/clear specified flag bit, arg is bit number):
|
|
||||||
* setflag
|
|
||||||
* clearflag
|
|
||||||
* setspawnflag
|
|
||||||
* clearspawnflag
|
|
||||||
|
|
||||||
### ent_info
|
|
||||||
Print information about entity by identificator.
|
|
||||||
|
|
||||||
`ent_info <identificator>`
|
|
||||||
|
|
||||||
### ent_getvars
|
|
||||||
Set client cvars containing entity information (useful for [scripting](extensions/console-scripting.md)) and then calls ent_last_cb.
|
|
||||||
|
|
||||||
`ent_getvars <identificator>`
|
|
||||||
|
|
||||||
These cvars are set:
|
|
||||||
```
|
|
||||||
ent_last_name
|
|
||||||
ent_last_num
|
|
||||||
ent_last_inst
|
|
||||||
ent_last_origin
|
|
||||||
ent_last_class
|
|
||||||
```
|
|
||||||
|
|
||||||
### ent_list
|
|
||||||
Print short information about antities, filtered by pattern.
|
|
||||||
|
|
||||||
`ent_list <pattern>`
|
|
||||||
|
|
||||||
## Syntax description
|
|
||||||
|
|
||||||
#### \<identificator\>
|
|
||||||
* !cross: entity under aim
|
|
||||||
* !\<number\>_\<serial\>: instance code
|
|
||||||
* Set by ent_getvars command
|
|
||||||
* Entity index
|
|
||||||
* Targetname pattern
|
|
||||||
|
|
||||||
#### \<pattern\>
|
|
||||||
|
|
||||||
Pattern is like identificator, but may filter many entities by classname.
|
|
||||||
|
|
||||||
#### (vector)
|
|
||||||
|
|
||||||
Used by ent_fire command. vector means three float values, entered without quotes.
|
|
||||||
|
|
||||||
#### key/value
|
|
||||||
|
|
||||||
All entities parameters may be set by specifiing key and value strings.
|
|
||||||
|
|
||||||
Originally, this mechanizm is used in map/bsp format, but it can be used in enttools too.
|
|
||||||
|
|
||||||
Keys and values are passed to server library and processed by entity keyvalue function, setting edict and entity owns parameters.
|
|
||||||
|
|
||||||
If value contains spaces, it must be put in quotes:
|
|
||||||
|
|
||||||
`ent_fire !cross set origin "0 0 0"`
|
|
||||||
|
|
||||||
## Using with scripting
|
|
||||||
|
|
||||||
ent_create and ent_getvars commands are setting cvars on client
|
|
||||||
|
|
||||||
It can be used with ent_last_cb alias that is executed after setting cvars.
|
|
||||||
|
|
||||||
Simple example:
|
|
||||||
|
|
||||||
```
|
|
||||||
ent_create weapon_c4
|
|
||||||
alias ent_last_cb "ent_fire \$ent_last_inst use"
|
|
||||||
```
|
|
||||||
|
|
||||||
Use weapon_c4 after creating it.
|
|
||||||
|
|
||||||
Note that you cannot use many different callbacks at the same time.
|
|
||||||
|
|
||||||
You can set entity name by by pattern and create special script, contatning all callbacks.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
> example.cfg
|
|
||||||
```
|
|
||||||
alias ent_last_cb exec entity_cb.cfg
|
|
||||||
ent create \<class\> targetname my_ent1_$name
|
|
||||||
ent_create \<class\> targetname my_ent2_$name
|
|
||||||
```
|
|
||||||
> entity_cb.cfg
|
|
||||||
```
|
|
||||||
if $ent_last_name == my_ent1_$name
|
|
||||||
:(ent1 actions)
|
|
||||||
if $ent_last_name == my_ent2_$name
|
|
||||||
:(ent2 actions)
|
|
||||||
```
|
|
||||||
Note that scripting cannot be blocking. You cannot wait for server answer and continue. But you can use small scripts, connected with ent_last_cb command. The best usage is user interaction. You can add touch buttons to screen or call user command menu actions by callbacks.
|
|
||||||
|
|
||||||
## Server side configuration
|
|
||||||
|
|
||||||
To enable entity tools on server, set sv_enttools_enable to 1
|
|
||||||
|
|
||||||
To change maximum number of entities, touched by ent_fire, change sv_enttools_maxfire to required number.
|
|
||||||
@@ -6,10 +6,9 @@ For connecting to GoldSrc-based servers, use this command:
|
|||||||
connect ip:port gs
|
connect ip:port gs
|
||||||
```
|
```
|
||||||
|
|
||||||
But keep in mind, there are requirement for server to be able to accept connections from Xash3D-based clients: it should use Reunion or Dproto.
|
But keep in mind, there are requirement for server to be able to accept connections from Xash3D-based clients: it should accept HLTV connections.
|
||||||
Without this requirement, you will just get "Steam validation rejected" error on connecting.
|
Without this requirement, you will just get "Steam validation rejected" error on connecting.
|
||||||
|
|
||||||
That is because proper authorization with Steam API is not implemented in engine yet (but we have plans on it).
|
That is because proper authorization with Steam API is not implemented in engine yet (but we have plans on it).
|
||||||
|
|
||||||
Also, we encountered that some GoldSrc-based servers are recognizing Xash3D clients as "fake clients" and banning/kicking them. Maybe this problem will be
|
In case of ReHLDS with Reunion plugin, by default it rejects HLTV clients. But connections from HLTV can be easily enabled in `reunion.cfg` file.
|
||||||
solved along with better compatibility with GoldSrc behavior, but may be not - we don't know logic behind this fake client checks.
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
# NAT bypass feature in Xash3D FWGS
|
|
||||||
Since IPv6 not as widespread as we would like, NAT (Network Address Translation) still being actively used by many internet service providers in an attempts to
|
|
||||||
mitigate IPv4 addresses exhaustion. In short, they uses one IPv4 address and doing some tricks with ports to represent many other users behind this address.
|
|
||||||
|
|
||||||
But this leads to a problem for users: they cannot accept direct
|
|
||||||
incoming connections anymore. This means that if you are behind provider's NAT and will try to setup Xash3D FWGS server - nobody will be able to connect to it,
|
|
||||||
and the server will not show up in servers public list.
|
|
||||||
|
|
||||||
## Is it possible to avoid this problem?
|
|
||||||
In most cases, it is possible to bypass NAT with UDP hole punching, and Xash3D FWGS uses this method too. But this method is not 100% guaranteed to work - it depends
|
|
||||||
on NAT configurations on both server and client side, and there is no way to control it.
|
|
||||||
|
|
||||||
First of all, server should not be behind symmetric NAT. You can check your NAT type on [this page](https://www.checkmynat.com/).
|
|
||||||
If you get "Symmetric NAT" result in this test, that means you cannot setup publicly available server with internet connection that you are using.
|
|
||||||
|
|
||||||
Here is more detailed scheme of different NAT types compatibility, it explains are users with different NAT types can connect to each other or not.
|
|
||||||
Client NAT type on rows, server NAT type on columns:
|
|
||||||
|
|
||||||
| *NAT Type* | Full Cone | Restricted Cone | Port Restricted Cone | Symmetric |
|
|
||||||
| -------------------- | --------- | --------------- | -------------------- | --------- |
|
|
||||||
| Full Cone | ✔️ | ✔️ | ✔️ | ✔️ |
|
|
||||||
| Restricted Cone | ✔️ | ✔️ | ✔️ | ✔️ |
|
|
||||||
| Port Restricted Cone | ✔️ | ✔️ | ✔️ | ❌ |
|
|
||||||
| Symmetric | ✔️ | ✔️ | ❌ | ❌ |
|
|
||||||
|
|
||||||
## How to use NAT bypass feature?
|
|
||||||
If you are starting server within the game, you need to enable option "*Use NAT Bypass instead of direct mode*" in bottom left corner of screen.
|
|
||||||
|
|
||||||
If you are starting dedicated server, you should add console variable `sv_nat 1` to server.cfg file, or add `+sv_nat 1` to server startup parameters.
|
|
||||||
|
|
||||||
If you need to connect to server behind NAT, there is separate tab named *NAT* for such servers in server browser. Note that it is useless to add NAT server to favorites,
|
|
||||||
or somehow trying manually to store it, because it has changing address and port.
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
Xash3D FWGS is intended to be easily portable for various platforms, however main issue is maintaining such ports.
|
Xash3D FWGS is intended to be easily ported to specific platform, however main issue is maintaining such ports.
|
||||||
|
|
||||||
This page is about merged ports to main source tree and responsible for it developers.
|
This page is about merged ports to main source tree and responsible for it developers.
|
||||||
|
|
||||||
For porting guidelines, read engine-porting-guide.md.
|
For porting guidelines, look engine-porting-guide.md.
|
||||||
|
|
||||||
Status:
|
Status:
|
||||||
* **Supported**: active, confirmed to be fully functional, gets built on CI.
|
* **Supported**: active, was confirmed to be fully functional.
|
||||||
* **Orphaned**: some work was done but not finished or actively tested due to lack of human resources.
|
|
||||||
* **In progress**: active, under development.
|
* **In progress**: active, under development.
|
||||||
* **Old Engine**: port was for old engine fork.
|
* **Old Engine**: port was for old engine fork
|
||||||
* **Deprecated**: not supported anymore.
|
* **Not maintained**: lack of human resources
|
||||||
|
* **Deprecated**: not supported anymore
|
||||||
|
|
||||||
Table is sorted by status and platform.
|
Table is sorted by status and platform.
|
||||||
|
|
||||||
@@ -17,22 +17,17 @@ Table is sorted by status and platform.
|
|||||||
| -------- | ------ | ---------- | ----
|
| -------- | ------ | ---------- | ----
|
||||||
| Android | Supported | @Velaron |
|
| Android | Supported | @Velaron |
|
||||||
| *BSD | Supported | @nekonomicon |
|
| *BSD | Supported | @nekonomicon |
|
||||||
|
| DOS4GW | Supported | @mittorn |
|
||||||
| GNU/Linux | Supported | @a1batross, @mittorn |
|
| GNU/Linux | Supported | @a1batross, @mittorn |
|
||||||
|
| Haiku | Supported | not maintained | Was added by #478 and #483
|
||||||
| macOS | Supported | @sofakng |
|
| macOS | Supported | @sofakng |
|
||||||
|
| MotoMAGX | Supported | @a1batross |
|
||||||
| PSVita | Supported | @fgsfdsfgs |
|
| PSVita | Supported | @fgsfdsfgs |
|
||||||
| Switch | Supported | @fgsfdsfgs |
|
| Switch | Supported | @fgsfdsfgs |
|
||||||
| Windows | Supported | @a1batross, @SNMetamorph |
|
| Windows | Supported | @a1batross, @SNMetamorph |
|
||||||
| DOS4GW | Orphaned | N/A | Haven't been confirmed to work for a very long time
|
|
||||||
| Haiku | Orphaned | N/A | Was added by #478 and #483
|
|
||||||
| IRIX | Orphaned | N/A | Undone, compiles but requires big endian port
|
|
||||||
| MotoMAGX | Orphaned | N/A | Should work but the compiler used for this platform is very unstable and easy to crash (it's GCC 3.4)
|
|
||||||
| SerenityOS | Orphaned | N/A | Works but not throughly tested
|
|
||||||
| Solaris | Orphaned | N/A | Works but not throughly tested
|
|
||||||
| WebAssembly System Interface | Orphaned | N/A | Undone, WASI is missing a lot of APIs we want to use
|
|
||||||
| Dreamcast | In progress | @maximqaxd | [GitHub Repository](https://github.com/maximqaxd/xash3d-fwgs_dc/)
|
|
||||||
| PSP | In progress | @Crow_bar, @Velaron | [GitHub Repository](https://github.com/Crow-bar/xash3d-fwgs)
|
| PSP | In progress | @Crow_bar, @Velaron | [GitHub Repository](https://github.com/Crow-bar/xash3d-fwgs)
|
||||||
| Wii | In progress | Collaborative effort | [GitHub Repository](https://github.com/saucesaft/xash3d-wii)
|
| Wii | In progress | Collaborative effort | [GitHub Repository](https://github.com/saucesaft/xash3d-wii)
|
||||||
| Emscripten | Old Engine | N/A |
|
| Emscripten | Old Engine | not maintained |
|
||||||
| 3DS | Old Engine fork | N/A | [GitHub Repository](https://github.com/masterfeizz/Xash3DS)
|
| 3DS | Old Engine fork | not maintained | [GitHub Repository](https://github.com/masterfeizz/Xash3DS)
|
||||||
| Oculus Quest | Old Engine fork | N/A | [GitHub Repository](https://github.com/DrBeef/Lambda1VR)
|
| Oculus Quest | Old Engine fork | @DrBeef | [GitHub Repository](https://github.com/DrBeef/Lambda1VR)
|
||||||
| iOS | Deprecated | N/A | See GitHub issue #61
|
| iOS | Deprecated | not maintained | See GitHub issue #61
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ If you like Xash3D FWGS, consider supporting individual engine maintainers. By s
|
|||||||
* TrueType font rendering, as a part of mainui_cpp.
|
* TrueType font rendering, as a part of mainui_cpp.
|
||||||
* External VGUI support module.
|
* External VGUI support module.
|
||||||
* PNG & KTX2 image format support.
|
* PNG & KTX2 image format support.
|
||||||
* Ogg Vorbis (`.ogg`) & Ogg Opus (`.opus`) audio formats support.
|
|
||||||
* [A set of small improvements](Documentation/), without broken compatibility.
|
* [A set of small improvements](Documentation/), without broken compatibility.
|
||||||
|
|
||||||
## Installation & Running
|
## Installation & Running
|
||||||
|
|||||||
14
android/.gitignore
vendored
14
android/.gitignore
vendored
@@ -1,14 +0,0 @@
|
|||||||
.gradle/
|
|
||||||
build/
|
|
||||||
.externalNativeBuild
|
|
||||||
.cxx/
|
|
||||||
.idea/
|
|
||||||
local.properties
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
.gradle
|
|
||||||
.settings
|
|
||||||
release/
|
|
||||||
*.hprof
|
|
||||||
.vscode/
|
|
||||||
*.bak
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.6)
|
|
||||||
|
|
||||||
# Only used to build Android project
|
|
||||||
|
|
||||||
project(XASH_ANDROID)
|
|
||||||
|
|
||||||
# armeabi-v7a requires cpufeatures library
|
|
||||||
include(AndroidNdkModules)
|
|
||||||
android_ndk_import_module_cpufeatures()
|
|
||||||
|
|
||||||
find_package(PythonInterp 2.7 REQUIRED)
|
|
||||||
|
|
||||||
get_filename_component(C_COMPILER_ID ${CMAKE_C_COMPILER} NAME_WE)
|
|
||||||
get_filename_component(CXX_COMPILER_ID ${CMAKE_CXX_COMPILER} NAME_WE)
|
|
||||||
|
|
||||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
|
||||||
set(BUILD_TYPE "debug")
|
|
||||||
else()
|
|
||||||
set(BUILD_TYPE "release")
|
|
||||||
list(APPEND WAF_EXTRA_ARGS --enable-poly-opt --enable-lto)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
|
|
||||||
set(64BIT ON CACHE BOOL "" FORCE)
|
|
||||||
list(APPEND WAF_EXTRA_ARGS -8) # only required for x86 when testing this cmakelist under linux
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(CMAKE_VERBOSE_MAKEFILE ON)
|
|
||||||
|
|
||||||
set(WAF_CC "${CMAKE_C_COMPILER} --target=${CMAKE_C_COMPILER_TARGET}")
|
|
||||||
set(WAF_CXX "${CMAKE_CXX_COMPILER} --target=${CMAKE_CXX_COMPILER_TARGET}")
|
|
||||||
|
|
||||||
# not cleanest way to get upper directory
|
|
||||||
set(ENGINE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../")
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E env
|
|
||||||
CC=${WAF_CC} CXX=${WAF_CXX}
|
|
||||||
AR=${CMAKE_AR} STRIP=${CMAKE_STRIP}
|
|
||||||
${PYTHON_EXECUTABLE} waf configure -T ${BUILD_TYPE} ${WAF_EXTRA_ARGS} cmake
|
|
||||||
--check-c-compiler=${C_COMPILER_ID} --check-cxx-compiler=${CXX_COMPILER_ID}
|
|
||||||
-s "${ENGINE_SOURCE_DIR}/SDL" --skip-sdl2-sanity-check --enable-bundled-deps
|
|
||||||
WORKING_DIRECTORY "${ENGINE_SOURCE_DIR}"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_subdirectory("${ENGINE_SOURCE_DIR}/3rdparty/hlsdk-portable" hlsdk-portable)
|
|
||||||
|
|
||||||
# try to build minimal SDL. Enable features as we're gonna use them
|
|
||||||
set(SDL_RENDER OFF)
|
|
||||||
set(SDL_POWER OFF)
|
|
||||||
set(SDL_VULKAN OFF)
|
|
||||||
set(SDL_DISKAUDIO OFF)
|
|
||||||
set(SDL_DUMMYAUDIO OFF)
|
|
||||||
set(SDL_DUMMYVIDEO OFF)
|
|
||||||
set(SDL_VULKAN OFF)
|
|
||||||
set(SDL_OFFSCREEN OFF)
|
|
||||||
set(SDL_STATIC OFF)
|
|
||||||
add_subdirectory("${ENGINE_SOURCE_DIR}/3rdparty/SDL" SDL)
|
|
||||||
add_subdirectory("${ENGINE_SOURCE_DIR}/" xash3d-fwgs)
|
|
||||||
add_subdirectory("${ENGINE_SOURCE_DIR}/3rdparty/mainui" mainui)
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.Month
|
|
||||||
import java.time.temporal.ChronoUnit
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id("com.android.application")
|
|
||||||
id("org.jetbrains.kotlin.android")
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "su.xash.engine"
|
|
||||||
ndkVersion = "27.2.12479018"
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
applicationId = "su.xash"
|
|
||||||
applicationIdSuffix = "engine"
|
|
||||||
versionName = "0.21"
|
|
||||||
versionCode = getBuildNum()
|
|
||||||
minSdk = 21
|
|
||||||
targetSdk = 34
|
|
||||||
compileSdk = 34
|
|
||||||
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
abiFilters("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
|
||||||
arguments("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=OFF")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
externalNativeBuild {
|
|
||||||
cmake {
|
|
||||||
version = "3.22.1"
|
|
||||||
path = file("CMakeLists.txt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
debug {
|
|
||||||
isMinifyEnabled = false
|
|
||||||
isShrinkResources = false
|
|
||||||
isDebuggable = true
|
|
||||||
applicationIdSuffix = ".test"
|
|
||||||
proguardFiles(
|
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
release {
|
|
||||||
isMinifyEnabled = true
|
|
||||||
isShrinkResources = true
|
|
||||||
proguardFiles(
|
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
register("asan") {
|
|
||||||
initWith(getByName("debug"))
|
|
||||||
}
|
|
||||||
|
|
||||||
register("continuous") {
|
|
||||||
initWith(getByName("release"))
|
|
||||||
applicationIdSuffix = ".test"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
getByName("main") {
|
|
||||||
assets.srcDirs("../../3rdparty/extras/xash-extras", "../moddb")
|
|
||||||
java.srcDir("../../3rdparty/SDL/android-project/app/src/main/java")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lint {
|
|
||||||
abortOnError = false
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
viewBinding = true
|
|
||||||
buildConfig = true
|
|
||||||
}
|
|
||||||
|
|
||||||
androidResources {
|
|
||||||
noCompress += ""
|
|
||||||
}
|
|
||||||
|
|
||||||
packaging {
|
|
||||||
jniLibs {
|
|
||||||
useLegacyPackaging = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation("com.google.android.material:material:1.11.0")
|
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.7")
|
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.7")
|
|
||||||
implementation("androidx.cardview:cardview:1.0.0")
|
|
||||||
implementation("androidx.annotation:annotation:1.7.1")
|
|
||||||
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
|
||||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
|
||||||
implementation("androidx.work:work-runtime-ktx:2.9.0")
|
|
||||||
// implementation "androidx.legacy:legacy-support-v4:1.0.0"
|
|
||||||
|
|
||||||
implementation("com.madgag.spongycastle:prov:1.58.0.0")
|
|
||||||
implementation("in.dragonbra:javasteam:1.2.0")
|
|
||||||
|
|
||||||
implementation("ch.acra:acra-http:5.11.2")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getBuildNum(): Int {
|
|
||||||
val now = LocalDateTime.now()
|
|
||||||
val releaseDate = LocalDateTime.of(2015, Month.APRIL, 1, 0, 0, 0)
|
|
||||||
val qBuildNum = releaseDate.until(now, ChronoUnit.DAYS)
|
|
||||||
val minuteOfDay = now.hour * 60 + now.minute
|
|
||||||
return (qBuildNum * 10000 + minuteOfDay).toInt()
|
|
||||||
}
|
|
||||||
89
android/app/proguard-rules.pro
vendored
89
android/app/proguard-rules.pro
vendored
@@ -1,89 +0,0 @@
|
|||||||
-keep class su.xash.engine.XashActivity {
|
|
||||||
java.lang.String loadAndroidID();
|
|
||||||
java.lang.String getAndroidID();
|
|
||||||
void saveAndroidID(java.lang.String);
|
|
||||||
java.lang.String getCallingPackage();
|
|
||||||
java.lang.String[] getAssetsList(boolean, java.lang.String);
|
|
||||||
android.content.res.AssetManager getAssets(boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLInputConnection {
|
|
||||||
void nativeCommitText(java.lang.String, int);
|
|
||||||
void nativeGenerateScancodeForUnichar(char);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep,includedescriptorclasses class org.libsdl.app.SDLActivity {
|
|
||||||
# for some reason these aren't compatible with allowoptimization modifier
|
|
||||||
boolean supportsRelativeMouse();
|
|
||||||
void setWindowStyle(boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity {
|
|
||||||
java.lang.String nativeGetHint(java.lang.String); # Java-side doesn't use this, so it gets minified, but C-side still tries to register it
|
|
||||||
boolean onNativeSoftReturnKey();
|
|
||||||
void onNativeKeyboardFocusLost();
|
|
||||||
boolean isScreenKeyboardShown();
|
|
||||||
android.util.DisplayMetrics getDisplayDPI();
|
|
||||||
java.lang.String clipboardGetText();
|
|
||||||
boolean clipboardHasText();
|
|
||||||
void clipboardSetText(java.lang.String);
|
|
||||||
int createCustomCursor(int[], int, int, int, int);
|
|
||||||
void destroyCustomCursor(int);
|
|
||||||
android.content.Context getContext();
|
|
||||||
boolean getManifestEnvironmentVariables();
|
|
||||||
android.view.Surface getNativeSurface();
|
|
||||||
void initTouch();
|
|
||||||
boolean isAndroidTV();
|
|
||||||
boolean isChromebook();
|
|
||||||
boolean isDeXMode();
|
|
||||||
boolean isTablet();
|
|
||||||
void manualBackButton();
|
|
||||||
int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]);
|
|
||||||
void minimizeWindow();
|
|
||||||
int openURL(java.lang.String);
|
|
||||||
void requestPermission(java.lang.String, int);
|
|
||||||
int showToast(java.lang.String, int, int, int, int);
|
|
||||||
boolean sendMessage(int, int);
|
|
||||||
boolean setActivityTitle(java.lang.String);
|
|
||||||
boolean setCustomCursor(int);
|
|
||||||
void setOrientation(int, int, boolean, java.lang.String);
|
|
||||||
boolean setRelativeMouseEnabled(boolean);
|
|
||||||
boolean setSystemCursor(int);
|
|
||||||
boolean shouldMinimizeOnFocusLoss();
|
|
||||||
boolean showTextInput(int, int, int, int);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager {
|
|
||||||
boolean initialize(boolean, boolean);
|
|
||||||
boolean openDevice(int);
|
|
||||||
int sendOutputReport(int, byte[]);
|
|
||||||
int sendFeatureReport(int, byte[]);
|
|
||||||
boolean getFeatureReport(int, byte[]);
|
|
||||||
void closeDevice(int);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager {
|
|
||||||
int[] getAudioOutputDevices();
|
|
||||||
int[] getAudioInputDevices();
|
|
||||||
int[] audioOpen(int, int, int, int, int);
|
|
||||||
void audioWriteFloatBuffer(float[]);
|
|
||||||
void audioWriteShortBuffer(short[]);
|
|
||||||
void audioWriteByteBuffer(byte[]);
|
|
||||||
void audioClose();
|
|
||||||
int[] captureOpen(int, int, int, int, int);
|
|
||||||
int captureReadFloatBuffer(float[], boolean);
|
|
||||||
int captureReadShortBuffer(short[], boolean);
|
|
||||||
int captureReadByteBuffer(byte[], boolean);
|
|
||||||
void captureClose();
|
|
||||||
void audioSetThreadPriority(boolean, int);
|
|
||||||
native int nativeSetupJNI();
|
|
||||||
native void removeAudioDevice(boolean, int);
|
|
||||||
native void addAudioDevice(boolean, int);
|
|
||||||
}
|
|
||||||
|
|
||||||
-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager {
|
|
||||||
void pollInputDevices();
|
|
||||||
void pollHapticDevices();
|
|
||||||
void hapticRun(int, float, int);
|
|
||||||
void hapticStop(int);
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="app_name" translatable="false">Xash3D FWGS (Test)</string>
|
|
||||||
<string name="authority" translatable="false">su.xash.engine.test.documents</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
#!/system/bin/sh
|
|
||||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
||||||
cmd=$1
|
|
||||||
shift
|
|
||||||
# This must be called *before* `LD_PRELOAD` is set. Otherwise, if this is a 32-
|
|
||||||
# bit app running on a 64-bit device, the 64-bit getprop will fail to load
|
|
||||||
# because it will preload a 32-bit ASan runtime.
|
|
||||||
# https://github.com/android/ndk/issues/1744
|
|
||||||
os_version=$(getprop ro.build.version.sdk)
|
|
||||||
if [ "$os_version" -eq "27" ]; then
|
|
||||||
cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
elif [ "$os_version" -eq "28" ]; then
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
else
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
|
|
||||||
fi
|
|
||||||
export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
|
|
||||||
ASAN_LIB=$(ls "$HERE"/libclang_rt.asan-*-android.so)
|
|
||||||
if [ -f "$HERE/libc++_shared.so" ]; then
|
|
||||||
# Workaround for https://github.com/android-ndk/ndk/issues/988.
|
|
||||||
export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
|
|
||||||
else
|
|
||||||
export LD_PRELOAD="$ASAN_LIB"
|
|
||||||
fi
|
|
||||||
exec $cmd
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
#!/system/bin/sh
|
|
||||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
||||||
cmd=$1
|
|
||||||
shift
|
|
||||||
# This must be called *before* `LD_PRELOAD` is set. Otherwise, if this is a 32-
|
|
||||||
# bit app running on a 64-bit device, the 64-bit getprop will fail to load
|
|
||||||
# because it will preload a 32-bit ASan runtime.
|
|
||||||
# https://github.com/android/ndk/issues/1744
|
|
||||||
os_version=$(getprop ro.build.version.sdk)
|
|
||||||
if [ "$os_version" -eq "27" ]; then
|
|
||||||
cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
elif [ "$os_version" -eq "28" ]; then
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
else
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
|
|
||||||
fi
|
|
||||||
export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
|
|
||||||
ASAN_LIB=$(ls "$HERE"/libclang_rt.asan-*-android.so)
|
|
||||||
if [ -f "$HERE/libc++_shared.so" ]; then
|
|
||||||
# Workaround for https://github.com/android-ndk/ndk/issues/988.
|
|
||||||
export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
|
|
||||||
else
|
|
||||||
export LD_PRELOAD="$ASAN_LIB"
|
|
||||||
fi
|
|
||||||
exec $cmd
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
#!/system/bin/sh
|
|
||||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
||||||
cmd=$1
|
|
||||||
shift
|
|
||||||
# This must be called *before* `LD_PRELOAD` is set. Otherwise, if this is a 32-
|
|
||||||
# bit app running on a 64-bit device, the 64-bit getprop will fail to load
|
|
||||||
# because it will preload a 32-bit ASan runtime.
|
|
||||||
# https://github.com/android/ndk/issues/1744
|
|
||||||
os_version=$(getprop ro.build.version.sdk)
|
|
||||||
if [ "$os_version" -eq "27" ]; then
|
|
||||||
cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
elif [ "$os_version" -eq "28" ]; then
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
else
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
|
|
||||||
fi
|
|
||||||
export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
|
|
||||||
ASAN_LIB=$(ls "$HERE"/libclang_rt.asan-*-android.so)
|
|
||||||
if [ -f "$HERE/libc++_shared.so" ]; then
|
|
||||||
# Workaround for https://github.com/android-ndk/ndk/issues/988.
|
|
||||||
export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
|
|
||||||
else
|
|
||||||
export LD_PRELOAD="$ASAN_LIB"
|
|
||||||
fi
|
|
||||||
exec $cmd
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
#!/system/bin/sh
|
|
||||||
HERE=$(cd "$(dirname "$0")" && pwd)
|
|
||||||
cmd=$1
|
|
||||||
shift
|
|
||||||
# This must be called *before* `LD_PRELOAD` is set. Otherwise, if this is a 32-
|
|
||||||
# bit app running on a 64-bit device, the 64-bit getprop will fail to load
|
|
||||||
# because it will preload a 32-bit ASan runtime.
|
|
||||||
# https://github.com/android/ndk/issues/1744
|
|
||||||
os_version=$(getprop ro.build.version.sdk)
|
|
||||||
if [ "$os_version" -eq "27" ]; then
|
|
||||||
cmd="$cmd -Xrunjdwp:transport=dt_android_adb,suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
elif [ "$os_version" -eq "28" ]; then
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y -Xcompiler-option --debuggable $@"
|
|
||||||
else
|
|
||||||
cmd="$cmd -XjdwpProvider:adbconnection -XjdwpOptions:suspend=n,server=y $@"
|
|
||||||
fi
|
|
||||||
export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
|
|
||||||
ASAN_LIB=$(ls "$HERE"/libclang_rt.asan-*-android.so)
|
|
||||||
if [ -f "$HERE/libc++_shared.so" ]; then
|
|
||||||
# Workaround for https://github.com/android-ndk/ndk/issues/988.
|
|
||||||
export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
|
|
||||||
else
|
|
||||||
export LD_PRELOAD="$ASAN_LIB"
|
|
||||||
fi
|
|
||||||
exec $cmd
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="app_name" translatable="false">Xash3D FWGS (Test)</string>
|
|
||||||
<string name="authority" translatable="false">su.xash.engine.test.documents</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="app_name" translatable="false">Xash3D FWGS (Test)</string>
|
|
||||||
<string name="authority" translatable="false">su.xash.engine.test.documents</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:allowAudioPlaybackCapture="true"
|
|
||||||
android:installLocation="preferExternal"
|
|
||||||
tools:targetApi="q">
|
|
||||||
<!-- OpenGL ES 1.1 -->
|
|
||||||
<uses-feature android:glEsVersion="0x00010000" />
|
|
||||||
<!-- Touchscreen support -->
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.touchscreen"
|
|
||||||
android:required="false" />
|
|
||||||
<!-- Game controller support -->
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.bluetooth"
|
|
||||||
android:required="false" />
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.gamepad"
|
|
||||||
android:required="false" />
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.usb.host"
|
|
||||||
android:required="false" />
|
|
||||||
<!-- External mouse input events -->
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.type.pc"
|
|
||||||
android:required="false" />
|
|
||||||
<!-- Audio recording support -->
|
|
||||||
<uses-feature
|
|
||||||
android:name="android.hardware.microphone"
|
|
||||||
android:required="false" />
|
|
||||||
<!-- Allow downloading to the external storage on Android 5.1 and older -->
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
|
||||||
android:maxSdkVersion="22" />
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
|
|
||||||
android:maxSdkVersion="32" />
|
|
||||||
<!-- Allow access to Bluetooth devices -->
|
|
||||||
<!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM -->
|
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.BLUETOOTH"
|
|
||||||
android:maxSdkVersion="30" />
|
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
|
||||||
<!-- Allow access to the vibrator -->
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
|
||||||
<!-- Allow recording audio -->
|
|
||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
||||||
<!-- Allow internet access -->
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
<!-- Dedicated server -->
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:name=".MainApplication"
|
|
||||||
android:allowBackup="true"
|
|
||||||
android:hardwareAccelerated="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:roundIcon="@mipmap/ic_launcher"
|
|
||||||
android:theme="@style/Theme.App"
|
|
||||||
android:usesCleartextTraffic="true">
|
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
<activity
|
|
||||||
android:name=".XashActivity"
|
|
||||||
android:alwaysRetainTaskState="true"
|
|
||||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation|touchscreen"
|
|
||||||
android:exported="true"
|
|
||||||
android:launchMode="singleTask"
|
|
||||||
android:preferMinimalPostProcessing="true"
|
|
||||||
android:windowSoftInputMode="adjustResize"
|
|
||||||
tools:targetApi="r">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<provider
|
|
||||||
android:name=".XashDocumentsProvider"
|
|
||||||
android:authorities="@string/authority"
|
|
||||||
android:exported="true"
|
|
||||||
android:grantUriPermissions="true"
|
|
||||||
android:permission="android.permission.MANAGE_DOCUMENTS">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
|
|
||||||
</intent-filter>
|
|
||||||
</provider>
|
|
||||||
</application>
|
|
||||||
|
|
||||||
<queries>
|
|
||||||
<intent>
|
|
||||||
<action android:name="su.xash.engine.MOD" />
|
|
||||||
</intent>
|
|
||||||
</queries>
|
|
||||||
</manifest>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package su.xash.engine;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
public class AndroidBug5497Workaround {
|
|
||||||
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
|
|
||||||
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
|
|
||||||
|
|
||||||
public static void assistActivity(Activity activity) {
|
|
||||||
new AndroidBug5497Workaround(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private View mChildOfContent;
|
|
||||||
private int usableHeightPrevious;
|
|
||||||
private FrameLayout.LayoutParams frameLayoutParams;
|
|
||||||
|
|
||||||
private AndroidBug5497Workaround(Activity activity) {
|
|
||||||
FrameLayout content = activity.findViewById(android.R.id.content);
|
|
||||||
mChildOfContent = content.getChildAt(0);
|
|
||||||
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(this::possiblyResizeChildOfContent);
|
|
||||||
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void possiblyResizeChildOfContent() {
|
|
||||||
int usableHeightNow = computeUsableHeight();
|
|
||||||
if (usableHeightNow != usableHeightPrevious) {
|
|
||||||
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
|
|
||||||
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
|
|
||||||
if (heightDifference > (usableHeightSansKeyboard / 4)) {
|
|
||||||
// keyboard probably just became visible
|
|
||||||
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
|
|
||||||
} else {
|
|
||||||
// keyboard probably just became hidden
|
|
||||||
frameLayoutParams.height = usableHeightSansKeyboard;
|
|
||||||
}
|
|
||||||
mChildOfContent.requestLayout();
|
|
||||||
usableHeightPrevious = usableHeightNow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int computeUsableHeight() {
|
|
||||||
Rect r = new Rect();
|
|
||||||
mChildOfContent.getWindowVisibleDisplayFrame(r);
|
|
||||||
return (r.bottom - r.top);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
package su.xash.engine
|
|
||||||
|
|
||||||
class DedicatedActivity {
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package su.xash.engine
|
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.IBinder
|
|
||||||
|
|
||||||
class DedicatedService: Service() {
|
|
||||||
override fun onBind(intent: Intent?): IBinder? {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package su.xash.engine
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.fragment.NavHostFragment
|
|
||||||
import androidx.navigation.ui.AppBarConfiguration
|
|
||||||
import androidx.navigation.ui.navigateUp
|
|
||||||
import androidx.navigation.ui.setupActionBarWithNavController
|
|
||||||
import su.xash.engine.databinding.ActivityMainBinding
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
|
||||||
private lateinit var binding: ActivityMainBinding
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
|
||||||
private lateinit var navController: NavController
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
|
|
||||||
val navHostFragment =
|
|
||||||
supportFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment
|
|
||||||
navController = navHostFragment.navController
|
|
||||||
appBarConfiguration = AppBarConfiguration(navController.graph)
|
|
||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package su.xash.engine
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.StrictMode
|
|
||||||
import org.acra.config.httpSender
|
|
||||||
import org.acra.data.StringFormat
|
|
||||||
import org.acra.ktx.initAcra
|
|
||||||
|
|
||||||
class MainApplication : Application() {
|
|
||||||
override fun attachBaseContext(base: Context?) {
|
|
||||||
super.attachBaseContext(base)
|
|
||||||
|
|
||||||
if (!BuildConfig.DEBUG) {
|
|
||||||
initAcra {
|
|
||||||
buildConfigClass = BuildConfig::class.java
|
|
||||||
reportFormat = StringFormat.JSON
|
|
||||||
|
|
||||||
httpSender {
|
|
||||||
uri = "http://bodis.pp.ua:5000/report"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// enable strict mode to detect memory leaks etc.
|
|
||||||
StrictMode.enableDefaults();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
package su.xash.engine;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.pm.ActivityInfo;
|
|
||||||
import android.content.res.AssetManager;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.provider.Settings.Secure;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
|
|
||||||
import org.libsdl.app.SDLActivity;
|
|
||||||
|
|
||||||
public class XashActivity extends SDLActivity {
|
|
||||||
private boolean mUseVolumeKeys;
|
|
||||||
private String mPackageName;
|
|
||||||
private static final String TAG = "XashActivity";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
//getWindow().addFlags(WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES);
|
|
||||||
getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidBug5497Workaround.assistActivity(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String[] getLibraries() {
|
|
||||||
return new String[]{"SDL2", "xash"};
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("HardwareIds")
|
|
||||||
private String getAndroidID() {
|
|
||||||
return Secure.getString(getContentResolver(), Secure.ANDROID_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("ApplySharedPref")
|
|
||||||
private void saveAndroidID(String id) {
|
|
||||||
getSharedPreferences("xash_preferences", MODE_PRIVATE).edit().putString("xash_id", id).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String loadAndroidID() {
|
|
||||||
return getSharedPreferences("xash_preferences", MODE_PRIVATE).getString("xash_id", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCallingPackage() {
|
|
||||||
if (mPackageName != null) {
|
|
||||||
return mPackageName;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.getCallingPackage();
|
|
||||||
}
|
|
||||||
|
|
||||||
private AssetManager getAssets(boolean isEngine) {
|
|
||||||
AssetManager am = null;
|
|
||||||
|
|
||||||
if (isEngine) {
|
|
||||||
am = getAssets();
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
am = getPackageManager().getResourcesForApplication(getCallingPackage()).getAssets();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "Unable to load mod assets!");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return am;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getAssetsList(boolean isEngine, String path) {
|
|
||||||
AssetManager am = getAssets(isEngine);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return am.list(path);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new String[]{};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
|
||||||
if (SDLActivity.mBrokenLibraries) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int keyCode = event.getKeyCode();
|
|
||||||
if (!mUseVolumeKeys) {
|
|
||||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
|
|
||||||
keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
|
|
||||||
keyCode == KeyEvent.KEYCODE_CAMERA ||
|
|
||||||
keyCode == KeyEvent.KEYCODE_ZOOM_IN ||
|
|
||||||
keyCode == KeyEvent.KEYCODE_ZOOM_OUT) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getWindow().superDispatchKeyEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: REMOVE LATER, temporary launchers support?
|
|
||||||
@Override
|
|
||||||
protected String[] getArguments() {
|
|
||||||
String gamedir = getIntent().getStringExtra("gamedir");
|
|
||||||
if (gamedir == null) gamedir = "valve";
|
|
||||||
nativeSetenv("XASH3D_GAME", gamedir);
|
|
||||||
|
|
||||||
String gamelibdir = getIntent().getStringExtra("gamelibdir");
|
|
||||||
if (gamelibdir != null) nativeSetenv("XASH3D_GAMELIBDIR", gamelibdir);
|
|
||||||
|
|
||||||
String pakfile = getIntent().getStringExtra("pakfile");
|
|
||||||
if (pakfile != null) nativeSetenv("XASH3D_EXTRAS_PAK2", pakfile);
|
|
||||||
|
|
||||||
mUseVolumeKeys = getIntent().getBooleanExtra("usevolume", false);
|
|
||||||
mPackageName = getIntent().getStringExtra("package");
|
|
||||||
|
|
||||||
String[] env = getIntent().getStringArrayExtra("env");
|
|
||||||
if (env != null) {
|
|
||||||
for (int i = 0; i < env.length; i += 2)
|
|
||||||
nativeSetenv(env[i], env[i + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
String argv = getIntent().getStringExtra("argv");
|
|
||||||
if (argv == null) argv = "-console -log";
|
|
||||||
return argv.split(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
package su.xash.engine;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.content.res.AssetFileDescriptor;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.MatrixCursor;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.CancellationSignal;
|
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
import android.provider.DocumentsContract.Document;
|
|
||||||
import android.provider.DocumentsContract.Root;
|
|
||||||
import android.provider.DocumentsProvider;
|
|
||||||
import android.webkit.MimeTypeMap;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
|
||||||
public class XashDocumentsProvider extends DocumentsProvider {
|
|
||||||
private static final String ALL_MIME_TYPES = "*/*";
|
|
||||||
private File mRootDir;
|
|
||||||
|
|
||||||
private static final String TAG = "XashDocumentsProvider";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreate() {
|
|
||||||
mRootDir = getContext().getExternalFilesDir(null);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{Root.COLUMN_ROOT_ID,
|
|
||||||
Root.COLUMN_MIME_TYPES,
|
|
||||||
Root.COLUMN_FLAGS,
|
|
||||||
Root.COLUMN_ICON,
|
|
||||||
Root.COLUMN_TITLE,
|
|
||||||
Root.COLUMN_SUMMARY,
|
|
||||||
Root.COLUMN_DOCUMENT_ID,
|
|
||||||
Root.COLUMN_AVAILABLE_BYTES};
|
|
||||||
|
|
||||||
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{Document.COLUMN_DOCUMENT_ID,
|
|
||||||
Document.COLUMN_MIME_TYPE,
|
|
||||||
Document.COLUMN_DISPLAY_NAME,
|
|
||||||
Document.COLUMN_LAST_MODIFIED,
|
|
||||||
Document.COLUMN_FLAGS,
|
|
||||||
Document.COLUMN_SIZE};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor queryRoots(String[] projection) {
|
|
||||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_ROOT_PROJECTION);
|
|
||||||
|
|
||||||
final String appName = getContext().getString(R.string.app_name);
|
|
||||||
final String docId = getDocIdForFile(mRootDir);
|
|
||||||
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH;
|
|
||||||
} else {
|
|
||||||
flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_IS_CHILD;
|
|
||||||
}
|
|
||||||
|
|
||||||
final MatrixCursor.RowBuilder row = result.newRow();
|
|
||||||
row.add(Root.COLUMN_ROOT_ID, docId);
|
|
||||||
row.add(Root.COLUMN_DOCUMENT_ID, docId);
|
|
||||||
row.add(Root.COLUMN_SUMMARY, null);
|
|
||||||
row.add(Root.COLUMN_FLAGS, flags);
|
|
||||||
row.add(Root.COLUMN_TITLE, appName);
|
|
||||||
row.add(Root.COLUMN_MIME_TYPES, ALL_MIME_TYPES);
|
|
||||||
row.add(Root.COLUMN_AVAILABLE_BYTES, mRootDir.getFreeSpace());
|
|
||||||
row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
|
|
||||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
|
||||||
|
|
||||||
includeFile(result, documentId, null);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException {
|
|
||||||
final MatrixCursor result = new MatrixCursor(projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION);
|
|
||||||
|
|
||||||
final File parent = getFileForDocId(parentDocumentId);
|
|
||||||
final File[] filesList = parent.listFiles();
|
|
||||||
|
|
||||||
if (filesList != null) {
|
|
||||||
for (File file : filesList) {
|
|
||||||
includeFile(result, null, file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ParcelFileDescriptor openDocument(final String documentId, String mode, CancellationSignal signal) throws FileNotFoundException {
|
|
||||||
final File file = getFileForDocId(documentId);
|
|
||||||
|
|
||||||
final int accessMode = ParcelFileDescriptor.parseMode(mode);
|
|
||||||
|
|
||||||
return ParcelFileDescriptor.open(file, accessMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException {
|
|
||||||
final File file = getFileForDocId(documentId);
|
|
||||||
|
|
||||||
final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
|
|
||||||
|
|
||||||
return new AssetFileDescriptor(pfd, 0, file.length());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException {
|
|
||||||
File newFile = new File(parentDocumentId, displayName);
|
|
||||||
|
|
||||||
int noConflictId = 1;
|
|
||||||
|
|
||||||
while (newFile.exists()) {
|
|
||||||
newFile = new File(parentDocumentId, displayName + " (" + noConflictId++ + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
boolean succeeded;
|
|
||||||
|
|
||||||
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
|
|
||||||
succeeded = newFile.mkdir();
|
|
||||||
} else {
|
|
||||||
succeeded = newFile.createNewFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!succeeded) {
|
|
||||||
throw new FileNotFoundException("Failed to create document with id " + newFile.getPath());
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new FileNotFoundException("Failed to create document with id " + newFile.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
return newFile.getPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteDocument(String documentId) throws FileNotFoundException {
|
|
||||||
File file = getFileForDocId(documentId);
|
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
if (!deleteDirectory(file)) {
|
|
||||||
throw new FileNotFoundException("Failed to delete document with id " + documentId);
|
|
||||||
}
|
|
||||||
} else if (!file.delete()) {
|
|
||||||
throw new FileNotFoundException("Failed to delete document with id " + documentId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDocumentType(String documentId) throws FileNotFoundException {
|
|
||||||
File file = getFileForDocId(documentId);
|
|
||||||
|
|
||||||
return getMimeType(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isChildDocument(String parentDocumentId, String documentId) {
|
|
||||||
return documentId.startsWith(parentDocumentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File getFileForDocId(String docId) throws FileNotFoundException {
|
|
||||||
final File f = new File(docId);
|
|
||||||
|
|
||||||
if (!f.exists()) throw new FileNotFoundException(f.getAbsolutePath() + " not found");
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getDocIdForFile(File file) {
|
|
||||||
return file.getAbsolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getMimeType(File file) {
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
return Document.MIME_TYPE_DIR;
|
|
||||||
} else {
|
|
||||||
final String name = file.getName();
|
|
||||||
final int lastDot = name.lastIndexOf('.');
|
|
||||||
|
|
||||||
if (lastDot >= 0) {
|
|
||||||
final String extension = name.substring(lastDot + 1).toLowerCase();
|
|
||||||
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
|
||||||
|
|
||||||
if (mime != null) return mime;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "application/octet-stream";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean deleteDirectory(File dir) {
|
|
||||||
final File[] allContents = dir.listFiles();
|
|
||||||
|
|
||||||
if (allContents != null) {
|
|
||||||
for (File file : allContents) {
|
|
||||||
deleteDirectory(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dir.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void includeFile(MatrixCursor result, String docId, File file) throws FileNotFoundException {
|
|
||||||
if (docId == null) {
|
|
||||||
docId = getDocIdForFile(file);
|
|
||||||
} else {
|
|
||||||
file = getFileForDocId(docId);
|
|
||||||
}
|
|
||||||
|
|
||||||
int flags = 0;
|
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
if (file.canWrite()) {
|
|
||||||
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
|
|
||||||
}
|
|
||||||
} else if (file.canWrite()) {
|
|
||||||
flags |= Document.FLAG_SUPPORTS_WRITE;
|
|
||||||
}
|
|
||||||
|
|
||||||
File parentFile = file.getParentFile();
|
|
||||||
if (parentFile != null && parentFile.canWrite()) {
|
|
||||||
flags |= Document.FLAG_SUPPORTS_DELETE;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String displayName = file.getName();
|
|
||||||
final String mimeType = getMimeType(file);
|
|
||||||
|
|
||||||
if (mimeType.startsWith("image/")) {
|
|
||||||
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
final MatrixCursor.RowBuilder row = result.newRow();
|
|
||||||
row.add(Document.COLUMN_DOCUMENT_ID, docId);
|
|
||||||
row.add(Document.COLUMN_DISPLAY_NAME, displayName);
|
|
||||||
row.add(Document.COLUMN_SIZE, file.length());
|
|
||||||
row.add(Document.COLUMN_MIME_TYPE, mimeType);
|
|
||||||
row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
|
||||||
row.add(Document.COLUMN_FLAGS, flags);
|
|
||||||
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package su.xash.engine.adapters
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.navigation.findNavController
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import su.xash.engine.R
|
|
||||||
import su.xash.engine.databinding.CardGameBinding
|
|
||||||
import su.xash.engine.model.Game
|
|
||||||
import su.xash.engine.ui.library.LibraryViewModel
|
|
||||||
|
|
||||||
|
|
||||||
class GameAdapter(private val libraryViewModel: LibraryViewModel) :
|
|
||||||
ListAdapter<Game, GameAdapter.GameViewHolder>(DiffCallback()) {
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameAdapter.GameViewHolder {
|
|
||||||
val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
|
||||||
return GameViewHolder(binding)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: GameAdapter.GameViewHolder, position: Int) {
|
|
||||||
return holder.bind(getItem(position))
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DiffCallback : DiffUtil.ItemCallback<Game>() {
|
|
||||||
override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
|
|
||||||
return oldItem.basedir.name == newItem.basedir.name
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
|
|
||||||
return oldItem.basedir.name == newItem.basedir.name && oldItem.installed == newItem.installed
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class GameViewHolder(val binding: CardGameBinding) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
fun bind(game: Game) {
|
|
||||||
binding.apply {
|
|
||||||
gameTitle.text = game.title
|
|
||||||
|
|
||||||
if (game.icon != null) {
|
|
||||||
gameIcon.setImageBitmap(game.icon)
|
|
||||||
} else {
|
|
||||||
gameIcon.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.cover != null) {
|
|
||||||
gameCover.setImageBitmap(game.cover)
|
|
||||||
} else {
|
|
||||||
gameCover.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!game.installed) {
|
|
||||||
launchButton.visibility = View.GONE
|
|
||||||
settingsButton.visibility = View.GONE
|
|
||||||
progressIndicator.visibility = View.VISIBLE
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
settingsButton.setOnClickListener {
|
|
||||||
libraryViewModel.setSelectedGame(game)
|
|
||||||
it.findNavController()
|
|
||||||
.navigate(R.id.action_libraryFragment_to_gameSettingsFragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
root.setOnClickListener { libraryViewModel.startEngine(it.context, game) }
|
|
||||||
launchButton.setOnClickListener {
|
|
||||||
libraryViewModel.startEngine(it.context, game)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package su.xash.engine.model
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import su.xash.engine.util.TGAReader
|
|
||||||
import java.util.Scanner
|
|
||||||
|
|
||||||
|
|
||||||
object BackgroundBitmap {
|
|
||||||
private const val BACKGROUND_ROWS = 3
|
|
||||||
private const val BACKGROUND_COLUMNS = 4
|
|
||||||
private const val BACKGROUND_WIDTH = 800
|
|
||||||
private const val BACKGROUND_HEIGHT = 600
|
|
||||||
|
|
||||||
fun createBackground(ctx: Context, file: DocumentFile): Bitmap {
|
|
||||||
var bitmap =
|
|
||||||
Bitmap.createBitmap(BACKGROUND_WIDTH, BACKGROUND_HEIGHT, Bitmap.Config.ARGB_8888)
|
|
||||||
var canvas = Canvas(bitmap)
|
|
||||||
var x: Int
|
|
||||||
var y = 0
|
|
||||||
var width: Int
|
|
||||||
var height = 0
|
|
||||||
|
|
||||||
var bgLayout = file.findFile("resource")?.findFile("HD_BackgroundLayout.txt")
|
|
||||||
if (bgLayout == null) {
|
|
||||||
bgLayout = file.findFile("resource")?.findFile("BackgroundLayout.txt")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bgLayout == null) {
|
|
||||||
val dir = file.findFile("resource")?.findFile("background")
|
|
||||||
for (i in 0 until BACKGROUND_ROWS) {
|
|
||||||
x = 0
|
|
||||||
for (j in 0 until BACKGROUND_COLUMNS) {
|
|
||||||
val filename = "${BACKGROUND_WIDTH}_${i + 1}_${'a' + j}_loading.tga"
|
|
||||||
val bmpFile = dir?.findFile(filename)
|
|
||||||
val bmpImage = loadTga(ctx, bmpFile!!)
|
|
||||||
|
|
||||||
canvas.drawBitmap(bmpImage, x.toFloat(), y.toFloat(), null)
|
|
||||||
x += bmpImage.width
|
|
||||||
height = bmpImage.height
|
|
||||||
|
|
||||||
}
|
|
||||||
y += height
|
|
||||||
}
|
|
||||||
return bitmap
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.contentResolver.openInputStream(bgLayout.uri).use { inputStream ->
|
|
||||||
Scanner(inputStream).use { scanner ->
|
|
||||||
while (scanner.hasNext()) {
|
|
||||||
when (val str = scanner.next()) {
|
|
||||||
"resolution" -> {
|
|
||||||
width = scanner.nextInt()
|
|
||||||
height = scanner.nextInt()
|
|
||||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
|
||||||
canvas = Canvas(bitmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
var bmpFile = file
|
|
||||||
str.split("/").forEach { bmpFile = bmpFile.findFile(it)!! }
|
|
||||||
//skip
|
|
||||||
scanner.next()
|
|
||||||
x = scanner.nextInt()
|
|
||||||
y = scanner.nextInt()
|
|
||||||
val bmp = loadTga(ctx, bmpFile)
|
|
||||||
canvas.drawBitmap(bmp, x.toFloat(), y.toFloat(), null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bitmap
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadTga(ctx: Context, file: DocumentFile): Bitmap {
|
|
||||||
ctx.contentResolver.openInputStream(file.uri).use {
|
|
||||||
val buffer = it?.readBytes()
|
|
||||||
val pixels = TGAReader.read(buffer, TGAReader.ARGB)
|
|
||||||
|
|
||||||
val width = TGAReader.getWidth(buffer)
|
|
||||||
val height = TGAReader.getHeight(buffer)
|
|
||||||
|
|
||||||
return Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
package su.xash.engine.model
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageInfo
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.net.Uri
|
|
||||||
import android.provider.MediaStore
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import su.xash.engine.XashActivity
|
|
||||||
|
|
||||||
|
|
||||||
class Game(val ctx: Context, val basedir: DocumentFile, var installed: Boolean = true) {
|
|
||||||
private var iconName = "game.ico"
|
|
||||||
var title = "Unknown Game"
|
|
||||||
var icon: Bitmap? = null
|
|
||||||
var cover: Bitmap? = null
|
|
||||||
|
|
||||||
private val pref = ctx.getSharedPreferences(basedir.name, Context.MODE_PRIVATE)
|
|
||||||
|
|
||||||
init {
|
|
||||||
basedir.findFile("gameinfo.txt")?.let {
|
|
||||||
parseGameInfo(it)
|
|
||||||
} ?: basedir.findFile("liblist.gam")?.let { parseGameInfo(it) }
|
|
||||||
|
|
||||||
basedir.findFile(iconName)
|
|
||||||
?.let { icon = MediaStore.Images.Media.getBitmap(ctx.contentResolver, it.uri) }
|
|
||||||
|
|
||||||
try {
|
|
||||||
cover = BackgroundBitmap.createBackground(ctx, basedir)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startEngine(ctx: Context) {
|
|
||||||
ctx.startActivity(Intent(ctx, XashActivity::class.java).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
||||||
putExtra("gamedir", basedir.name)
|
|
||||||
putExtra("argv", pref.getString("arguments", "-console -log"))
|
|
||||||
putExtra("usevolume", pref.getBoolean("use_volume_buttons", false))
|
|
||||||
//.putExtra("gamelibdir", getGameLibDir(context))
|
|
||||||
//.putExtra("package", getPackageName()) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun parseGameInfo(file: DocumentFile) {
|
|
||||||
ctx.contentResolver.openInputStream(file.uri).use { inputStream ->
|
|
||||||
inputStream?.bufferedReader().use { reader ->
|
|
||||||
reader?.forEachLine {
|
|
||||||
val tokens = it.split("\\s+".toRegex(), limit = 2)
|
|
||||||
if (tokens.size >= 2) {
|
|
||||||
val k = tokens[0]
|
|
||||||
val v = tokens[1].trim('"')
|
|
||||||
|
|
||||||
if (k == "title" || k == "game") title = v
|
|
||||||
if (k == "icon") iconName = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getPackageName(): String? {
|
|
||||||
// return if (mDbEntry != null) {
|
|
||||||
// mDbEntry.getPackageName()
|
|
||||||
// } else null
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getGameLibDir(ctx: Context): String? {
|
|
||||||
val pkgName = getPackageName()
|
|
||||||
if (pkgName != null) {
|
|
||||||
val pkgInfo: PackageInfo = try {
|
|
||||||
ctx.packageManager.getPackageInfo(pkgName, 0)
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
e.printStackTrace()
|
|
||||||
ctx.startActivity(
|
|
||||||
Intent(
|
|
||||||
Intent.ACTION_VIEW, Uri.parse("market://details?id=$pkgName")
|
|
||||||
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
|
||||||
)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return pkgInfo.applicationInfo.nativeLibraryDir
|
|
||||||
}
|
|
||||||
return ctx.applicationInfo.nativeLibraryDir
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun getGames(ctx: Context, file: DocumentFile): List<Game> {
|
|
||||||
val games = mutableListOf<Game>()
|
|
||||||
|
|
||||||
if (checkIfGamedir(file)) {
|
|
||||||
games.add(Game(ctx, file))
|
|
||||||
} else {
|
|
||||||
file.listFiles().forEach {
|
|
||||||
if (it.isDirectory) {
|
|
||||||
if (checkIfGamedir(it)) {
|
|
||||||
games.add(Game(ctx, it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return games
|
|
||||||
}
|
|
||||||
|
|
||||||
fun checkIfGamedir(file: DocumentFile): Boolean {
|
|
||||||
// exclude unfinished downloads
|
|
||||||
if (file.name?.startsWith('.') == true)
|
|
||||||
return false
|
|
||||||
|
|
||||||
file.findFile("liblist.gam")?.let { return true }
|
|
||||||
file.findFile("gameinfo.txt")?.let { return true }
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Intent intent = new Intent("su.xash.engine.MOD");
|
|
||||||
// for (ResolveInfo info : context.getPackageManager()
|
|
||||||
// .queryIntentActivities(intent, PackageManager.GET_META_DATA)) {
|
|
||||||
// String packageName = info.activityInfo.applicationInfo.packageName;
|
|
||||||
// String gameDir = info.activityInfo.applicationInfo.metaData.getString(
|
|
||||||
// "su.xash.engine.gamedir");
|
|
||||||
// Log.d(TAG, "package = " + packageName + " gamedir = " + gameDir);
|
|
||||||
// }
|
|
||||||
|
|
||||||
//public void startEngine(Context context) {
|
|
||||||
// context.startActivity(new Intent(context, XashActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra("gamedir", getGameDir()).putExtra("argv", getArguments()).putExtra("usevolume", getVolumeState()).putExtra("gamelibdir", getGameLibDir(context)).putExtra("package", getPackageName()));
|
|
||||||
//}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package su.xash.engine.model
|
|
||||||
|
|
||||||
import org.json.JSONArray
|
|
||||||
import org.json.JSONObject
|
|
||||||
import java.io.InputStream
|
|
||||||
|
|
||||||
class ModDatabase(inputStream: InputStream) {
|
|
||||||
val entries = mutableListOf<Entry>()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val VERSION = 1
|
|
||||||
|
|
||||||
fun getFilename(): String {
|
|
||||||
return "v${VERSION}.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
inputStream.bufferedReader().use {
|
|
||||||
val jsonArray = JSONArray(it.readText())
|
|
||||||
|
|
||||||
for (i in 0..<jsonArray.length()) {
|
|
||||||
entries.add(Entry(jsonArray.getJSONObject(i)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getByGameDir(gamedir: String): Entry? {
|
|
||||||
return entries.filter { it.gamedir.equals(gamedir) }.firstOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inner class Entry(jsonObject: JSONObject) {
|
|
||||||
var name: String? = null
|
|
||||||
var appid: Int? = null
|
|
||||||
var gamedir: String? = null
|
|
||||||
|
|
||||||
// TODO Depots
|
|
||||||
var pkgname: String? = null
|
|
||||||
|
|
||||||
init {
|
|
||||||
if (jsonObject.has("name")) {
|
|
||||||
name = jsonObject.getString("name");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonObject.has("app_id")) {
|
|
||||||
appid = jsonObject.getInt("app_id");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonObject.has("gamedir")) {
|
|
||||||
gamedir = jsonObject.getString("gamedir");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonObject.has("package_name")) {
|
|
||||||
pkgname = jsonObject.getString("package_name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
package su.xash.engine.ui.library
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuInflater
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.MenuProvider
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import su.xash.engine.R
|
|
||||||
import su.xash.engine.adapters.GameAdapter
|
|
||||||
import su.xash.engine.databinding.FragmentLibraryBinding
|
|
||||||
|
|
||||||
class LibraryFragment : Fragment(), MenuProvider {
|
|
||||||
private var _binding: FragmentLibraryBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
private val libraryViewModel: LibraryViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
_binding = FragmentLibraryBinding.inflate(inflater, container, false)
|
|
||||||
|
|
||||||
val adapter = GameAdapter(libraryViewModel)
|
|
||||||
binding.gamesList.adapter = adapter
|
|
||||||
|
|
||||||
requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
|
|
||||||
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
binding.swipeRefresh.setOnRefreshListener { libraryViewModel.reloadGames(requireContext()) }
|
|
||||||
|
|
||||||
libraryViewModel.isReloading.observe(viewLifecycleOwner) {
|
|
||||||
binding.swipeRefresh.isRefreshing = it
|
|
||||||
}
|
|
||||||
|
|
||||||
libraryViewModel.installedGames.observe(viewLifecycleOwner) {
|
|
||||||
(binding.gamesList.adapter as GameAdapter).submitList(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
libraryViewModel.workInfos.observe(viewLifecycleOwner) {
|
|
||||||
libraryViewModel.refreshDownloads(requireContext())
|
|
||||||
}
|
|
||||||
|
|
||||||
libraryViewModel.downloads.observe(viewLifecycleOwner) {
|
|
||||||
libraryViewModel.reloadGames(requireContext())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
|
||||||
menuInflater.inflate(R.menu.menu_library, menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
|
||||||
when (menuItem.itemId) {
|
|
||||||
R.id.action_browse -> {
|
|
||||||
startActivity(
|
|
||||||
Intent(Intent.ACTION_VIEW).setDataAndType(
|
|
||||||
null, "vnd.android.document/directory"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_install -> {
|
|
||||||
findNavController().navigate(R.id.action_libraryFragment_to_setupFragment)
|
|
||||||
}
|
|
||||||
|
|
||||||
R.id.action_settings -> {
|
|
||||||
findNavController().navigate(R.id.action_libraryFragment_to_appSettingsFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
package su.xash.engine.ui.library
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.work.Data
|
|
||||||
import androidx.work.OneTimeWorkRequestBuilder
|
|
||||||
import androidx.work.OutOfQuotaPolicy
|
|
||||||
import androidx.work.WorkInfo
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import su.xash.engine.model.Game
|
|
||||||
import su.xash.engine.model.ModDatabase
|
|
||||||
import su.xash.engine.workers.FileCopyWorker
|
|
||||||
import su.xash.engine.workers.KEY_FILE_URI
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
const val TAG_INSTALL = "TAG_INSTALL"
|
|
||||||
|
|
||||||
class LibraryViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
val installedGames: LiveData<List<Game>> get() = _installedGames
|
|
||||||
private val _installedGames = MutableLiveData(emptyList<Game>())
|
|
||||||
|
|
||||||
val downloads: LiveData<List<Game>> get() = _downloads
|
|
||||||
private val _downloads = MutableLiveData(emptyList<Game>())
|
|
||||||
|
|
||||||
val isReloading: LiveData<Boolean> get() = _isReloading
|
|
||||||
private val _isReloading = MutableLiveData(false)
|
|
||||||
|
|
||||||
private val workManager = WorkManager.getInstance(application.applicationContext)
|
|
||||||
val workInfos: LiveData<List<WorkInfo>> = workManager.getWorkInfosByTagLiveData(TAG_INSTALL)
|
|
||||||
|
|
||||||
val selectedItem: LiveData<Game> get() = _selectedItem
|
|
||||||
private val _selectedItem = MutableLiveData<Game>()
|
|
||||||
|
|
||||||
val appPreferences = application.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
|
|
||||||
val modDb: ModDatabase
|
|
||||||
|
|
||||||
init {
|
|
||||||
modDb = application.assets.open(ModDatabase.getFilename()).use { ModDatabase(it) }
|
|
||||||
reloadGames(application.applicationContext)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reloadGames(ctx: Context) {
|
|
||||||
if (isReloading.value == true) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_isReloading.value = true
|
|
||||||
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val games = mutableListOf<Game>()
|
|
||||||
val root = DocumentFile.fromFile(ctx.getExternalFilesDir(null)!!)
|
|
||||||
|
|
||||||
val installedGames = Game.getGames(ctx, root)
|
|
||||||
.filter { p -> _downloads.value?.any { p.basedir.name == it.basedir.name } == false }
|
|
||||||
|
|
||||||
games.addAll(installedGames)
|
|
||||||
downloads.value?.let { games.addAll(it) }
|
|
||||||
|
|
||||||
_installedGames.postValue(games)
|
|
||||||
_isReloading.postValue(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun refreshDownloads(ctx: Context) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val games = mutableListOf<Game>()
|
|
||||||
|
|
||||||
workInfos.value?.filter {
|
|
||||||
it.state == WorkInfo.State.RUNNING && !it.progress.getString(FileCopyWorker.Input)
|
|
||||||
.isNullOrEmpty()
|
|
||||||
}?.forEach {
|
|
||||||
val uri = Uri.parse(it.progress.getString(FileCopyWorker.Input))
|
|
||||||
val file = DocumentFile.fromTreeUri(ctx, uri)
|
|
||||||
games.addAll(Game.getGames(ctx, file!!))
|
|
||||||
games.forEach { g -> g.installed = false }
|
|
||||||
}
|
|
||||||
|
|
||||||
_downloads.postValue(games)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun installGame(uri: Uri) {
|
|
||||||
val data = Data.Builder().putString(KEY_FILE_URI, uri.toString()).build()
|
|
||||||
val request = OneTimeWorkRequestBuilder<FileCopyWorker>().run {
|
|
||||||
setInputData(data)
|
|
||||||
addTag(TAG_INSTALL)
|
|
||||||
build()
|
|
||||||
}
|
|
||||||
workManager.enqueue(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSelectedGame(game: Game) {
|
|
||||||
_selectedItem.value = game
|
|
||||||
}
|
|
||||||
|
|
||||||
fun uninstallGame(game: Game) {
|
|
||||||
viewModelScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
game.installed = false
|
|
||||||
game.basedir.delete()
|
|
||||||
_installedGames.postValue(_installedGames.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun startEngine(ctx: Context, game: Game) {
|
|
||||||
game.startEngine(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package su.xash.engine.ui.settings
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import su.xash.engine.databinding.FragmentAppSettingsBinding
|
|
||||||
|
|
||||||
class AppSettingsFragment : Fragment() {
|
|
||||||
private var _binding: FragmentAppSettingsBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
_binding = FragmentAppSettingsBinding.inflate(inflater, container, false)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
childFragmentManager.beginTransaction()
|
|
||||||
.add(binding.settingsFragment.id, AppSettingsPreferenceFragment()).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package su.xash.engine.ui.settings
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import su.xash.engine.R
|
|
||||||
|
|
||||||
class AppSettingsPreferenceFragment() : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
preferenceManager.sharedPreferencesName = "app_preferences";
|
|
||||||
setPreferencesFromResource(R.xml.app_preferences, rootKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package su.xash.engine.ui.settings
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import su.xash.engine.R
|
|
||||||
import su.xash.engine.databinding.FragmentGameSettingsBinding
|
|
||||||
import su.xash.engine.ui.library.LibraryViewModel
|
|
||||||
|
|
||||||
class GameSettingsFragment : Fragment() {
|
|
||||||
private var _binding: FragmentGameSettingsBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
|
|
||||||
private val libraryViewModel: LibraryViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
_binding = FragmentGameSettingsBinding.inflate(inflater, container, false)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
val game = libraryViewModel.selectedItem.value!!
|
|
||||||
|
|
||||||
binding.gameCard.apply {
|
|
||||||
gameTitle.text = game.title
|
|
||||||
|
|
||||||
if (game.icon != null) {
|
|
||||||
gameIcon.setImageBitmap(game.icon)
|
|
||||||
} else {
|
|
||||||
gameIcon.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
if (game.cover != null) {
|
|
||||||
gameCover.setImageBitmap(game.cover)
|
|
||||||
} else {
|
|
||||||
gameCover.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonsContainer.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
childFragmentManager.beginTransaction()
|
|
||||||
.add(binding.settingsFragment.id, GameSettingsPreferenceFragment(game))
|
|
||||||
.commit();
|
|
||||||
|
|
||||||
binding.bottomNavigation.menu.findItem(R.id.action_uninstall).setOnMenuItemClickListener {
|
|
||||||
libraryViewModel.uninstallGame(game)
|
|
||||||
findNavController().popBackStack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package su.xash.engine.ui.settings
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.preference.ListPreference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import androidx.preference.SwitchPreferenceCompat
|
|
||||||
import su.xash.engine.R
|
|
||||||
import su.xash.engine.model.Game
|
|
||||||
|
|
||||||
class GameSettingsPreferenceFragment(val game: Game) : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
preferenceManager.sharedPreferencesName = game.basedir.name;
|
|
||||||
setPreferencesFromResource(R.xml.game_preferences, rootKey);
|
|
||||||
|
|
||||||
val packageList = findPreference<ListPreference>("package_name")!!
|
|
||||||
packageList.entries = arrayOf(getString(R.string.app_name))
|
|
||||||
packageList.entryValues = arrayOf(requireContext().packageName)
|
|
||||||
|
|
||||||
if (packageList.value == null) {
|
|
||||||
packageList.setValueIndex(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
val separatePackages = findPreference<SwitchPreferenceCompat>("separate_libraries")!!
|
|
||||||
val clientPackage = findPreference<ListPreference>("client_package")!!
|
|
||||||
val serverPackage = findPreference<ListPreference>("server_package")!!
|
|
||||||
separatePackages.setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
if (newValue == true) {
|
|
||||||
packageList.isVisible = false
|
|
||||||
clientPackage.isVisible = true
|
|
||||||
serverPackage.isVisible = true
|
|
||||||
} else {
|
|
||||||
packageList.isVisible = true
|
|
||||||
clientPackage.isVisible = false
|
|
||||||
serverPackage.isVisible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package su.xash.engine.ui.setup
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
|
||||||
import su.xash.engine.databinding.FragmentSetupBinding
|
|
||||||
import su.xash.engine.ui.setup.pages.LocationPageFragment
|
|
||||||
import su.xash.engine.ui.setup.pages.WelcomePageFragment
|
|
||||||
|
|
||||||
class SetupFragment : Fragment() {
|
|
||||||
private var _binding: FragmentSetupBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
private val setupViewModel: SetupViewModel by activityViewModels()
|
|
||||||
|
|
||||||
private lateinit var setupPageAdapter: SetupPageAdapter
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
_binding = FragmentSetupBinding.inflate(inflater, container, false)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
setupPageAdapter = SetupPageAdapter(this)
|
|
||||||
binding.viewPager.isUserInputEnabled = false
|
|
||||||
binding.viewPager.adapter = setupPageAdapter
|
|
||||||
|
|
||||||
setupViewModel.pageNumber.observe(viewLifecycleOwner) {
|
|
||||||
binding.viewPager.setCurrentItem(it, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SetupPageAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
|
|
||||||
val pages = listOf(WelcomePageFragment(), LocationPageFragment())
|
|
||||||
override fun getItemCount(): Int = 2
|
|
||||||
override fun createFragment(position: Int): Fragment = pages[position]
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package su.xash.engine.ui.setup
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import su.xash.engine.MainApplication
|
|
||||||
import su.xash.engine.model.Game
|
|
||||||
|
|
||||||
class SetupViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
val pageNumber: LiveData<Int> get() = _pageNumber
|
|
||||||
private val _pageNumber = MutableLiveData(0)
|
|
||||||
|
|
||||||
fun checkIfGameDir(uri: Uri): Boolean {
|
|
||||||
val ctx = getApplication<MainApplication>().applicationContext
|
|
||||||
val file = DocumentFile.fromTreeUri(ctx, uri)!!
|
|
||||||
return Game.checkIfGamedir(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setPageNumber(pos: Int) {
|
|
||||||
_pageNumber.value = pos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package su.xash.engine.ui.setup.pages
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import su.xash.engine.R
|
|
||||||
import su.xash.engine.databinding.PageLocationBinding
|
|
||||||
import su.xash.engine.ui.library.LibraryViewModel
|
|
||||||
import su.xash.engine.ui.setup.SetupViewModel
|
|
||||||
|
|
||||||
class LocationPageFragment : Fragment() {
|
|
||||||
private var _binding: PageLocationBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
private val setupViewModel: SetupViewModel by activityViewModels()
|
|
||||||
private val libraryViewModel: LibraryViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
_binding = PageLocationBinding.inflate(inflater, container, false)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
binding.pageButton.setOnClickListener {
|
|
||||||
getGamesDirectory.launch(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
private val getGamesDirectory =
|
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) {
|
|
||||||
it?.let {
|
|
||||||
if (!setupViewModel.checkIfGameDir(it)) {
|
|
||||||
MaterialAlertDialogBuilder(requireContext()).apply {
|
|
||||||
setTitle(R.string.error)
|
|
||||||
setMessage(R.string.setup_location_empty)
|
|
||||||
setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() }
|
|
||||||
show()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
requireContext().contentResolver.takePersistableUriPermission(it, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
libraryViewModel.installGame(it)
|
|
||||||
findNavController().navigate(R.id.action_setupFragment_to_libraryFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package su.xash.engine.ui.setup.pages
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import androidx.fragment.app.activityViewModels
|
|
||||||
import su.xash.engine.databinding.PageWelcomeBinding
|
|
||||||
import su.xash.engine.ui.setup.SetupViewModel
|
|
||||||
|
|
||||||
class WelcomePageFragment : Fragment() {
|
|
||||||
private var _binding: PageWelcomeBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
|
||||||
private val setupViewModel: SetupViewModel by activityViewModels()
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
|
||||||
): View? {
|
|
||||||
_binding = PageWelcomeBinding.inflate(inflater, container, false)
|
|
||||||
return binding.root
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
binding.pageButton.setOnClickListener {
|
|
||||||
setupViewModel.setPageNumber(1)
|
|
||||||
}
|
|
||||||
setupViewModel.setPageNumber(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,589 +0,0 @@
|
|||||||
/**
|
|
||||||
* TGAReader.java
|
|
||||||
* <p>
|
|
||||||
* Copyright (c) 2014 Kenji Sasaki
|
|
||||||
* Released under the MIT license.
|
|
||||||
* https://github.com/npedotnet/TGAReader/blob/master/LICENSE
|
|
||||||
* <p>
|
|
||||||
* English document
|
|
||||||
* https://github.com/npedotnet/TGAReader/blob/master/README.md
|
|
||||||
* <p>
|
|
||||||
* Japanese document
|
|
||||||
* http://3dtech.jp/wiki/index.php?TGAReader
|
|
||||||
*/
|
|
||||||
|
|
||||||
package su.xash.engine.util;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public final class TGAReader {
|
|
||||||
|
|
||||||
public static final Order ARGB = new Order(16, 8, 0, 24);
|
|
||||||
public static final Order ABGR = new Order(0, 8, 16, 24);
|
|
||||||
|
|
||||||
public static int getWidth(byte[] buffer) {
|
|
||||||
return (buffer[12] & 0xFF) | (buffer[13] & 0xFF) << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getHeight(byte[] buffer) {
|
|
||||||
return (buffer[14] & 0xFF) | (buffer[15] & 0xFF) << 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int[] read(byte[] buffer, Order order) throws IOException {
|
|
||||||
|
|
||||||
// header
|
|
||||||
// int idFieldLength = buffer[0] & 0xFF;
|
|
||||||
// int colormapType = buffer[1] & 0xFF;
|
|
||||||
int type = buffer[2] & 0xFF;
|
|
||||||
int colormapOrigin = (buffer[3] & 0xFF) | (buffer[4] & 0xFF) << 8;
|
|
||||||
int colormapLength = (buffer[5] & 0xFF) | (buffer[6] & 0xFF) << 8;
|
|
||||||
int colormapDepth = buffer[7] & 0xFF;
|
|
||||||
// int originX = (buffer[8] & 0xFF) | (buffer[9] & 0xFF) << 8; // unsupported
|
|
||||||
// int originY = (buffer[10] & 0xFF) | (buffer[11] & 0xFF) << 8; // unsupported
|
|
||||||
int width = getWidth(buffer);
|
|
||||||
int height = getHeight(buffer);
|
|
||||||
int depth = buffer[16] & 0xFF;
|
|
||||||
int descriptor = buffer[17] & 0xFF;
|
|
||||||
|
|
||||||
int[] pixels;
|
|
||||||
|
|
||||||
// data
|
|
||||||
switch (type) {
|
|
||||||
case COLORMAP: {
|
|
||||||
int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
|
|
||||||
pixels = createPixelsFromColormap(width, height, colormapDepth, buffer, imageDataOffset, buffer, colormapOrigin, descriptor, order);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RGB:
|
|
||||||
pixels = createPixelsFromRGB(width, height, depth, buffer, 18, descriptor, order);
|
|
||||||
break;
|
|
||||||
case GRAYSCALE:
|
|
||||||
pixels = createPixelsFromGrayscale(width, height, depth, buffer, 18, descriptor, order);
|
|
||||||
break;
|
|
||||||
case COLORMAP_RLE: {
|
|
||||||
int imageDataOffset = 18 + (colormapDepth / 8) * colormapLength;
|
|
||||||
byte[] decodeBuffer = decodeRLE(width, height, depth, buffer, imageDataOffset);
|
|
||||||
pixels = createPixelsFromColormap(width, height, colormapDepth, decodeBuffer, 0, buffer, colormapOrigin, descriptor, order);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case RGB_RLE: {
|
|
||||||
byte[] decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
|
|
||||||
pixels = createPixelsFromRGB(width, height, depth, decodeBuffer, 0, descriptor, order);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GRAYSCALE_RLE: {
|
|
||||||
byte[] decodeBuffer = decodeRLE(width, height, depth, buffer, 18);
|
|
||||||
pixels = createPixelsFromGrayscale(width, height, depth, decodeBuffer, 0, descriptor, order);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IOException("Unsupported image type: " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pixels;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int COLORMAP = 1;
|
|
||||||
private static final int RGB = 2;
|
|
||||||
private static final int GRAYSCALE = 3;
|
|
||||||
private static final int COLORMAP_RLE = 9;
|
|
||||||
private static final int RGB_RLE = 10;
|
|
||||||
private static final int GRAYSCALE_RLE = 11;
|
|
||||||
|
|
||||||
private static final int RIGHT_ORIGIN = 0x10;
|
|
||||||
private static final int UPPER_ORIGIN = 0x20;
|
|
||||||
|
|
||||||
private static byte[] decodeRLE(int width, int height, int depth, byte[] buffer, int offset) {
|
|
||||||
int elementCount = depth / 8;
|
|
||||||
byte[] elements = new byte[elementCount];
|
|
||||||
int decodeBufferLength = elementCount * width * height;
|
|
||||||
byte[] decodeBuffer = new byte[decodeBufferLength];
|
|
||||||
int decoded = 0;
|
|
||||||
while (decoded < decodeBufferLength) {
|
|
||||||
int packet = buffer[offset++] & 0xFF;
|
|
||||||
if ((packet & 0x80) != 0) { // RLE
|
|
||||||
for (int i = 0; i < elementCount; i++) {
|
|
||||||
elements[i] = buffer[offset++];
|
|
||||||
}
|
|
||||||
int count = (packet & 0x7F) + 1;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
for (int j = 0; j < elementCount; j++) {
|
|
||||||
decodeBuffer[decoded++] = elements[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // RAW
|
|
||||||
int count = (packet + 1) * elementCount;
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
decodeBuffer[decoded++] = buffer[offset++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return decodeBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] createPixelsFromColormap(int width, int height, int depth, byte[] bytes, int offset, byte[] palette, int colormapOrigin, int descriptor, Order order) throws IOException {
|
|
||||||
int[] pixels;
|
|
||||||
int rs = order.redShift;
|
|
||||||
int gs = order.greenShift;
|
|
||||||
int bs = order.blueShift;
|
|
||||||
int as = order.alphaShift;
|
|
||||||
switch (depth) {
|
|
||||||
case 24:
|
|
||||||
pixels = new int[width * height];
|
|
||||||
if ((descriptor & RIGHT_ORIGIN) != 0) {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 3 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * i + (width - j - 1)] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 3 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * (height - i - 1) + (width - j - 1)] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 3 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * i + j] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 3 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * (height - i - 1) + j] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
pixels = new int[width * height];
|
|
||||||
if ((descriptor & RIGHT_ORIGIN) != 0) {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 4 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = palette[index + 3] & 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * i + (width - j - 1)] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 4 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = palette[index + 3] & 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * (height - i - 1) + (width - j - 1)] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 4 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = palette[index + 3] & 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * i + j] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int colormapIndex = bytes[offset + width * i + j] &
|
|
||||||
0xFF - colormapOrigin;
|
|
||||||
int color = 0xFFFFFFFF;
|
|
||||||
if (colormapIndex >= 0) {
|
|
||||||
int index = 4 * colormapIndex + 18;
|
|
||||||
int b = palette[index] & 0xFF;
|
|
||||||
int g = palette[index + 1] & 0xFF;
|
|
||||||
int r = palette[index + 2] & 0xFF;
|
|
||||||
int a = palette[index + 3] & 0xFF;
|
|
||||||
color = (r << rs) | (g << gs) | (b << bs) | (a << as);
|
|
||||||
}
|
|
||||||
pixels[width * (height - i - 1) + j] = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IOException("Unsupported depth:" + depth);
|
|
||||||
}
|
|
||||||
return pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] createPixelsFromRGB(int width, int height, int depth, byte[] bytes, int offset, int descriptor, Order order) throws IOException {
|
|
||||||
int[] pixels;
|
|
||||||
int rs = order.redShift;
|
|
||||||
int gs = order.greenShift;
|
|
||||||
int bs = order.blueShift;
|
|
||||||
int as = order.alphaShift;
|
|
||||||
switch (depth) {
|
|
||||||
case 24:
|
|
||||||
pixels = new int[width * height];
|
|
||||||
if ((descriptor & RIGHT_ORIGIN) != 0) {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 3 * width * i + 3 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * i + (width - j - 1)] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 3 * width * i + 3 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + (width - j - 1)] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 3 * width * i + 3 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * i + j] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 3 * width * i + 3 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + j] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 32:
|
|
||||||
pixels = new int[width * height];
|
|
||||||
if ((descriptor & RIGHT_ORIGIN) != 0) {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 4 * width * i + 4 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = bytes[index + 3] & 0xFF;
|
|
||||||
pixels[width * i + (width - j - 1)] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 4 * width * i + 4 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = bytes[index + 3] & 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + (width - j - 1)] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 4 * width * i + 4 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = bytes[index + 3] & 0xFF;
|
|
||||||
pixels[width * i + j] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int index = offset + 4 * width * i + 4 * j;
|
|
||||||
int b = bytes[index] & 0xFF;
|
|
||||||
int g = bytes[index + 1] & 0xFF;
|
|
||||||
int r = bytes[index + 2] & 0xFF;
|
|
||||||
int a = bytes[index + 3] & 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + j] = (r << rs) |
|
|
||||||
(g << gs) |
|
|
||||||
(b << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IOException("Unsupported depth:" + depth);
|
|
||||||
}
|
|
||||||
return pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] createPixelsFromGrayscale(int width, int height, int depth, byte[] bytes, int offset, int descriptor, Order order) throws IOException {
|
|
||||||
int[] pixels;
|
|
||||||
int rs = order.redShift;
|
|
||||||
int gs = order.greenShift;
|
|
||||||
int bs = order.blueShift;
|
|
||||||
int as = order.alphaShift;
|
|
||||||
switch (depth) {
|
|
||||||
case 8:
|
|
||||||
pixels = new int[width * height];
|
|
||||||
if ((descriptor & RIGHT_ORIGIN) != 0) {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + width * i + j] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * i + (width - j - 1)] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + width * i + j] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + (width - j - 1)] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + width * i + j] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * i + j] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + width * i + j] & 0xFF;
|
|
||||||
int a = 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + j] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
pixels = new int[width * height];
|
|
||||||
if ((descriptor & RIGHT_ORIGIN) != 0) {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
|
|
||||||
int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
|
|
||||||
pixels[width * i + (width - j - 1)] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerRight
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
|
|
||||||
int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + (width - j - 1)] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ((descriptor & UPPER_ORIGIN) != 0) {
|
|
||||||
// UpperLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
|
|
||||||
int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
|
|
||||||
pixels[width * i + j] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// LowerLeft
|
|
||||||
for (int i = 0; i < height; i++) {
|
|
||||||
for (int j = 0; j < width; j++) {
|
|
||||||
int e = bytes[offset + 2 * width * i + 2 * j] & 0xFF;
|
|
||||||
int a = bytes[offset + 2 * width * i + 2 * j + 1] & 0xFF;
|
|
||||||
pixels[width * (height - i - 1) + j] = (e << rs) |
|
|
||||||
(e << gs) |
|
|
||||||
(e << bs) |
|
|
||||||
(a << as);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new IOException("Unsupported depth:" + depth);
|
|
||||||
}
|
|
||||||
return pixels;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TGAReader() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Order {
|
|
||||||
Order(int redShift, int greenShift, int blueShift, int alphaShift) {
|
|
||||||
this.redShift = redShift;
|
|
||||||
this.greenShift = greenShift;
|
|
||||||
this.blueShift = blueShift;
|
|
||||||
this.alphaShift = alphaShift;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int redShift;
|
|
||||||
public int greenShift;
|
|
||||||
public int blueShift;
|
|
||||||
public int alphaShift;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package su.xash.engine.workers
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
|
||||||
import androidx.work.CoroutineWorker
|
|
||||||
import androidx.work.WorkerParameters
|
|
||||||
import androidx.work.workDataOf
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
const val KEY_FILE_URI = "KEY_FILE_URI"
|
|
||||||
|
|
||||||
class FileCopyWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
|
|
||||||
companion object {
|
|
||||||
const val Input = "Input"
|
|
||||||
const val Progress = "Progress"
|
|
||||||
}
|
|
||||||
|
|
||||||
private var fileCount = 0
|
|
||||||
private var fileCopied = 0
|
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
val fileUri = inputData.getString(KEY_FILE_URI)
|
|
||||||
setProgress(workDataOf(Input to fileUri))
|
|
||||||
|
|
||||||
val uri = Uri.parse(fileUri)
|
|
||||||
val source = DocumentFile.fromTreeUri(applicationContext, uri)
|
|
||||||
|
|
||||||
fileCount = source?.countDirFiles() ?: return@withContext Result.failure()
|
|
||||||
|
|
||||||
setProgress(workDataOf(Progress to 0f))
|
|
||||||
|
|
||||||
val gamedir = source.name!!
|
|
||||||
val externalFilesDir = DocumentFile.fromFile(applicationContext.getExternalFilesDir(null)!!)
|
|
||||||
|
|
||||||
// create a directory to store staged files
|
|
||||||
val target = externalFilesDir.createDirectory(".$gamedir")!!
|
|
||||||
|
|
||||||
source.copyDirTo(applicationContext, this@FileCopyWorker, target)
|
|
||||||
|
|
||||||
target.renameTo(gamedir)
|
|
||||||
|
|
||||||
setProgress(workDataOf(Progress to 1f))
|
|
||||||
}
|
|
||||||
return Result.success()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun fileCopied(count: Int) {
|
|
||||||
if(count == 0)
|
|
||||||
return
|
|
||||||
|
|
||||||
fileCopied += count
|
|
||||||
val percentage: Float = fileCopied.toFloat() / fileCount.toFloat();
|
|
||||||
setProgress(workDataOf(Progress to percentage))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun DocumentFile.countDirFiles(): Int {
|
|
||||||
var count: Int = 0
|
|
||||||
|
|
||||||
listFiles().forEach {
|
|
||||||
if (it.isDirectory)
|
|
||||||
count += it.countDirFiles()
|
|
||||||
else
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
fun DocumentFile.copyFileTo(ctx: Context, file: DocumentFile) {
|
|
||||||
val outFile = file.createFile("application", name!!)!!
|
|
||||||
|
|
||||||
ctx.contentResolver.openOutputStream(outFile.uri).use { os ->
|
|
||||||
ctx.contentResolver.openInputStream(uri).use {
|
|
||||||
it?.copyTo(os!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun DocumentFile.copyDirTo(ctx: Context, worker: FileCopyWorker, dir: DocumentFile) {
|
|
||||||
var count: Int = 0
|
|
||||||
|
|
||||||
listFiles().forEach {
|
|
||||||
if (it.isDirectory) {
|
|
||||||
val outDir = dir.createDirectory(it.name!!)!!
|
|
||||||
it.copyDirTo(ctx, worker, outDir)
|
|
||||||
} else {
|
|
||||||
it.copyFileTo(ctx, dir)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
worker.fileCopied(count)
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#000000"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M21,10h-8.35C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H13l2,2l2,-2l2,2l4,-4.04L21,10zM7,15c-1.65,0 -3,-1.35 -3,-3c0,-1.65 1.35,-3 3,-3s3,1.35 3,3C10,13.65 8.65,15 7,15z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#000000"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="#000000"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M8,5v14l11,-7z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M20,4H4C2.89,4 2,4.9 2,6v12c0,1.1 0.89,2 2,2h16c1.1,0 2,-0.9 2,-2V6C22,4.9 21.11,4 20,4zM20,18H4V8h16V18zM18,17h-6v-2h6V17zM7.5,17l-1.41,-1.41L8.67,13l-2.59,-2.59L7.5,9l4,4L7.5,17z" />
|
|
||||||
</vector>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_enabled="true">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<gradient android:endColor="#2D73FF" android:startColor="#06BFFF" />
|
|
||||||
<corners android:radius="4dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
<item android:state_enabled="false">
|
|
||||||
<shape android:shape="rectangle">
|
|
||||||
<gradient android:endColor="#6D6D6D" android:startColor="#8F8F8F" />
|
|
||||||
<corners android:radius="4dp" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</selector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="M12.004,2c-5.25,0 -9.556,4.05 -9.964,9.197l5.36,2.216c0.454,-0.31 1.002,-0.492 1.593,-0.492 0.053,0 0.104,0.003 0.157,0.005l2.384,-3.452v-0.049c0,-2.08 1.69,-3.77 3.77,-3.77 2.079,0 3.77,1.692 3.77,3.772s-1.692,3.771 -3.77,3.771h-0.087l-3.397,2.426c0,0.043 0.003,0.088 0.003,0.133 0,1.562 -1.262,2.83 -2.825,2.83 -1.362,0 -2.513,-0.978 -2.775,-2.273l-3.838,-1.589C3.573,18.922 7.427,22 12.005,22c5.522,0 9.998,-4.477 9.998,-10 0,-5.522 -4.477,-10 -9.999,-10zM7.078,16.667c0.218,0.452 0.595,0.832 1.094,1.041 1.081,0.45 2.328,-0.063 2.777,-1.145 0.22,-0.525 0.22,-1.1 0.004,-1.625 -0.215,-0.525 -0.625,-0.934 -1.147,-1.152 -0.52,-0.217 -1.075,-0.208 -1.565,-0.025l1.269,0.525c0.797,0.333 1.174,1.25 0.84,2.046 -0.33,0.797 -1.247,1.175 -2.044,0.843l-1.228,-0.508zM17.818,9.422c0,-1.385 -1.128,-2.512 -2.513,-2.512 -1.387,0 -2.512,1.127 -2.512,2.512 0,1.388 1.125,2.513 2.512,2.513 1.386,0 2.512,-1.125 2.512,-2.513zM15.31,7.53c1.04,0 1.888,0.845 1.888,1.888s-0.847,1.888 -1.888,1.888c-1.044,0 -1.888,-0.845 -1.888,-1.888s0.845,-1.888 1.888,-1.888z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="295.46dp"
|
|
||||||
android:height="90.47dp"
|
|
||||||
android:viewportWidth="295.46"
|
|
||||||
android:viewportHeight="90.47">
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m45.08,1c-23.24,0 -42.28,17.92 -44.08,40.69l23.71,9.8c2.01,-1.37 4.44,-2.18 7.05,-2.18 0.23,0 0.47,0.01 0.7,0.02l10.54,-15.28c0,-0.07 0,-0.14 0,-0.22 0,-9.2 7.48,-16.68 16.68,-16.68 9.2,0 16.68,7.48 16.68,16.68s-7.48,16.68 -16.68,16.68c-0.13,0 -0.25,0 -0.38,-0.01l-15.04,10.73c0.01,0.19 0.01,0.39 0.01,0.59 0,6.91 -5.62,12.52 -12.52,12.52 -6.06,0 -11.13,-4.33 -12.28,-10.06l-16.96,-7.01c5.25,18.57 22.31,32.18 42.56,32.18 24.43,0 44.24,-19.81 44.24,-44.24 0,-24.43 -19.81,-44.24 -44.24,-44.24"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m28.72,68.12 l-5.43,-2.24c0.96,2.01 2.63,3.68 4.84,4.61 4.78,1.99 10.3,-0.28 12.29,-5.06 0.96,-2.31 0.97,-4.87 0.01,-7.19 -0.95,-2.32 -2.76,-4.13 -5.07,-5.1 -2.3,-0.96 -4.76,-0.92 -6.93,-0.1l5.61,2.32c3.53,1.47 5.2,5.52 3.72,9.05 -1.47,3.53 -5.52,5.2 -9.05,3.72"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m70.8,33.83c0,-6.13 -4.99,-11.12 -11.12,-11.12 -6.13,0 -11.12,4.99 -11.12,11.12 0,6.13 4.99,11.11 11.12,11.11 6.13,0 11.12,-4.99 11.12,-11.11m-19.45,-0.02c0,-4.61 3.74,-8.35 8.35,-8.35s8.35,3.74 8.35,8.35 -3.74,8.35 -8.35,8.35 -8.35,-3.74 -8.35,-8.35"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m136.56,31.27 l-2.96,5.21c-2.28,-1.6 -5.38,-2.56 -8.08,-2.56 -3.09,0 -5,1.28 -5,3.57 0,2.78 3.39,3.43 8.44,5.24 5.42,1.92 8.54,4.17 8.54,9.14 0,6.79 -5.34,10.61 -13.02,10.61 -3.74,0 -8.26,-0.97 -11.73,-3.08l2.16,-5.78c2.82,1.49 6.19,2.37 9.2,2.37 4.05,0 5.98,-1.5 5.98,-3.7 0,-2.53 -2.94,-3.29 -7.68,-4.86 -5.4,-1.8 -9.15,-4.17 -9.15,-9.67 0,-6.2 4.96,-9.76 12.1,-9.76 4.98,0 8.98,1.58 11.2,3.26"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m152.76,61.9v-27.34h-10.13v-5.99h27.21v5.99h-10.1v27.34z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m197.9,42.05v5.99h-13.36v7.82h15.5v6.04h-22.47v-33.33h22.47v5.97h-15.5v7.51z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m215.62,55.43 l-2.21,6.47h-7.32l12.49,-33.33h7.03l12.85,33.32h-7.56l-2.25,-6.47h-13.03zM222.07,36.52 L217.51,49.87h9.2z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m261.22,60.93 l-8.97,-19.3v20.27h-6.68v-33.33h6.67l11.2,24.06 10.8,-24.06h6.73v33.33h-6.68v-20.44l-9.12,19.47z"/>
|
|
||||||
<path
|
|
||||||
android:fillColor="#FF000000"
|
|
||||||
android:pathData="m294.46,32.78c0,2.86 -2.15,4.65 -4.61,4.65 -2.47,0 -4.62,-1.78 -4.62,-4.65 0,-2.86 2.15,-4.64 4.62,-4.64 2.46,0 4.61,1.77 4.61,4.64m-8.46,0c0,2.4 1.73,3.9 3.85,3.9 2.11,0 3.83,-1.5 3.83,-3.9 0,-2.4 -1.72,-3.88 -3.83,-3.88 -2.12,0 -3.85,1.5 -3.85,3.88m3.91,-2.37c1.2,0 1.6,0.63 1.6,1.32 0,0.63 -0.37,1.05 -0.82,1.26l1.07,2.01h-0.88l-0.9,-1.78h-0.93v1.78h-0.73v-4.58zM289.05,32.54h0.81c0.53,0 0.84,-0.33 0.84,-0.75 0,-0.42 -0.22,-0.69 -0.84,-0.69h-0.81v1.44z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/appBarLayout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/fragmentContainerView"
|
|
||||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
app:defaultNavHost="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/appBarLayout"
|
|
||||||
app:navGraph="@navigation/nav_graph"
|
|
||||||
tools:layout="@layout/fragment_library"/>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
style="@style/Widget.Material3.CardView.Filled"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="128dp">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/gameCover"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
tools:src="@android:mipmap/sym_def_app_icon" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
style="@style/Widget.Material3.CardView.Filled"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
app:contentPadding="10dp"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/buttonsContainer"
|
|
||||||
app:layout_constraintHorizontal_bias="0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/gameIcon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_marginRight="8dp"
|
|
||||||
android:adjustViewBounds="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/gameTitle"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:src="@android:mipmap/sym_def_app_icon" />
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
|
||||||
android:id="@+id/gameTitle"
|
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:gravity="center"
|
|
||||||
android:singleLine="true"
|
|
||||||
app:layout_constrainedWidth="true"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/gameIcon"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="Game Title" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/buttonsContainer"
|
|
||||||
style="@style/Widget.Material3.CardView.Filled"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
|
||||||
android:id="@+id/progressIndicator"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:indeterminate="true"
|
|
||||||
android:padding="10dp"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:indicatorSize="20dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/launchButton"
|
|
||||||
style="@style/Widget.Material3.Button.IconButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:icon="@drawable/ic_baseline_play_arrow_24"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/settingsButton" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/settingsButton"
|
|
||||||
style="@style/Widget.Material3.Button.IconButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:icon="@drawable/ic_baseline_settings_24"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<!-- <com.google.android.material.textfield.TextInputLayout-->
|
|
||||||
<!-- android:layout_width="match_parent"-->
|
|
||||||
<!-- android:layout_height="wrap_content"-->
|
|
||||||
<!-- app:layout_constraintTop_toTopOf="parent"-->
|
|
||||||
<!-- app:layout_constraintBottom_toBottomOf="parent">-->
|
|
||||||
|
|
||||||
<!-- <com.google.android.material.textfield.TextInputEditText-->
|
|
||||||
<!-- android:layout_width="match_parent"-->
|
|
||||||
<!-- android:layout_height="wrap_content" />-->
|
|
||||||
<!-- </com.google.android.material.textfield.TextInputLayout>-->
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/summary"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@android:id/title"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"/>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingHorizontal="8dp">
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/settingsFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:layout="@android:layout/list_content" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/gameCard"
|
|
||||||
layout="@layout/card_game"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
style="@style/Widget.Material3.CardView.Filled"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="8dp"
|
|
||||||
app:contentPadding="16dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/gameCard">
|
|
||||||
|
|
||||||
<androidx.fragment.app.FragmentContainerView
|
|
||||||
android:id="@+id/settingsFragment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
tools:layout="@android:layout/list_content" />
|
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
|
||||||
android:id="@+id/bottomNavigation"
|
|
||||||
style="@style/Widget.MaterialComponents.BottomNavigationView.Colored"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:menu="@menu/menu_game_settings" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:id="@+id/swipeRefresh">
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/gamesList"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layoutManager="LinearLayoutManager"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingHorizontal="8dp"
|
|
||||||
tools:listitem="@layout/card_game" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
|
||||||
android:id="@+id/viewPager"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/steam_background"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:orientation="vertical"
|
|
||||||
tools:context=".fragment.SteamLoginFragment">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/imageView"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@drawable/steam_logo"
|
|
||||||
app:tint="#C5C3C0" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:layout_width="320dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:startIconDrawable="@drawable/ic_baseline_person_24">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/steamLogin"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/username"
|
|
||||||
android:inputType="textEmailAddress" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:layout_width="320dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
|
|
||||||
app:startIconDrawable="@drawable/ic_baseline_key_24">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/steamPassword"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/password"
|
|
||||||
android:inputType="textPassword" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/loginButton"
|
|
||||||
style="@style/App.Theme.SteamButton"
|
|
||||||
android:layout_width="240dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="@string/login" />
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/title"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/summary"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toBottomOf="@android:id/title"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.BodyMedium"/>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/pagePic"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginTop="64dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
android:src="@drawable/ic_baseline_folder_open_24"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/pageTitle"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/pageTitle"
|
|
||||||
style="@style/TextAppearance.Material3.TitleLarge"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:text="@string/setup_location_title"
|
|
||||||
android:textStyle="bold"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/pageMessage"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/pagePic" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/pageMessage"
|
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:text="@string/setup_location_message"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/pageButton"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/pageTitle" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/pageButton"
|
|
||||||
android:layout_width="240dp"
|
|
||||||
android:layout_marginBottom="128dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/ok"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/pageMessage" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/pagePic"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginTop="64dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
android:src="@mipmap/ic_launcher"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/pageTitle"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/pageTitle"
|
|
||||||
style="@style/TextAppearance.Material3.TitleLarge"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:text="@string/setup_welcome_title"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/pageMessage"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/pagePic" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/pageMessage"
|
|
||||||
style="@style/TextAppearance.Material3.TitleMedium"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:text="@string/setup_welcome_message"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/pageButton"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/pageTitle" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/pageButton"
|
|
||||||
android:layout_width="240dp"
|
|
||||||
android:layout_marginBottom="128dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/next"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/pageMessage" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:padding="32dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/textView"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/steam_guard_code"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.TitleLarge"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:id="@+id/textInputLayout"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/textView">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/steamCode"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:hint="@string/steam_guard_code" />
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/title"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
android:textAppearance="@style/TextAppearance.Material3.TitleMedium"/>
|
|
||||||
|
|
||||||
<com.google.android.material.materialswitch.MaterialSwitch
|
|
||||||
android:id="@+id/switchWidget"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_dedicated"
|
|
||||||
android:icon="@drawable/ic_baseline_terminal_24"
|
|
||||||
android:title="@string/dedicated_server"
|
|
||||||
app:showAsAction="always|withText"
|
|
||||||
android:visible="false"/>
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_uninstall"
|
|
||||||
android:icon="@drawable/ic_baseline_delete_24"
|
|
||||||
android:title="@string/uninstall"
|
|
||||||
app:showAsAction="always|withText" />
|
|
||||||
</menu>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_browse"
|
|
||||||
android:icon="@drawable/ic_baseline_folder_open_24"
|
|
||||||
android:title="@string/browse"
|
|
||||||
app:showAsAction="always|withText" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_install"
|
|
||||||
android:icon="@drawable/ic_baseline_add_24"
|
|
||||||
android:title="@string/install"
|
|
||||||
app:showAsAction="always|withText" />
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_settings"
|
|
||||||
android:icon="@drawable/ic_baseline_settings_24"
|
|
||||||
android:title="@string/app_settings"
|
|
||||||
app:showAsAction="always|withText"
|
|
||||||
android:visible="false"/>
|
|
||||||
</menu>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
|
||||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="108dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="108dp">
|
|
||||||
|
|
||||||
<path android:fillColor="#FFFFFF" android:pathData="M352.3,335.9l0,64.8l45.7,0l69.9,113l-109.9,171.8l87.3,0l68.4,-103.5l65.5,103.5l90,0l0,-62l-42,-0.7l-70.4,-112.1l109.3,-174.8l-84.1,0l-68.3,106.4l-69,-106.4z"/>
|
|
||||||
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="108dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="108dp">
|
|
||||||
|
|
||||||
<path android:fillColor="#FF000000" android:pathData="M352.3,335.9l0,64.8l45.7,0l69.9,113l-109.9,171.8l87.3,0l68.4,-103.5l65.5,103.5l90,0l0,-62l-42,-0.7l-70.4,-112.1l109.3,-174.8l-84.1,0l-68.3,106.4l-69,-106.4z"/>
|
|
||||||
|
|
||||||
</vector>
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<background android:drawable="@color/ic_launcher_background"/>
|
|
||||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
|
||||||
</adaptive-icon>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5.4 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user