Merge branch 'main' into bsp-compilation
8
.github/dependabot.yml
vendored
@@ -10,4 +10,10 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "friday"
|
||||
day: "friday"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "src/ModManUi"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
day: "friday"
|
||||
|
||||
4
.github/workflows/ci.yaml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
update-alternatives --set g++ /usr/bin/g++-13
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v5
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
@@ -88,7 +88,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v5
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 24
|
||||
|
||||
|
||||
6
.github/workflows/release.yaml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
chmod +x build/bin/Release_x86/{ImageConverter,Unlinker,Linker}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: oat-linux
|
||||
path: |
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
run: msbuild /m /p:Configuration=Release /p:Platform=Win32 build
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: oat-windows
|
||||
path: |
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
actions: read
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v5
|
||||
- uses: actions/download-artifact@v6
|
||||
- name: Zip artifacts
|
||||
run: |
|
||||
7z a oat-linux.tar ./oat-linux/*
|
||||
|
||||
3
.gitignore
vendored
@@ -2,4 +2,5 @@ local/
|
||||
/build/
|
||||
.vscode
|
||||
.idea
|
||||
user*.*
|
||||
user*.*
|
||||
*.aps
|
||||
|
||||
BIN
repo/logo.ico
Normal file
|
After Width: | Height: | Size: 122 KiB |
41
repo/logo.svg
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="180mm"
|
||||
height="280mm"
|
||||
viewBox="0 0 180 280"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
sodipodi:docname="oat.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.55748644"
|
||||
inkscape:cx="452.02893"
|
||||
inkscape:cy="637.68367"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1129"
|
||||
inkscape:window-x="1672"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><path
|
||||
style="fill:#ca893d;stroke:none;stroke-width:0.881059"
|
||||
d="m 98.086477,10.284091 c -8.72518,12.545685 -15.237436,27.541318 -20.227373,41.960408 -3.104147,8.969857 -5.812459,19.004812 -4.896093,28.595242 0.41179,4.309657 1.595038,9.342328 3.883297,13.054348 1.19243,1.93436 3.972179,3.54359 4.644286,5.59952 1.132057,3.462881 0.909997,7.594171 1.067247,11.184641 0.22594,5.15968 0.51324,10.35958 0.28634,15.5409 -0.3654,8.34369 -3.489751,17.57917 -6.829117,25.17624 -2.04753,4.65813 -5.389125,9.45537 -5.902181,14.60844 H 69.802066 C 69.428692,157.30909 68.39352,149.17787 64.82898,141.1384 75.011385,136.3776 73.657115,122.65058 70.794713,113.78643 65.563672,97.587209 53.473562,84.176688 41.857476,72.149305 37.619329,67.76109 33.443501,62.288958 28.463292,58.771675 c 1.201365,14.708585 3.193196,29.718655 6.39527,44.136135 1.511576,6.80593 3.110873,14.60973 6.361456,20.82479 3.151072,6.02479 6.473392,11.85107 12.41952,15.52075 1.752628,1.08164 3.561182,1.74167 5.59472,2.0594 1.176058,0.18376 2.636821,-0.0988 3.528117,0.85691 1.173307,1.25817 1.535394,3.59115 2.066605,5.1851 1.69453,5.08456 2.404493,10.21034 2.76954,15.54089 1.469279,21.45514 -6.881483,41.26494 -14.154923,60.9203 -2.206922,5.96387 -1.917171,12.42981 -4.426517,18.33825 -2.403204,5.65852 -5.04249,10.67632 -8.37313,15.84715 -2.374548,3.68649 -5.354731,7.31098 -5.964301,11.81564 2.47018,-0.1903 3.038119,-2.82753 4.388386,-4.57352 3.89855,-5.04115 7.715127,-10.16928 10.461561,-15.94046 3.33192,-7.0015 3.745199,-14.84262 6.313487,-22.06806 2.818465,-7.92927 7.795915,-15.19426 10.096059,-23.31135 2.396537,-8.45722 3.160481,-18.98132 8.199263,-26.40872 1.46339,-2.15711 6.39217,1.07699 8.718006,1.4102 6.147516,0.88066 13.038256,-1.07257 18.338249,-4.15983 14.87798,-8.66642 26.73707,-22.12908 36.95136,-35.77476 3.64006,-4.8629 7.90141,-9.65862 10.91459,-14.94707 -17.5133,5.69666 -36.27908,7.95457 -52.528213,16.98706 -7.78769,4.32897 -16.026201,10.01268 -20.284457,18.13536 -2.56198,4.88701 -4.187247,11.9503 -4.891786,17.40579 h -0.310817 c 0.614948,-7.72723 3.670776,-18.18531 8.250587,-24.50184 3.749657,-5.17156 8.837313,-9.2803 13.094873,-14.01175 2.36883,-2.63249 4.88214,-5.13015 7.20804,-7.80282 1.245893,-1.43163 2.551273,-3.52678 4.393193,-4.25887 1.20406,-0.47856 2.57834,0.48361 3.72981,0.79503 1.8591,0.50279 3.99226,0.48511 5.90554,0.3312 5.65903,-0.45521 11.62338,-4.4788 15.83947,-7.99228 8.21271,-6.84406 14.21432,-17.10575 19.05247,-26.479481 5.65071,-10.948038 11.25178,-22.296627 15.46055,-33.879144 -7.39769,3.100324 -14.60944,8.079765 -21.44643,12.290015 -13.6263,8.391132 -28.16089,17.768635 -36.53572,31.84612 -2.21587,3.72473 -4.21456,8.09477 -4.75461,12.43271 -0.32263,2.59168 0.73864,7.23757 -0.56096,9.3243 -5.272543,8.46593 -15.701159,13.80125 -20.31228,22.68994 h -0.310811 c 3.562782,-9.69925 5.533592,-19.77335 5.279342,-30.14934 -0.0931,-3.79938 -0.23558,-7.72278 -0.70557,-11.50025 -0.20864,-1.67681 -1.0711,-4.18252 -0.56768,-5.834791 0.44415,-1.4578 5.237446,-2.58065 6.561706,-3.63844 3.757,-3.00105 6.54717,-7.522098 8.25898,-11.973201 4.556173,-11.847047 4.432803,-25.46557 3.579913,-37.919777 -0.82233,-12.008128 -1.45907,-24.202473 -3.757633,-36.05487 h -0.62164 M 41.206824,136.16532 c -4.577383,14.0675 -9.920637,27.90215 -13.512903,42.27122 -1.868976,7.47591 -3.775657,15.23233 -3.672256,23.00052 0.08961,6.73198 0.506919,13.59238 3.971806,19.58033 1.116888,1.93019 2.668842,3.59228 4.510453,4.84237 1.047967,0.71137 2.550759,1.50965 2.260382,2.98203 -2.011788,10.20091 -7.414724,20.8876 -7.54309,31.33985 -0.03129,2.54764 -0.188076,4.91327 -0.0089,7.45962 0.06313,0.89695 0.05796,2.52568 1.28644,2.62757 2.264499,0.18782 1.724703,-7.8199 1.837087,-9.46556 0.716764,-10.49542 4.319911,-21.07358 6.250172,-31.38683 0.412302,-2.20291 5.085544,-3.54345 6.784267,-4.9213 3.678849,-2.98392 6.186696,-7.17302 7.860861,-11.55782 6.997983,-18.32831 1.510219,-40.7053 -3.89505,-58.74457 -1.125111,-3.75489 -2.43295,-7.44285 -3.578958,-11.18944 -0.692919,-2.26534 -1.097206,-4.91594 -2.550342,-6.83799 m 68.379927,45.06858 c -7.01423,5.71207 -13.581003,12.15259 -20.203163,18.31044 -13.160612,12.23781 -29.547148,26.49536 -30.980819,45.71803 -0.232877,3.1224 0.512818,7.05521 1.688402,9.94618 0.542897,1.33509 1.866557,2.60843 2.060126,4.04063 0.502499,3.71786 -3.512484,7.06263 -2.295402,11.18944 h 0.310818 c 1.63038,-2.80664 2.85027,-6.70538 3.108178,-9.94617 2.677512,0.39543 4.987636,1.24904 7.770447,0.83893 7.879489,-1.16126 13.744304,-8.70168 17.87778,-14.82573 8.74176,-12.95153 13.041363,-29.03305 16.444333,-44.13613 1.14054,-5.06196 2.39095,-10.11934 3.31274,-15.23008 0.31706,-1.75783 1.43015,-4.18134 0.90656,-5.90554 z"
|
||||
id="path1" /></g></svg>
|
||||
|
After Width: | Height: | Size: 5.8 KiB |
BIN
repo/logo_circle.ico
Normal file
|
After Width: | Height: | Size: 130 KiB |
77
repo/logo_circle.svg
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="300mm"
|
||||
height="300mm"
|
||||
viewBox="0 0 300 300"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
sodipodi:docname="oat_circle.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.55748644"
|
||||
inkscape:cx="452.02893"
|
||||
inkscape:cy="637.68367"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1129"
|
||||
inkscape:window-x="1672"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter19"
|
||||
x="-0.16704432"
|
||||
y="-0.08986926"
|
||||
width="1.3601893"
|
||||
height="1.1937806"><feFlood
|
||||
result="flood"
|
||||
in="SourceGraphic"
|
||||
flood-opacity="0.403922"
|
||||
flood-color="rgb(0,0,0)"
|
||||
id="feFlood18" /><feGaussianBlur
|
||||
result="blur"
|
||||
in="SourceGraphic"
|
||||
stdDeviation="8.000000"
|
||||
id="feGaussianBlur18" /><feOffset
|
||||
result="offset"
|
||||
in="blur"
|
||||
dx="3.000000"
|
||||
dy="3.000000"
|
||||
id="feOffset18" /><feComposite
|
||||
result="comp1"
|
||||
operator="in"
|
||||
in="flood"
|
||||
in2="offset"
|
||||
id="feComposite18" /><feComposite
|
||||
result="comp2"
|
||||
operator="over"
|
||||
in="SourceGraphic"
|
||||
in2="comp1"
|
||||
id="feComposite19" /></filter></defs><g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><circle
|
||||
style="fill:#ca893d;stroke-width:0.264583;fill-opacity:1"
|
||||
id="path2"
|
||||
cx="149.73645"
|
||||
cy="151.63484"
|
||||
r="128" /><path
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0.724;fill-opacity:1;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter19)"
|
||||
d="m 157.15533,40.658523 c -7.16528,10.302747 -12.51327,22.617436 -16.6111,34.458658 -2.54917,7.366211 -4.77329,15.607101 -4.02076,23.482935 0.33817,3.539174 1.30988,7.672094 3.18904,10.720474 0.97925,1.58854 3.26203,2.91006 3.81397,4.59843 0.92967,2.84378 0.74731,6.23647 0.87645,9.18503 0.18554,4.23723 0.42148,8.50748 0.23514,12.76248 -0.30007,6.85199 -2.86584,14.43633 -5.6082,20.67519 -1.68147,3.82534 -4.42564,7.76492 -4.84697,11.99672 h -0.25525 c -0.30662,-7.14028 -1.15672,-13.81779 -4.08399,-20.41995 8.36198,-3.90966 7.24983,-15.18254 4.89917,-22.46194 -4.29583,-13.3031 -14.22445,-24.31606 -23.76379,-34.193176 -3.48045,-3.603683 -6.90971,-8.097499 -10.999551,-10.985956 0.986581,12.07896 2.622311,24.405512 5.251911,36.245412 1.24134,5.58915 2.55471,11.99777 5.22415,17.1017 2.58771,4.94766 5.31606,9.73231 10.19914,12.74592 1.43929,0.88826 2.9245,1.43029 4.59448,1.69122 0.9658,0.15091 2.16541,-0.0811 2.89736,0.70371 0.96354,1.03323 1.26089,2.94912 1.69713,4.2581 1.39158,4.17553 1.97461,8.38492 2.2744,12.76246 1.2066,17.61936 -5.6512,33.88753 -11.62428,50.02887 -1.81237,4.89764 -1.57442,10.20759 -3.63514,15.05971 -1.97356,4.64688 -4.14099,8.76759 -6.87617,13.01397 -1.95003,3.02741 -4.3974,6.00391 -4.89799,9.70322 2.02855,-0.15628 2.49496,-2.32202 3.60382,-3.75586 3.20156,-4.13988 6.3358,-8.3512 8.59123,-13.0906 2.73623,-5.74976 3.07562,-12.18903 5.18474,-18.12269 2.31458,-6.51166 6.40216,-12.47781 8.29107,-19.14371 1.96809,-6.94523 2.59545,-15.58781 6.73339,-21.68733 1.20177,-1.77146 5.24937,0.88445 7.15939,1.15809 5.04845,0.72321 10.70726,-0.88082 15.0597,-3.41613 12.21808,-7.11703 21.95698,-18.17281 30.34514,-29.37889 2.98928,-3.99351 6.48878,-7.93184 8.96326,-12.27481 -14.38224,4.6782 -29.79304,6.53244 -43.13713,13.95008 -6.39539,3.55503 -13.16101,8.2226 -16.65797,14.89309 -2.10395,4.01331 -3.43865,9.81381 -4.01723,14.29396 h -0.25524 c 0.50501,-6.34574 3.0145,-14.93411 6.77553,-20.12136 3.07929,-4.24698 7.25737,-7.62116 10.75375,-11.50671 1.94533,-2.16185 4.00931,-4.21297 5.91938,-6.40782 1.02315,-1.17568 2.09515,-2.89626 3.60777,-3.49746 0.98879,-0.393 2.11738,0.39715 3.06298,0.65289 1.52673,0.4129 3.27852,0.39838 4.84974,0.27199 4.6473,-0.37383 9.54534,-3.67807 13.00767,-6.56341 6.74442,-5.62047 11.67306,-14.04755 15.64624,-21.74543 4.64046,-8.990734 9.24016,-18.310406 12.69648,-27.822182 -6.07512,2.546043 -11.99754,6.635251 -17.6122,10.092786 -11.19017,6.890952 -23.12624,14.591936 -30.0038,26.152626 -1.81972,3.05881 -3.46108,6.64757 -3.90458,10.20997 -0.26495,2.12833 0.60659,5.94362 -0.46067,7.65728 -4.32991,6.95238 -12.89408,11.33384 -16.68082,18.6334 h -0.25524 c 2.92582,-7.9652 4.54429,-16.23824 4.33549,-24.75919 -0.0764,-3.12012 -0.19346,-6.34209 -0.57942,-9.44422 -0.17134,-1.37703 -0.87961,-3.43476 -0.46619,-4.79164 0.36474,-1.19717 4.30109,-2.11927 5.38859,-2.98795 3.08532,-2.46452 5.37666,-6.17729 6.78243,-9.83261 3.74161,-9.72902 3.6403,-20.912802 2.93989,-31.140425 -0.67532,-9.861296 -1.19822,-19.875516 -3.08584,-29.608922 h -0.5105 M 110.44471,144.0345 c -3.75903,11.55249 -8.14701,22.91376 -11.097041,34.71391 -1.534837,6.13935 -3.100637,12.50907 -3.015727,18.88845 0.0736,5.52842 0.41629,11.16231 3.261717,16.07972 0.917211,1.58511 2.191701,2.95005 3.704061,3.97665 0.86062,0.58419 2.09474,1.23975 1.85628,2.44889 -1.65212,8.37718 -6.089111,17.15329 -6.194528,25.73687 -0.0257,2.09216 -0.15445,4.03486 -0.007,6.12597 0.0519,0.73659 0.0476,2.07414 1.056448,2.15781 1.85965,0.15424 1.41636,-6.42184 1.50865,-7.77329 0.58862,-8.61903 3.54759,-17.30601 5.13276,-25.77544 0.33859,-1.80907 4.17634,-2.90995 5.57136,-4.04146 3.02114,-2.45045 5.08063,-5.89062 6.45548,-9.4915 5.74688,-15.05154 1.24022,-33.42794 -3.19868,-48.24212 -0.92396,-3.08358 -1.99799,-6.1122 -2.9391,-9.18897 -0.56904,-1.86034 -0.90105,-4.03706 -2.0944,-5.61549 m 56.15486,37.01115 c -5.76022,4.69086 -11.15297,9.97993 -16.59121,15.03687 -10.80773,10.04991 -24.26466,21.75848 -25.44202,37.54449 -0.19124,2.56417 0.42114,5.79387 1.38655,8.16798 0.44584,1.0964 1.53285,2.14209 1.69181,3.31824 0.41267,3.05318 -2.88452,5.79996 -1.88503,9.18898 h 0.25525 c 1.3389,-2.30487 2.3407,-5.50659 2.5525,-8.16798 2.19882,0.32473 4.09594,1.02573 6.38123,0.68894 6.47079,-0.95364 11.28708,-7.14598 14.68157,-12.17516 7.17889,-10.63603 10.7098,-23.84247 13.50438,-36.2454 0.93663,-4.15697 1.96349,-8.31019 2.72048,-12.50722 0.26038,-1.44356 1.17447,-3.43379 0.74449,-4.84974 z"
|
||||
id="path1" /></g></svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
26
src/Common/Game/CommonAsset.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "CommonAsset.h"
|
||||
|
||||
#include "IW3/CommonAssetIW3.h"
|
||||
#include "IW4/CommonAssetIW4.h"
|
||||
#include "IW5/CommonAssetIW5.h"
|
||||
#include "T5/CommonAssetT5.h"
|
||||
#include "T6/CommonAssetT6.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
ICommonAssetTypeMapper* ICommonAssetTypeMapper::GetCommonAssetMapperByGame(GameId gameId)
|
||||
{
|
||||
static ICommonAssetTypeMapper* assetTypeMappers[static_cast<unsigned>(GameId::COUNT)]{
|
||||
new IW3::CommonAssetTypeMapper(),
|
||||
new IW4::CommonAssetTypeMapper(),
|
||||
new IW5::CommonAssetTypeMapper(),
|
||||
new T5::CommonAssetTypeMapper(),
|
||||
new T6::CommonAssetTypeMapper(),
|
||||
};
|
||||
|
||||
assert(static_cast<unsigned>(gameId) < static_cast<unsigned>(GameId::COUNT));
|
||||
auto* result = assetTypeMappers[static_cast<unsigned>(gameId)];
|
||||
assert(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
181
src/Common/Game/CommonAsset.h
Normal file
@@ -0,0 +1,181 @@
|
||||
#pragma once
|
||||
|
||||
#include "IGame.h"
|
||||
#include "Zone/ZoneTypes.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
enum class CommonAssetType : std::uint8_t
|
||||
{
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
PHYS_PRESET,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
XANIM,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
XMODEL,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
MATERIAL,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
TECHNIQUE_SET,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
IMAGE,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
SOUND,
|
||||
// IW3, IW4, IW5
|
||||
SOUND_CURVE,
|
||||
// IW3, IW4, IW5
|
||||
LOADED_SOUND,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
CLIP_MAP,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
COM_WORLD,
|
||||
// IW3, IW4, T5, T6
|
||||
GAME_WORLD_SP,
|
||||
// IW3, IW4, T5, T6
|
||||
GAME_WORLD_MP,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
MAP_ENTS,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
GFX_WORLD,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
LIGHT_DEF,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
UI_MAP,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
FONT,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
MENU_LIST,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
MENU,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
LOCALIZE_ENTRY,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
WEAPON,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
SOUND_DRIVER_GLOBALS,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
FX,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
IMPACT_FX,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
AI_TYPE,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
MP_TYPE,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
CHARACTER,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
XMODEL_ALIAS,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
RAW_FILE,
|
||||
// IW3, IW4, IW5, T5, T6
|
||||
STRING_TABLE,
|
||||
|
||||
// IW3, T5, T6
|
||||
XMODEL_PIECES,
|
||||
|
||||
// IW4, IW5
|
||||
PHYS_COLL_MAP,
|
||||
// IW4, IW5
|
||||
XMODEL_SURFS,
|
||||
// IW4, IW5
|
||||
PIXEL_SHADER,
|
||||
// IW4, IW5
|
||||
VERTEX_SHADER,
|
||||
// IW4, IW5
|
||||
VERTEX_DECL,
|
||||
// IW4, IW5
|
||||
FX_WORLD,
|
||||
// IW4, IW5, T6
|
||||
LEADERBOARD,
|
||||
// IW4, IW5
|
||||
STRUCTURED_DATA_DEF,
|
||||
// IW4, IW5, T6
|
||||
TRACER,
|
||||
// IW4, IW5, T6
|
||||
VEHICLE,
|
||||
// IW4, IW5, T6
|
||||
ADDON_MAP_ENTS,
|
||||
|
||||
// IW5
|
||||
GLASS_WORLD,
|
||||
// IW5
|
||||
PATH_DATA,
|
||||
// IW5
|
||||
VEHICLE_TRACK,
|
||||
// IW5, T6
|
||||
ATTACHMENT,
|
||||
// IW5
|
||||
SURFACE_FX,
|
||||
// IW5, T6
|
||||
SCRIPT,
|
||||
|
||||
// T5, T6
|
||||
PHYS_CONSTRAINTS,
|
||||
// T5, T6
|
||||
DESTRUCTIBLE_DEF,
|
||||
// T5, T6
|
||||
SOUND_PATCH,
|
||||
// T5, T6
|
||||
WEAPON_DEF,
|
||||
// T5, T6
|
||||
WEAPON_VARIANT,
|
||||
// T5, T6
|
||||
MP_BODY,
|
||||
// T5, T6
|
||||
MP_HEAD,
|
||||
// T5
|
||||
PACK_INDEX,
|
||||
// T5, T6
|
||||
XGLOBALS,
|
||||
// T5, T6
|
||||
DDL,
|
||||
// T5, T6
|
||||
GLASSES,
|
||||
// T5, T6
|
||||
EMBLEM_SET,
|
||||
|
||||
// T6
|
||||
FONT_ICON,
|
||||
// T6
|
||||
WEAPON_FULL,
|
||||
// T6
|
||||
ATTACHMENT_UNIQUE,
|
||||
// T6
|
||||
WEAPON_CAMO,
|
||||
// T6
|
||||
KEY_VALUE_PAIRS,
|
||||
// T6
|
||||
MEMORY_BLOCK,
|
||||
// T6
|
||||
SKINNED_VERTS,
|
||||
// T6
|
||||
QDB,
|
||||
// T6
|
||||
SLUG,
|
||||
// T6
|
||||
FOOTSTEP_TABLE,
|
||||
// T6
|
||||
FOOTSTEP_FX_TABLE,
|
||||
// T6
|
||||
ZBARRIER,
|
||||
|
||||
COUNT,
|
||||
};
|
||||
|
||||
class ICommonAssetTypeMapper
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] virtual CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const = 0;
|
||||
[[nodiscard]] virtual std::optional<asset_type_t> CommonToGameAssetType(CommonAssetType commonAssetType) const = 0;
|
||||
|
||||
static ICommonAssetTypeMapper* GetCommonAssetMapperByGame(GameId gameId);
|
||||
|
||||
protected:
|
||||
ICommonAssetTypeMapper() = default;
|
||||
virtual ~ICommonAssetTypeMapper() = default;
|
||||
ICommonAssetTypeMapper(const ICommonAssetTypeMapper& other) = default;
|
||||
ICommonAssetTypeMapper(ICommonAssetTypeMapper&& other) noexcept = default;
|
||||
ICommonAssetTypeMapper& operator=(const ICommonAssetTypeMapper& other) = default;
|
||||
ICommonAssetTypeMapper& operator=(ICommonAssetTypeMapper&& other) noexcept = default;
|
||||
};
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
#include "GameLanguage.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
enum class GameId
|
||||
enum class GameId : std::uint8_t
|
||||
{
|
||||
IW3,
|
||||
IW4,
|
||||
@@ -18,7 +19,7 @@ enum class GameId
|
||||
|
||||
// The full uppercase names are macros in the standard lib
|
||||
// So unfortunately not usable as values in the enum
|
||||
enum class GameEndianness
|
||||
enum class GameEndianness : std::uint8_t
|
||||
{
|
||||
/* Little endian */
|
||||
LE,
|
||||
@@ -26,12 +27,19 @@ enum class GameEndianness
|
||||
BE
|
||||
};
|
||||
|
||||
enum class GameWordSize
|
||||
enum class GameWordSize : std::uint8_t
|
||||
{
|
||||
ARCH_32,
|
||||
ARCH_64
|
||||
};
|
||||
|
||||
enum class GamePlatform : std::uint8_t
|
||||
{
|
||||
PC,
|
||||
XBOX,
|
||||
PS3
|
||||
};
|
||||
|
||||
static constexpr const char* GameId_Names[]{
|
||||
"IW3",
|
||||
"IW4",
|
||||
|
||||
100
src/Common/Game/IW3/CommonAssetIW3.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "CommonAssetIW3.h"
|
||||
|
||||
#include "IW3.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace IW3
|
||||
{
|
||||
CommonAssetTypeMapper::CommonAssetTypeMapper() = default;
|
||||
|
||||
CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const
|
||||
{
|
||||
static CommonAssetType lookupTable[static_cast<unsigned>(ASSET_TYPE_COUNT)]{
|
||||
CommonAssetType::XMODEL_PIECES, // ASSET_TYPE_XMODELPIECES
|
||||
CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET
|
||||
CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS
|
||||
CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL
|
||||
CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL
|
||||
CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET
|
||||
CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE
|
||||
CommonAssetType::SOUND, // ASSET_TYPE_SOUND
|
||||
CommonAssetType::SOUND_CURVE, // ASSET_TYPE_SOUND_CURVE
|
||||
CommonAssetType::LOADED_SOUND, // ASSET_TYPE_LOADED_SOUND
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_PVS
|
||||
CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD
|
||||
CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP
|
||||
CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP
|
||||
CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS
|
||||
CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD
|
||||
CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF
|
||||
CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP
|
||||
CommonAssetType::FONT, // ASSET_TYPE_FONT
|
||||
CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST
|
||||
CommonAssetType::MENU, // ASSET_TYPE_MENU
|
||||
CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY
|
||||
CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON
|
||||
CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS
|
||||
CommonAssetType::FX, // ASSET_TYPE_FX
|
||||
CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX
|
||||
CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE
|
||||
CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE
|
||||
CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER
|
||||
CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS
|
||||
CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE
|
||||
CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE
|
||||
};
|
||||
|
||||
assert(gameAssetType < ASSET_TYPE_COUNT);
|
||||
return lookupTable[gameAssetType];
|
||||
}
|
||||
|
||||
std::optional<asset_type_t> CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const
|
||||
{
|
||||
#define MAP_COMMON(common, game) \
|
||||
case common: \
|
||||
return game;
|
||||
|
||||
switch (commonAssetType)
|
||||
{
|
||||
MAP_COMMON(CommonAssetType::XMODEL_PIECES, ASSET_TYPE_XMODELPIECES)
|
||||
MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET)
|
||||
MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS)
|
||||
MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL)
|
||||
MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL)
|
||||
MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET)
|
||||
MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE)
|
||||
MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND)
|
||||
MAP_COMMON(CommonAssetType::SOUND_CURVE, ASSET_TYPE_SOUND_CURVE)
|
||||
MAP_COMMON(CommonAssetType::LOADED_SOUND, ASSET_TYPE_LOADED_SOUND)
|
||||
MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_PVS)
|
||||
MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP)
|
||||
MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS)
|
||||
MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD)
|
||||
MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF)
|
||||
MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP)
|
||||
MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT)
|
||||
MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST)
|
||||
MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU)
|
||||
MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY)
|
||||
MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON)
|
||||
MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS)
|
||||
MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX)
|
||||
MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX)
|
||||
MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE)
|
||||
MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE)
|
||||
MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER)
|
||||
MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS)
|
||||
MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE)
|
||||
MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE)
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#undef MAP_COMMON
|
||||
}
|
||||
} // namespace IW3
|
||||
15
src/Common/Game/IW3/CommonAssetIW3.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/CommonAsset.h"
|
||||
|
||||
namespace IW3
|
||||
{
|
||||
class CommonAssetTypeMapper final : public ICommonAssetTypeMapper
|
||||
{
|
||||
public:
|
||||
CommonAssetTypeMapper();
|
||||
|
||||
[[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override;
|
||||
[[nodiscard]] std::optional<asset_type_t> CommonToGameAssetType(CommonAssetType commonAssetType) const override;
|
||||
};
|
||||
} // namespace IW3
|
||||
120
src/Common/Game/IW4/CommonAssetIW4.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "CommonAssetIW4.h"
|
||||
|
||||
#include "IW4.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace IW4
|
||||
{
|
||||
CommonAssetTypeMapper::CommonAssetTypeMapper() = default;
|
||||
|
||||
CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const
|
||||
{
|
||||
static CommonAssetType lookupTable[static_cast<unsigned>(ASSET_TYPE_COUNT)]{
|
||||
CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET
|
||||
CommonAssetType::PHYS_COLL_MAP, // ASSET_TYPE_PHYSCOLLMAP
|
||||
CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS
|
||||
CommonAssetType::XMODEL_SURFS, // ASSET_TYPE_XMODEL_SURFS
|
||||
CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL
|
||||
CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL
|
||||
CommonAssetType::PIXEL_SHADER, // ASSET_TYPE_PIXELSHADER
|
||||
CommonAssetType::VERTEX_SHADER, // ASSET_TYPE_VERTEXSHADER
|
||||
CommonAssetType::VERTEX_DECL, // ASSET_TYPE_VERTEXDECL
|
||||
CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET
|
||||
CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE
|
||||
CommonAssetType::SOUND, // ASSET_TYPE_SOUND
|
||||
CommonAssetType::SOUND_CURVE, // ASSET_TYPE_SOUND_CURVE
|
||||
CommonAssetType::LOADED_SOUND, // ASSET_TYPE_LOADED_SOUND
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_SP
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_MP
|
||||
CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD
|
||||
CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP
|
||||
CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP
|
||||
CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS
|
||||
CommonAssetType::FX_WORLD, // ASSET_TYPE_FXWORLD
|
||||
CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD
|
||||
CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF
|
||||
CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP
|
||||
CommonAssetType::FONT, // ASSET_TYPE_FONT
|
||||
CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST
|
||||
CommonAssetType::MENU, // ASSET_TYPE_MENU
|
||||
CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY
|
||||
CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON
|
||||
CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS
|
||||
CommonAssetType::FX, // ASSET_TYPE_FX
|
||||
CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX
|
||||
CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE
|
||||
CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE
|
||||
CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER
|
||||
CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS
|
||||
CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE
|
||||
CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE
|
||||
CommonAssetType::LEADERBOARD, // ASSET_TYPE_LEADERBOARD
|
||||
CommonAssetType::STRUCTURED_DATA_DEF, // ASSET_TYPE_STRUCTURED_DATA_DEF
|
||||
CommonAssetType::TRACER, // ASSET_TYPE_TRACER
|
||||
CommonAssetType::VEHICLE, // ASSET_TYPE_VEHICLE
|
||||
CommonAssetType::ADDON_MAP_ENTS, // ASSET_TYPE_ADDON_MAP_ENTS
|
||||
};
|
||||
|
||||
assert(gameAssetType < ASSET_TYPE_COUNT);
|
||||
return lookupTable[gameAssetType];
|
||||
}
|
||||
|
||||
std::optional<asset_type_t> CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const
|
||||
{
|
||||
#define MAP_COMMON(common, game) \
|
||||
case common: \
|
||||
return game;
|
||||
|
||||
switch (commonAssetType)
|
||||
{
|
||||
MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET)
|
||||
MAP_COMMON(CommonAssetType::PHYS_COLL_MAP, ASSET_TYPE_PHYSCOLLMAP)
|
||||
MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS)
|
||||
MAP_COMMON(CommonAssetType::XMODEL_SURFS, ASSET_TYPE_XMODEL_SURFS)
|
||||
MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL)
|
||||
MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL)
|
||||
MAP_COMMON(CommonAssetType::PIXEL_SHADER, ASSET_TYPE_PIXELSHADER)
|
||||
MAP_COMMON(CommonAssetType::VERTEX_SHADER, ASSET_TYPE_VERTEXSHADER)
|
||||
MAP_COMMON(CommonAssetType::VERTEX_DECL, ASSET_TYPE_VERTEXDECL)
|
||||
MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET)
|
||||
MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE)
|
||||
MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND)
|
||||
MAP_COMMON(CommonAssetType::SOUND_CURVE, ASSET_TYPE_SOUND_CURVE)
|
||||
MAP_COMMON(CommonAssetType::LOADED_SOUND, ASSET_TYPE_LOADED_SOUND)
|
||||
MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_MP)
|
||||
MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP)
|
||||
MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS)
|
||||
MAP_COMMON(CommonAssetType::FX_WORLD, ASSET_TYPE_FXWORLD)
|
||||
MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD)
|
||||
MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF)
|
||||
MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP)
|
||||
MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT)
|
||||
MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST)
|
||||
MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU)
|
||||
MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY)
|
||||
MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON)
|
||||
MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS)
|
||||
MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX)
|
||||
MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX)
|
||||
MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE)
|
||||
MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE)
|
||||
MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER)
|
||||
MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS)
|
||||
MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE)
|
||||
MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE)
|
||||
MAP_COMMON(CommonAssetType::LEADERBOARD, ASSET_TYPE_LEADERBOARD)
|
||||
MAP_COMMON(CommonAssetType::STRUCTURED_DATA_DEF, ASSET_TYPE_STRUCTURED_DATA_DEF)
|
||||
MAP_COMMON(CommonAssetType::TRACER, ASSET_TYPE_TRACER)
|
||||
MAP_COMMON(CommonAssetType::VEHICLE, ASSET_TYPE_VEHICLE)
|
||||
MAP_COMMON(CommonAssetType::ADDON_MAP_ENTS, ASSET_TYPE_ADDON_MAP_ENTS)
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#undef MAP_COMMON
|
||||
}
|
||||
} // namespace IW4
|
||||
15
src/Common/Game/IW4/CommonAssetIW4.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/CommonAsset.h"
|
||||
|
||||
namespace IW4
|
||||
{
|
||||
class CommonAssetTypeMapper final : public ICommonAssetTypeMapper
|
||||
{
|
||||
public:
|
||||
CommonAssetTypeMapper();
|
||||
|
||||
[[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override;
|
||||
[[nodiscard]] std::optional<asset_type_t> CommonToGameAssetType(CommonAssetType commonAssetType) const override;
|
||||
};
|
||||
} // namespace IW4
|
||||
127
src/Common/Game/IW5/CommonAssetIW5.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "CommonAssetIW5.h"
|
||||
|
||||
#include "IW5.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace IW5
|
||||
{
|
||||
CommonAssetTypeMapper::CommonAssetTypeMapper() = default;
|
||||
|
||||
CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const
|
||||
{
|
||||
static CommonAssetType lookupTable[static_cast<unsigned>(ASSET_TYPE_COUNT)]{
|
||||
CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET
|
||||
CommonAssetType::PHYS_COLL_MAP, // ASSET_TYPE_PHYSCOLLMAP
|
||||
CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS
|
||||
CommonAssetType::XMODEL_SURFS, // ASSET_TYPE_XMODEL_SURFS
|
||||
CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL
|
||||
CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL
|
||||
CommonAssetType::PIXEL_SHADER, // ASSET_TYPE_PIXELSHADER
|
||||
CommonAssetType::VERTEX_SHADER, // ASSET_TYPE_VERTEXSHADER
|
||||
CommonAssetType::VERTEX_DECL, // ASSET_TYPE_VERTEXDECL
|
||||
CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET
|
||||
CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE
|
||||
CommonAssetType::SOUND, // ASSET_TYPE_SOUND
|
||||
CommonAssetType::SOUND_CURVE, // ASSET_TYPE_SOUND_CURVE
|
||||
CommonAssetType::LOADED_SOUND, // ASSET_TYPE_LOADED_SOUND
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP
|
||||
CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD
|
||||
CommonAssetType::GLASS_WORLD, // ASSET_TYPE_GLASSWORLD
|
||||
CommonAssetType::PATH_DATA, // ASSET_TYPE_PATHDATA
|
||||
CommonAssetType::VEHICLE_TRACK, // ASSET_TYPE_VEHICLE_TRACK
|
||||
CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS
|
||||
CommonAssetType::FX_WORLD, // ASSET_TYPE_FXWORLD
|
||||
CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD
|
||||
CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF
|
||||
CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP
|
||||
CommonAssetType::FONT, // ASSET_TYPE_FONT
|
||||
CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST
|
||||
CommonAssetType::MENU, // ASSET_TYPE_MENU
|
||||
CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY
|
||||
CommonAssetType::ATTACHMENT, // ASSET_TYPE_ATTACHMENT
|
||||
CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON
|
||||
CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS
|
||||
CommonAssetType::FX, // ASSET_TYPE_FX
|
||||
CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX
|
||||
CommonAssetType::SURFACE_FX, // ASSET_TYPE_SURFACE_FX
|
||||
CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE
|
||||
CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE
|
||||
CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER
|
||||
CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS
|
||||
CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE
|
||||
CommonAssetType::SCRIPT, // ASSET_TYPE_SCRIPTFILE
|
||||
CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE
|
||||
CommonAssetType::LEADERBOARD, // ASSET_TYPE_LEADERBOARD
|
||||
CommonAssetType::STRUCTURED_DATA_DEF, // ASSET_TYPE_STRUCTURED_DATA_DEF
|
||||
CommonAssetType::TRACER, // ASSET_TYPE_TRACER
|
||||
CommonAssetType::VEHICLE, // ASSET_TYPE_VEHICLE
|
||||
CommonAssetType::ADDON_MAP_ENTS, // ASSET_TYPE_ADDON_MAP_ENTS
|
||||
};
|
||||
|
||||
assert(gameAssetType < ASSET_TYPE_COUNT);
|
||||
return lookupTable[gameAssetType];
|
||||
}
|
||||
|
||||
std::optional<asset_type_t> CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const
|
||||
{
|
||||
#define MAP_COMMON(common, game) \
|
||||
case common: \
|
||||
return game;
|
||||
|
||||
switch (commonAssetType)
|
||||
{
|
||||
MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET)
|
||||
MAP_COMMON(CommonAssetType::PHYS_COLL_MAP, ASSET_TYPE_PHYSCOLLMAP)
|
||||
MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS)
|
||||
MAP_COMMON(CommonAssetType::XMODEL_SURFS, ASSET_TYPE_XMODEL_SURFS)
|
||||
MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL)
|
||||
MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL)
|
||||
MAP_COMMON(CommonAssetType::PIXEL_SHADER, ASSET_TYPE_PIXELSHADER)
|
||||
MAP_COMMON(CommonAssetType::VERTEX_SHADER, ASSET_TYPE_VERTEXSHADER)
|
||||
MAP_COMMON(CommonAssetType::VERTEX_DECL, ASSET_TYPE_VERTEXDECL)
|
||||
MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET)
|
||||
MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE)
|
||||
MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND)
|
||||
MAP_COMMON(CommonAssetType::SOUND_CURVE, ASSET_TYPE_SOUND_CURVE)
|
||||
MAP_COMMON(CommonAssetType::LOADED_SOUND, ASSET_TYPE_LOADED_SOUND)
|
||||
MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP)
|
||||
MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD)
|
||||
MAP_COMMON(CommonAssetType::GLASS_WORLD, ASSET_TYPE_GLASSWORLD)
|
||||
MAP_COMMON(CommonAssetType::PATH_DATA, ASSET_TYPE_PATHDATA)
|
||||
MAP_COMMON(CommonAssetType::VEHICLE_TRACK, ASSET_TYPE_VEHICLE_TRACK)
|
||||
MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS)
|
||||
MAP_COMMON(CommonAssetType::FX_WORLD, ASSET_TYPE_FXWORLD)
|
||||
MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD)
|
||||
MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF)
|
||||
MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP)
|
||||
MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT)
|
||||
MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST)
|
||||
MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU)
|
||||
MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY)
|
||||
MAP_COMMON(CommonAssetType::ATTACHMENT, ASSET_TYPE_ATTACHMENT)
|
||||
MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON)
|
||||
MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS)
|
||||
MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX)
|
||||
MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX)
|
||||
MAP_COMMON(CommonAssetType::SURFACE_FX, ASSET_TYPE_SURFACE_FX)
|
||||
MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE)
|
||||
MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE)
|
||||
MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER)
|
||||
MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS)
|
||||
MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE)
|
||||
MAP_COMMON(CommonAssetType::SCRIPT, ASSET_TYPE_SCRIPTFILE)
|
||||
MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE)
|
||||
MAP_COMMON(CommonAssetType::LEADERBOARD, ASSET_TYPE_LEADERBOARD)
|
||||
MAP_COMMON(CommonAssetType::STRUCTURED_DATA_DEF, ASSET_TYPE_STRUCTURED_DATA_DEF)
|
||||
MAP_COMMON(CommonAssetType::TRACER, ASSET_TYPE_TRACER)
|
||||
MAP_COMMON(CommonAssetType::VEHICLE, ASSET_TYPE_VEHICLE)
|
||||
MAP_COMMON(CommonAssetType::ADDON_MAP_ENTS, ASSET_TYPE_ADDON_MAP_ENTS)
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#undef MAP_COMMON
|
||||
}
|
||||
} // namespace IW5
|
||||
15
src/Common/Game/IW5/CommonAssetIW5.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/CommonAsset.h"
|
||||
|
||||
namespace IW5
|
||||
{
|
||||
class CommonAssetTypeMapper final : public ICommonAssetTypeMapper
|
||||
{
|
||||
public:
|
||||
CommonAssetTypeMapper();
|
||||
|
||||
[[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override;
|
||||
[[nodiscard]] std::optional<asset_type_t> CommonToGameAssetType(CommonAssetType commonAssetType) const override;
|
||||
};
|
||||
} // namespace IW5
|
||||
120
src/Common/Game/T5/CommonAssetT5.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "CommonAssetT5.h"
|
||||
|
||||
#include "T5.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace T5
|
||||
{
|
||||
CommonAssetTypeMapper::CommonAssetTypeMapper() = default;
|
||||
|
||||
CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const
|
||||
{
|
||||
static CommonAssetType lookupTable[static_cast<unsigned>(ASSET_TYPE_COUNT)]{
|
||||
CommonAssetType::XMODEL_PIECES, // ASSET_TYPE_XMODELPIECES
|
||||
CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET
|
||||
CommonAssetType::PHYS_CONSTRAINTS, // ASSET_TYPE_PHYSCONSTRAINTS
|
||||
CommonAssetType::DESTRUCTIBLE_DEF, // ASSET_TYPE_DESTRUCTIBLEDEF
|
||||
CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS
|
||||
CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL
|
||||
CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL
|
||||
CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET
|
||||
CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE
|
||||
CommonAssetType::SOUND, // ASSET_TYPE_SOUND
|
||||
CommonAssetType::SOUND_PATCH, // ASSET_TYPE_SOUND_PATCH
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_PVS
|
||||
CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD
|
||||
CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP
|
||||
CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP
|
||||
CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS
|
||||
CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD
|
||||
CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF
|
||||
CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP
|
||||
CommonAssetType::FONT, // ASSET_TYPE_FONT
|
||||
CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST
|
||||
CommonAssetType::MENU, // ASSET_TYPE_MENU
|
||||
CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY
|
||||
CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON
|
||||
CommonAssetType::WEAPON_DEF, // ASSET_TYPE_WEAPONDEF
|
||||
CommonAssetType::WEAPON_VARIANT, // ASSET_TYPE_WEAPON_VARIANT
|
||||
CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS
|
||||
CommonAssetType::FX, // ASSET_TYPE_FX
|
||||
CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX
|
||||
CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE
|
||||
CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE
|
||||
CommonAssetType::MP_BODY, // ASSET_TYPE_MPBODY
|
||||
CommonAssetType::MP_HEAD, // ASSET_TYPE_MPHEAD
|
||||
CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER
|
||||
CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS
|
||||
CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE
|
||||
CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE
|
||||
CommonAssetType::PACK_INDEX, // ASSET_TYPE_PACK_INDEX
|
||||
CommonAssetType::XGLOBALS, // ASSET_TYPE_XGLOBALS
|
||||
CommonAssetType::DDL, // ASSET_TYPE_DDL
|
||||
CommonAssetType::GLASSES, // ASSET_TYPE_GLASSES
|
||||
CommonAssetType::EMBLEM_SET, // ASSET_TYPE_EMBLEMSET
|
||||
};
|
||||
|
||||
assert(gameAssetType < ASSET_TYPE_COUNT);
|
||||
return lookupTable[gameAssetType];
|
||||
}
|
||||
|
||||
std::optional<asset_type_t> CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const
|
||||
{
|
||||
#define MAP_COMMON(common, game) \
|
||||
case common: \
|
||||
return game;
|
||||
|
||||
switch (commonAssetType)
|
||||
{
|
||||
MAP_COMMON(CommonAssetType::XMODEL_PIECES, ASSET_TYPE_XMODELPIECES)
|
||||
MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET)
|
||||
MAP_COMMON(CommonAssetType::PHYS_CONSTRAINTS, ASSET_TYPE_PHYSCONSTRAINTS)
|
||||
MAP_COMMON(CommonAssetType::DESTRUCTIBLE_DEF, ASSET_TYPE_DESTRUCTIBLEDEF)
|
||||
MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS)
|
||||
MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL)
|
||||
MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL)
|
||||
MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET)
|
||||
MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE)
|
||||
MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND)
|
||||
MAP_COMMON(CommonAssetType::SOUND_PATCH, ASSET_TYPE_SOUND_PATCH)
|
||||
MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_PVS)
|
||||
MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP)
|
||||
MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS)
|
||||
MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD)
|
||||
MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF)
|
||||
MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP)
|
||||
MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT)
|
||||
MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST)
|
||||
MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU)
|
||||
MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY)
|
||||
MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON)
|
||||
MAP_COMMON(CommonAssetType::WEAPON_DEF, ASSET_TYPE_WEAPONDEF)
|
||||
MAP_COMMON(CommonAssetType::WEAPON_VARIANT, ASSET_TYPE_WEAPON_VARIANT)
|
||||
MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS)
|
||||
MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX)
|
||||
MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX)
|
||||
MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE)
|
||||
MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE)
|
||||
MAP_COMMON(CommonAssetType::MP_BODY, ASSET_TYPE_MPBODY)
|
||||
MAP_COMMON(CommonAssetType::MP_HEAD, ASSET_TYPE_MPHEAD)
|
||||
MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER)
|
||||
MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS)
|
||||
MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE)
|
||||
MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE)
|
||||
MAP_COMMON(CommonAssetType::PACK_INDEX, ASSET_TYPE_PACK_INDEX)
|
||||
MAP_COMMON(CommonAssetType::XGLOBALS, ASSET_TYPE_XGLOBALS)
|
||||
MAP_COMMON(CommonAssetType::DDL, ASSET_TYPE_DDL)
|
||||
MAP_COMMON(CommonAssetType::GLASSES, ASSET_TYPE_GLASSES)
|
||||
MAP_COMMON(CommonAssetType::EMBLEM_SET, ASSET_TYPE_EMBLEMSET)
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#undef MAP_COMMON
|
||||
}
|
||||
} // namespace T5
|
||||
15
src/Common/Game/T5/CommonAssetT5.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/CommonAsset.h"
|
||||
|
||||
namespace T5
|
||||
{
|
||||
class CommonAssetTypeMapper final : public ICommonAssetTypeMapper
|
||||
{
|
||||
public:
|
||||
CommonAssetTypeMapper();
|
||||
|
||||
[[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override;
|
||||
[[nodiscard]] std::optional<asset_type_t> CommonToGameAssetType(CommonAssetType commonAssetType) const override;
|
||||
};
|
||||
} // namespace T5
|
||||
154
src/Common/Game/T6/CommonAssetT6.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
#include "CommonAssetT6.h"
|
||||
|
||||
#include "T6.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace T6
|
||||
{
|
||||
CommonAssetTypeMapper::CommonAssetTypeMapper() = default;
|
||||
|
||||
CommonAssetType CommonAssetTypeMapper::GameToCommonAssetType(const asset_type_t gameAssetType) const
|
||||
{
|
||||
static CommonAssetType lookupTable[static_cast<unsigned>(ASSET_TYPE_COUNT)]{
|
||||
CommonAssetType::XMODEL_PIECES, // ASSET_TYPE_XMODELPIECES
|
||||
CommonAssetType::PHYS_PRESET, // ASSET_TYPE_PHYSPRESET
|
||||
CommonAssetType::PHYS_CONSTRAINTS, // ASSET_TYPE_PHYSCONSTRAINTS
|
||||
CommonAssetType::DESTRUCTIBLE_DEF, // ASSET_TYPE_DESTRUCTIBLEDEF
|
||||
CommonAssetType::XANIM, // ASSET_TYPE_XANIMPARTS
|
||||
CommonAssetType::XMODEL, // ASSET_TYPE_XMODEL
|
||||
CommonAssetType::MATERIAL, // ASSET_TYPE_MATERIAL
|
||||
CommonAssetType::TECHNIQUE_SET, // ASSET_TYPE_TECHNIQUE_SET
|
||||
CommonAssetType::IMAGE, // ASSET_TYPE_IMAGE
|
||||
CommonAssetType::SOUND, // ASSET_TYPE_SOUND
|
||||
CommonAssetType::SOUND_PATCH, // ASSET_TYPE_SOUND_PATCH
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP
|
||||
CommonAssetType::CLIP_MAP, // ASSET_TYPE_CLIPMAP_PVS
|
||||
CommonAssetType::COM_WORLD, // ASSET_TYPE_COMWORLD
|
||||
CommonAssetType::GAME_WORLD_SP, // ASSET_TYPE_GAMEWORLD_SP
|
||||
CommonAssetType::GAME_WORLD_MP, // ASSET_TYPE_GAMEWORLD_MP
|
||||
CommonAssetType::MAP_ENTS, // ASSET_TYPE_MAP_ENTS
|
||||
CommonAssetType::GFX_WORLD, // ASSET_TYPE_GFXWORLD
|
||||
CommonAssetType::LIGHT_DEF, // ASSET_TYPE_LIGHT_DEF
|
||||
CommonAssetType::UI_MAP, // ASSET_TYPE_UI_MAP
|
||||
CommonAssetType::FONT, // ASSET_TYPE_FONT
|
||||
CommonAssetType::FONT_ICON, // ASSET_TYPE_FONTICON
|
||||
CommonAssetType::MENU_LIST, // ASSET_TYPE_MENULIST
|
||||
CommonAssetType::MENU, // ASSET_TYPE_MENU
|
||||
CommonAssetType::LOCALIZE_ENTRY, // ASSET_TYPE_LOCALIZE_ENTRY
|
||||
CommonAssetType::WEAPON, // ASSET_TYPE_WEAPON
|
||||
CommonAssetType::WEAPON_DEF, // ASSET_TYPE_WEAPONDEF
|
||||
CommonAssetType::WEAPON_VARIANT, // ASSET_TYPE_WEAPON_VARIANT
|
||||
CommonAssetType::WEAPON_FULL, // ASSET_TYPE_WEAPON_FULL
|
||||
CommonAssetType::ATTACHMENT, // ASSET_TYPE_ATTACHMENT
|
||||
CommonAssetType::ATTACHMENT_UNIQUE, // ASSET_TYPE_ATTACHMENT_UNIQUE
|
||||
CommonAssetType::WEAPON_CAMO, // ASSET_TYPE_WEAPON_CAMO
|
||||
CommonAssetType::SOUND_DRIVER_GLOBALS, // ASSET_TYPE_SNDDRIVER_GLOBALS
|
||||
CommonAssetType::FX, // ASSET_TYPE_FX
|
||||
CommonAssetType::IMPACT_FX, // ASSET_TYPE_IMPACT_FX
|
||||
CommonAssetType::AI_TYPE, // ASSET_TYPE_AITYPE
|
||||
CommonAssetType::MP_TYPE, // ASSET_TYPE_MPTYPE
|
||||
CommonAssetType::MP_BODY, // ASSET_TYPE_MPBODY
|
||||
CommonAssetType::MP_HEAD, // ASSET_TYPE_MPHEAD
|
||||
CommonAssetType::CHARACTER, // ASSET_TYPE_CHARACTER
|
||||
CommonAssetType::XMODEL_ALIAS, // ASSET_TYPE_XMODELALIAS
|
||||
CommonAssetType::RAW_FILE, // ASSET_TYPE_RAWFILE
|
||||
CommonAssetType::STRING_TABLE, // ASSET_TYPE_STRINGTABLE
|
||||
CommonAssetType::LEADERBOARD, // ASSET_TYPE_LEADERBOARD
|
||||
CommonAssetType::XGLOBALS, // ASSET_TYPE_XGLOBALS
|
||||
CommonAssetType::DDL, // ASSET_TYPE_DDL
|
||||
CommonAssetType::GLASSES, // ASSET_TYPE_GLASSES
|
||||
CommonAssetType::EMBLEM_SET, // ASSET_TYPE_EMBLEMSET
|
||||
CommonAssetType::SCRIPT, // ASSET_TYPE_SCRIPTPARSETREE
|
||||
CommonAssetType::KEY_VALUE_PAIRS, // ASSET_TYPE_KEYVALUEPAIRS
|
||||
CommonAssetType::VEHICLE, // ASSET_TYPE_VEHICLEDEF
|
||||
CommonAssetType::MEMORY_BLOCK, // ASSET_TYPE_MEMORYBLOCK
|
||||
CommonAssetType::ADDON_MAP_ENTS, // ASSET_TYPE_ADDON_MAP_ENTS
|
||||
CommonAssetType::TRACER, // ASSET_TYPE_TRACER
|
||||
CommonAssetType::SKINNED_VERTS, // ASSET_TYPE_SKINNEDVERTS
|
||||
CommonAssetType::QDB, // ASSET_TYPE_QDB
|
||||
CommonAssetType::SLUG, // ASSET_TYPE_SLUG
|
||||
CommonAssetType::FOOTSTEP_TABLE, // ASSET_TYPE_FOOTSTEP_TABLE
|
||||
CommonAssetType::FOOTSTEP_FX_TABLE, // ASSET_TYPE_FOOTSTEPFX_TABLE
|
||||
CommonAssetType::ZBARRIER, // ASSET_TYPE_ZBARRIER
|
||||
};
|
||||
|
||||
assert(gameAssetType < ASSET_TYPE_COUNT);
|
||||
return lookupTable[gameAssetType];
|
||||
}
|
||||
|
||||
std::optional<asset_type_t> CommonAssetTypeMapper::CommonToGameAssetType(const CommonAssetType commonAssetType) const
|
||||
{
|
||||
#define MAP_COMMON(common, game) \
|
||||
case common: \
|
||||
return game;
|
||||
|
||||
switch (commonAssetType)
|
||||
{
|
||||
MAP_COMMON(CommonAssetType::XMODEL_PIECES, ASSET_TYPE_XMODELPIECES)
|
||||
MAP_COMMON(CommonAssetType::PHYS_PRESET, ASSET_TYPE_PHYSPRESET)
|
||||
MAP_COMMON(CommonAssetType::PHYS_CONSTRAINTS, ASSET_TYPE_PHYSCONSTRAINTS)
|
||||
MAP_COMMON(CommonAssetType::DESTRUCTIBLE_DEF, ASSET_TYPE_DESTRUCTIBLEDEF)
|
||||
MAP_COMMON(CommonAssetType::XANIM, ASSET_TYPE_XANIMPARTS)
|
||||
MAP_COMMON(CommonAssetType::XMODEL, ASSET_TYPE_XMODEL)
|
||||
MAP_COMMON(CommonAssetType::MATERIAL, ASSET_TYPE_MATERIAL)
|
||||
MAP_COMMON(CommonAssetType::TECHNIQUE_SET, ASSET_TYPE_TECHNIQUE_SET)
|
||||
MAP_COMMON(CommonAssetType::IMAGE, ASSET_TYPE_IMAGE)
|
||||
MAP_COMMON(CommonAssetType::SOUND, ASSET_TYPE_SOUND)
|
||||
MAP_COMMON(CommonAssetType::SOUND_PATCH, ASSET_TYPE_SOUND_PATCH)
|
||||
MAP_COMMON(CommonAssetType::CLIP_MAP, ASSET_TYPE_CLIPMAP_PVS)
|
||||
MAP_COMMON(CommonAssetType::COM_WORLD, ASSET_TYPE_COMWORLD)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_SP, ASSET_TYPE_GAMEWORLD_SP)
|
||||
MAP_COMMON(CommonAssetType::GAME_WORLD_MP, ASSET_TYPE_GAMEWORLD_MP)
|
||||
MAP_COMMON(CommonAssetType::MAP_ENTS, ASSET_TYPE_MAP_ENTS)
|
||||
MAP_COMMON(CommonAssetType::GFX_WORLD, ASSET_TYPE_GFXWORLD)
|
||||
MAP_COMMON(CommonAssetType::LIGHT_DEF, ASSET_TYPE_LIGHT_DEF)
|
||||
MAP_COMMON(CommonAssetType::UI_MAP, ASSET_TYPE_UI_MAP)
|
||||
MAP_COMMON(CommonAssetType::FONT, ASSET_TYPE_FONT)
|
||||
MAP_COMMON(CommonAssetType::FONT_ICON, ASSET_TYPE_FONTICON)
|
||||
MAP_COMMON(CommonAssetType::MENU_LIST, ASSET_TYPE_MENULIST)
|
||||
MAP_COMMON(CommonAssetType::MENU, ASSET_TYPE_MENU)
|
||||
MAP_COMMON(CommonAssetType::LOCALIZE_ENTRY, ASSET_TYPE_LOCALIZE_ENTRY)
|
||||
MAP_COMMON(CommonAssetType::WEAPON, ASSET_TYPE_WEAPON)
|
||||
MAP_COMMON(CommonAssetType::WEAPON_DEF, ASSET_TYPE_WEAPONDEF)
|
||||
MAP_COMMON(CommonAssetType::WEAPON_VARIANT, ASSET_TYPE_WEAPON_VARIANT)
|
||||
MAP_COMMON(CommonAssetType::WEAPON_FULL, ASSET_TYPE_WEAPON_FULL)
|
||||
MAP_COMMON(CommonAssetType::ATTACHMENT, ASSET_TYPE_ATTACHMENT)
|
||||
MAP_COMMON(CommonAssetType::ATTACHMENT_UNIQUE, ASSET_TYPE_ATTACHMENT_UNIQUE)
|
||||
MAP_COMMON(CommonAssetType::WEAPON_CAMO, ASSET_TYPE_WEAPON_CAMO)
|
||||
MAP_COMMON(CommonAssetType::SOUND_DRIVER_GLOBALS, ASSET_TYPE_SNDDRIVER_GLOBALS)
|
||||
MAP_COMMON(CommonAssetType::FX, ASSET_TYPE_FX)
|
||||
MAP_COMMON(CommonAssetType::IMPACT_FX, ASSET_TYPE_IMPACT_FX)
|
||||
MAP_COMMON(CommonAssetType::AI_TYPE, ASSET_TYPE_AITYPE)
|
||||
MAP_COMMON(CommonAssetType::MP_TYPE, ASSET_TYPE_MPTYPE)
|
||||
MAP_COMMON(CommonAssetType::MP_BODY, ASSET_TYPE_MPBODY)
|
||||
MAP_COMMON(CommonAssetType::MP_HEAD, ASSET_TYPE_MPHEAD)
|
||||
MAP_COMMON(CommonAssetType::CHARACTER, ASSET_TYPE_CHARACTER)
|
||||
MAP_COMMON(CommonAssetType::XMODEL_ALIAS, ASSET_TYPE_XMODELALIAS)
|
||||
MAP_COMMON(CommonAssetType::RAW_FILE, ASSET_TYPE_RAWFILE)
|
||||
MAP_COMMON(CommonAssetType::STRING_TABLE, ASSET_TYPE_STRINGTABLE)
|
||||
MAP_COMMON(CommonAssetType::LEADERBOARD, ASSET_TYPE_LEADERBOARD)
|
||||
MAP_COMMON(CommonAssetType::XGLOBALS, ASSET_TYPE_XGLOBALS)
|
||||
MAP_COMMON(CommonAssetType::DDL, ASSET_TYPE_DDL)
|
||||
MAP_COMMON(CommonAssetType::GLASSES, ASSET_TYPE_GLASSES)
|
||||
MAP_COMMON(CommonAssetType::EMBLEM_SET, ASSET_TYPE_EMBLEMSET)
|
||||
MAP_COMMON(CommonAssetType::SCRIPT, ASSET_TYPE_SCRIPTPARSETREE)
|
||||
MAP_COMMON(CommonAssetType::KEY_VALUE_PAIRS, ASSET_TYPE_KEYVALUEPAIRS)
|
||||
MAP_COMMON(CommonAssetType::VEHICLE, ASSET_TYPE_VEHICLEDEF)
|
||||
MAP_COMMON(CommonAssetType::MEMORY_BLOCK, ASSET_TYPE_MEMORYBLOCK)
|
||||
MAP_COMMON(CommonAssetType::ADDON_MAP_ENTS, ASSET_TYPE_ADDON_MAP_ENTS)
|
||||
MAP_COMMON(CommonAssetType::TRACER, ASSET_TYPE_TRACER)
|
||||
MAP_COMMON(CommonAssetType::SKINNED_VERTS, ASSET_TYPE_SKINNEDVERTS)
|
||||
MAP_COMMON(CommonAssetType::QDB, ASSET_TYPE_QDB)
|
||||
MAP_COMMON(CommonAssetType::SLUG, ASSET_TYPE_SLUG)
|
||||
MAP_COMMON(CommonAssetType::FOOTSTEP_TABLE, ASSET_TYPE_FOOTSTEP_TABLE)
|
||||
MAP_COMMON(CommonAssetType::FOOTSTEP_FX_TABLE, ASSET_TYPE_FOOTSTEPFX_TABLE)
|
||||
MAP_COMMON(CommonAssetType::ZBARRIER, ASSET_TYPE_ZBARRIER)
|
||||
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#undef MAP_COMMON
|
||||
}
|
||||
} // namespace T6
|
||||
15
src/Common/Game/T6/CommonAssetT6.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/CommonAsset.h"
|
||||
|
||||
namespace T6
|
||||
{
|
||||
class CommonAssetTypeMapper final : public ICommonAssetTypeMapper
|
||||
{
|
||||
public:
|
||||
CommonAssetTypeMapper();
|
||||
|
||||
[[nodiscard]] CommonAssetType GameToCommonAssetType(asset_type_t gameAssetType) const override;
|
||||
[[nodiscard]] std::optional<asset_type_t> CommonToGameAssetType(CommonAssetType commonAssetType) const override;
|
||||
};
|
||||
} // namespace T6
|
||||
12
src/Common/Utils/ProgressCallback.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
class ProgressCallback
|
||||
{
|
||||
public:
|
||||
ProgressCallback() = default;
|
||||
virtual ~ProgressCallback() = default;
|
||||
|
||||
virtual void OnProgress(size_t current, size_t total) = 0;
|
||||
};
|
||||
@@ -363,14 +363,16 @@ class LinkerImpl final : public Linker
|
||||
zoneDirectory = fs::current_path();
|
||||
auto absoluteZoneDirectory = absolute(zoneDirectory).string();
|
||||
|
||||
auto zone = ZoneLoading::LoadZone(zonePath);
|
||||
if (!zone)
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\".", zonePath);
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
con::debug("Load zone \"{}\"", zone->m_name);
|
||||
auto zone = std::move(*maybeZone);
|
||||
|
||||
con::debug("Loaded zone \"{}\"", zone->m_name);
|
||||
|
||||
m_loaded_zones.emplace_back(std::move(zone));
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace
|
||||
{
|
||||
std::unique_ptr<Zone> CreateZone(const ZoneCreationContext& context, const GameId gameId)
|
||||
{
|
||||
return std::make_unique<Zone>(context.m_definition->m_name, 0, gameId);
|
||||
return std::make_unique<Zone>(context.m_definition->m_name, 0, gameId, GamePlatform::PC);
|
||||
}
|
||||
|
||||
std::vector<Gdt*> CreateGdtList(const ZoneCreationContext& context)
|
||||
|
||||
@@ -36,6 +36,12 @@ function ModMan:project()
|
||||
path.join(folder, "ModMan/**.cpp")
|
||||
}
|
||||
|
||||
filter { "system:windows" }
|
||||
files {
|
||||
path.join(folder, "ModMan/**.rc")
|
||||
}
|
||||
filter {}
|
||||
|
||||
includedirs {
|
||||
"%{wks.location}/src/ModMan"
|
||||
}
|
||||
@@ -47,10 +53,16 @@ function ModMan:project()
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
ZoneLoading:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
ObjWriting:include(includes)
|
||||
json:include(includes)
|
||||
webview:include(includes)
|
||||
|
||||
links:linkto(Utils)
|
||||
links:linkto(ZoneLoading)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(ObjWriting)
|
||||
links:linkto(webview)
|
||||
links:linkall()
|
||||
end
|
||||
|
||||
91
src/ModMan/Context/FastFileContext.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "FastFileContext.h"
|
||||
|
||||
#include "Web/Binds/ZoneBinds.h"
|
||||
#include "Web/UiCommunication.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr double MIN_PROGRESS_TO_REPORT = 0.5;
|
||||
|
||||
class LoadingEventProgressReporter : public ProgressCallback
|
||||
{
|
||||
public:
|
||||
explicit LoadingEventProgressReporter(std::string zoneName)
|
||||
: m_zone_name(std::move(zoneName)),
|
||||
m_last_progress(0)
|
||||
{
|
||||
}
|
||||
|
||||
void OnProgress(const size_t current, const size_t total) override
|
||||
{
|
||||
const double percentage = static_cast<double>(current) / static_cast<double>(total) * 100.0;
|
||||
|
||||
if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT)
|
||||
{
|
||||
m_last_progress = percentage;
|
||||
ui::NotifyZoneLoadProgress(m_zone_name, percentage);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_zone_name;
|
||||
double m_last_progress;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
LoadedZone::LoadedZone(std::unique_ptr<Zone> zone, std::string filePath)
|
||||
: m_zone(std::move(zone)),
|
||||
m_file_path(std::move(filePath))
|
||||
{
|
||||
}
|
||||
|
||||
void FastFileContext::Destroy()
|
||||
{
|
||||
// Unload all zones
|
||||
m_loaded_zones.clear();
|
||||
}
|
||||
|
||||
result::Expected<LoadedZone*, std::string> FastFileContext::LoadFastFile(const std::string& path)
|
||||
{
|
||||
auto zone = ZoneLoading::LoadZone(path, std::make_unique<LoadingEventProgressReporter>(fs::path(path).filename().replace_extension().string()));
|
||||
if (!zone)
|
||||
return result::Unexpected(std::move(zone.error()));
|
||||
|
||||
auto loadedZone = std::make_unique<LoadedZone>(std::move(*zone), path);
|
||||
|
||||
LoadedZone* result;
|
||||
{
|
||||
std::lock_guard lock(m_zone_lock);
|
||||
result = m_loaded_zones.emplace_back(std::move(loadedZone)).get();
|
||||
}
|
||||
|
||||
ui::NotifyZoneLoaded(*result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
result::Expected<NoResult, std::string> FastFileContext::UnloadZone(const std::string& zoneName)
|
||||
{
|
||||
{
|
||||
std::lock_guard lock(m_zone_lock);
|
||||
const auto existingZone = std::ranges::find_if(m_loaded_zones,
|
||||
[&zoneName](const std::unique_ptr<LoadedZone>& loadedZone)
|
||||
{
|
||||
return loadedZone->m_zone->m_name == zoneName;
|
||||
});
|
||||
|
||||
if (existingZone != m_loaded_zones.end())
|
||||
{
|
||||
m_loaded_zones.erase(existingZone);
|
||||
ui::NotifyZoneUnloaded(zoneName);
|
||||
return NoResult();
|
||||
}
|
||||
}
|
||||
|
||||
return result::Unexpected(std::format("No zone with name {} loaded", zoneName));
|
||||
}
|
||||
29
src/ModMan/Context/FastFileContext.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/Result.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <vector>
|
||||
|
||||
class LoadedZone
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<Zone> m_zone;
|
||||
std::string m_file_path;
|
||||
|
||||
LoadedZone(std::unique_ptr<Zone> zone, std::string filePath);
|
||||
};
|
||||
|
||||
class FastFileContext
|
||||
{
|
||||
public:
|
||||
void Destroy();
|
||||
|
||||
result::Expected<LoadedZone*, std::string> LoadFastFile(const std::string& path);
|
||||
result::Expected<NoResult, std::string> UnloadZone(const std::string& zoneName);
|
||||
|
||||
std::vector<std::unique_ptr<LoadedZone>> m_loaded_zones;
|
||||
std::shared_mutex m_zone_lock;
|
||||
};
|
||||
18
src/ModMan/Context/ModManContext.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "ModManContext.h"
|
||||
|
||||
ModManContext& ModManContext::Get()
|
||||
{
|
||||
static ModManContext context;
|
||||
return context;
|
||||
}
|
||||
|
||||
void ModManContext::Startup()
|
||||
{
|
||||
m_db_thread.Start();
|
||||
}
|
||||
|
||||
void ModManContext::Destroy()
|
||||
{
|
||||
m_fast_file.Destroy();
|
||||
m_db_thread.Terminate();
|
||||
}
|
||||
22
src/ModMan/Context/ModManContext.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "FastFileContext.h"
|
||||
#include "Utils/DispatchableThread.h"
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class ModManContext
|
||||
{
|
||||
public:
|
||||
static ModManContext& Get();
|
||||
|
||||
void Startup();
|
||||
void Destroy();
|
||||
|
||||
std::unique_ptr<webview::webview> m_main_webview;
|
||||
std::unique_ptr<webview::webview> m_dev_tools_webview;
|
||||
FastFileContext m_fast_file;
|
||||
|
||||
DispatchableThread m_db_thread;
|
||||
};
|
||||
BIN
src/ModMan/ModMan.rc
Normal file
105
src/ModMan/ModManArgs.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
#include "ModManArgs.h"
|
||||
|
||||
#include "GitVersion.h"
|
||||
#include "Utils/Arguments/UsageInformation.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
|
||||
#include <format>
|
||||
#include <type_traits>
|
||||
|
||||
namespace
|
||||
{
|
||||
// clang-format off
|
||||
const CommandLineOption* const OPTION_HELP =
|
||||
CommandLineOption::Builder::Create()
|
||||
.WithShortName("?")
|
||||
.WithLongName("help")
|
||||
.WithDescription("Displays usage information.")
|
||||
.Build();
|
||||
|
||||
const CommandLineOption* const OPTION_VERSION =
|
||||
CommandLineOption::Builder::Create()
|
||||
.WithLongName("version")
|
||||
.WithDescription("Prints the application version.")
|
||||
.Build();
|
||||
|
||||
const CommandLineOption* const OPTION_VERBOSE =
|
||||
CommandLineOption::Builder::Create()
|
||||
.WithShortName("v")
|
||||
.WithLongName("verbose")
|
||||
.WithDescription("Outputs a lot more and more detailed messages.")
|
||||
.Build();
|
||||
|
||||
const CommandLineOption* const OPTION_NO_COLOR =
|
||||
CommandLineOption::Builder::Create()
|
||||
.WithLongName("no-color")
|
||||
.WithDescription("Disables colored terminal output.")
|
||||
.Build();
|
||||
// clang-format on
|
||||
|
||||
const CommandLineOption* const COMMAND_LINE_OPTIONS[]{
|
||||
OPTION_HELP,
|
||||
OPTION_VERSION,
|
||||
OPTION_VERBOSE,
|
||||
OPTION_NO_COLOR,
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ModManArgs::ModManArgs()
|
||||
: m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v<decltype(COMMAND_LINE_OPTIONS)>)
|
||||
{
|
||||
}
|
||||
|
||||
void ModManArgs::PrintUsage() const
|
||||
{
|
||||
UsageInformation usage(m_argument_parser.GetExecutableName());
|
||||
|
||||
for (const auto* commandLineOption : COMMAND_LINE_OPTIONS)
|
||||
{
|
||||
usage.AddCommandLineOption(commandLineOption);
|
||||
}
|
||||
|
||||
usage.Print();
|
||||
}
|
||||
|
||||
void ModManArgs::PrintVersion()
|
||||
{
|
||||
con::info("OpenAssetTools ModMan {}", GIT_VERSION);
|
||||
}
|
||||
|
||||
bool ModManArgs::ParseArgs(const int argc, const char** argv, bool& shouldContinue)
|
||||
{
|
||||
shouldContinue = true;
|
||||
if (!m_argument_parser.ParseArguments(argc, argv))
|
||||
{
|
||||
PrintUsage();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the user requested help
|
||||
if (m_argument_parser.IsOptionSpecified(OPTION_HELP))
|
||||
{
|
||||
PrintUsage();
|
||||
shouldContinue = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the user wants to see the version
|
||||
if (m_argument_parser.IsOptionSpecified(OPTION_VERSION))
|
||||
{
|
||||
PrintVersion();
|
||||
shouldContinue = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -v; --verbose
|
||||
if (m_argument_parser.IsOptionSpecified(OPTION_VERBOSE))
|
||||
con::globalLogLevel = con::LogLevel::DEBUG;
|
||||
else
|
||||
con::globalLogLevel = con::LogLevel::INFO;
|
||||
|
||||
// --no-color
|
||||
con::globalUseColor = !m_argument_parser.IsOptionSpecified(OPTION_NO_COLOR);
|
||||
|
||||
return true;
|
||||
}
|
||||
19
src/ModMan/ModManArgs.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/Arguments/ArgumentParser.h"
|
||||
|
||||
class ModManArgs
|
||||
{
|
||||
public:
|
||||
ModManArgs();
|
||||
bool ParseArgs(int argc, const char** argv, bool& shouldContinue);
|
||||
|
||||
private:
|
||||
/**
|
||||
* \brief Prints a command line usage help text for the ModMan tool to stdout.
|
||||
*/
|
||||
void PrintUsage() const;
|
||||
static void PrintVersion();
|
||||
|
||||
ArgumentParser m_argument_parser;
|
||||
};
|
||||
77
src/ModMan/Utils/DispatchableThread.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "DispatchableThread.h"
|
||||
|
||||
DispatchableThread::DispatchableThread()
|
||||
: m_terminate(false)
|
||||
{
|
||||
}
|
||||
|
||||
DispatchableThread::~DispatchableThread()
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void DispatchableThread::Start()
|
||||
{
|
||||
m_terminate = false;
|
||||
m_thread = std::thread(
|
||||
[&]
|
||||
{
|
||||
ThreadLoop();
|
||||
});
|
||||
}
|
||||
|
||||
void DispatchableThread::Terminate()
|
||||
{
|
||||
std::unique_lock lock(m_cb_mutex);
|
||||
|
||||
if (!m_terminate)
|
||||
{
|
||||
m_terminate = true;
|
||||
m_cv.notify_all();
|
||||
lock.unlock();
|
||||
m_thread.join();
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void DispatchableThread::Dispatch(cb_t cb)
|
||||
{
|
||||
std::lock_guard lock(m_cb_mutex);
|
||||
|
||||
m_cb_list.emplace_back(std::move(cb));
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
std::optional<DispatchableThread::cb_t> DispatchableThread::NextCallback()
|
||||
{
|
||||
if (m_terminate || m_cb_list.empty())
|
||||
return std::nullopt;
|
||||
|
||||
auto cb = std::move(m_cb_list.front());
|
||||
m_cb_list.pop_front();
|
||||
|
||||
return cb;
|
||||
}
|
||||
|
||||
void DispatchableThread::ThreadLoop()
|
||||
{
|
||||
while (!m_terminate)
|
||||
{
|
||||
std::unique_lock lock(m_cb_mutex);
|
||||
m_cv.wait(lock,
|
||||
[&]
|
||||
{
|
||||
return !m_cb_list.empty() || m_terminate;
|
||||
});
|
||||
|
||||
auto maybeCb = NextCallback();
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (maybeCb)
|
||||
(*maybeCb)();
|
||||
}
|
||||
}
|
||||
37
src/ModMan/Utils/DispatchableThread.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
class DispatchableThread
|
||||
{
|
||||
public:
|
||||
using cb_t = std::function<void()>;
|
||||
|
||||
DispatchableThread();
|
||||
~DispatchableThread();
|
||||
DispatchableThread(const DispatchableThread& other) = delete;
|
||||
DispatchableThread(DispatchableThread&& other) noexcept = default;
|
||||
DispatchableThread& operator=(const DispatchableThread& other) = delete;
|
||||
DispatchableThread& operator=(DispatchableThread&& other) noexcept = default;
|
||||
|
||||
void Start();
|
||||
void Terminate();
|
||||
|
||||
void Dispatch(cb_t cb);
|
||||
|
||||
private:
|
||||
std::optional<cb_t> NextCallback();
|
||||
void ThreadLoop();
|
||||
|
||||
std::mutex m_cb_mutex;
|
||||
std::deque<cb_t> m_cb_list;
|
||||
|
||||
std::condition_variable m_cv;
|
||||
std::thread m_thread;
|
||||
bool m_terminate;
|
||||
};
|
||||
174
src/ModMan/Web/Binds/AssetBinds.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
#include "AssetBinds.h"
|
||||
|
||||
#include "Context/ModManContext.h"
|
||||
#include "Game/CommonAsset.h"
|
||||
#include "Web/UiCommunication.h"
|
||||
|
||||
#include "Json/JsonExtension.h"
|
||||
#include <cstdint>
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(CommonAssetType,
|
||||
{
|
||||
{CommonAssetType::PHYS_PRESET, "PHYS_PRESET" },
|
||||
{CommonAssetType::XANIM, "XANIM" },
|
||||
{CommonAssetType::XMODEL, "XMODEL" },
|
||||
{CommonAssetType::MATERIAL, "MATERIAL" },
|
||||
{CommonAssetType::TECHNIQUE_SET, "TECHNIQUE_SET" },
|
||||
{CommonAssetType::IMAGE, "IMAGE" },
|
||||
{CommonAssetType::SOUND, "SOUND" },
|
||||
{CommonAssetType::SOUND_CURVE, "SOUND_CURVE" },
|
||||
{CommonAssetType::LOADED_SOUND, "LOADED_SOUND" },
|
||||
{CommonAssetType::CLIP_MAP, "CLIP_MAP" },
|
||||
{CommonAssetType::COM_WORLD, "COM_WORLD" },
|
||||
{CommonAssetType::GAME_WORLD_SP, "GAME_WORLD_SP" },
|
||||
{CommonAssetType::GAME_WORLD_MP, "GAME_WORLD_MP" },
|
||||
{CommonAssetType::MAP_ENTS, "MAP_ENTS" },
|
||||
{CommonAssetType::GFX_WORLD, "GFX_WORLD" },
|
||||
{CommonAssetType::LIGHT_DEF, "LIGHT_DEF" },
|
||||
{CommonAssetType::UI_MAP, "UI_MAP" },
|
||||
{CommonAssetType::FONT, "FONT" },
|
||||
{CommonAssetType::MENU_LIST, "MENU_LIST" },
|
||||
{CommonAssetType::MENU, "MENU" },
|
||||
{CommonAssetType::LOCALIZE_ENTRY, "LOCALIZE_ENTRY" },
|
||||
{CommonAssetType::WEAPON, "WEAPON" },
|
||||
{CommonAssetType::SOUND_DRIVER_GLOBALS, "SOUND_DRIVER_GLOBALS"},
|
||||
{CommonAssetType::FX, "FX" },
|
||||
{CommonAssetType::IMPACT_FX, "IMPACT_FX" },
|
||||
{CommonAssetType::AI_TYPE, "AI_TYPE" },
|
||||
{CommonAssetType::MP_TYPE, "MP_TYPE" },
|
||||
{CommonAssetType::CHARACTER, "CHARACTER" },
|
||||
{CommonAssetType::XMODEL_ALIAS, "XMODEL_ALIAS" },
|
||||
{CommonAssetType::RAW_FILE, "RAW_FILE" },
|
||||
{CommonAssetType::STRING_TABLE, "STRING_TABLE" },
|
||||
{CommonAssetType::XMODEL_PIECES, "XMODEL_PIECES" },
|
||||
{CommonAssetType::PHYS_COLL_MAP, "PHYS_COLL_MAP" },
|
||||
{CommonAssetType::XMODEL_SURFS, "XMODEL_SURFS" },
|
||||
{CommonAssetType::PIXEL_SHADER, "PIXEL_SHADER" },
|
||||
{CommonAssetType::VERTEX_SHADER, "VERTEX_SHADER" },
|
||||
{CommonAssetType::VERTEX_DECL, "VERTEX_DECL" },
|
||||
{CommonAssetType::FX_WORLD, "FX_WORLD" },
|
||||
{CommonAssetType::LEADERBOARD, "LEADERBOARD" },
|
||||
{CommonAssetType::STRUCTURED_DATA_DEF, "STRUCTURED_DATA_DEF" },
|
||||
{CommonAssetType::TRACER, "TRACER" },
|
||||
{CommonAssetType::VEHICLE, "VEHICLE" },
|
||||
{CommonAssetType::ADDON_MAP_ENTS, "ADDON_MAP_ENTS" },
|
||||
{CommonAssetType::GLASS_WORLD, "GLASS_WORLD" },
|
||||
{CommonAssetType::PATH_DATA, "PATH_DATA" },
|
||||
{CommonAssetType::VEHICLE_TRACK, "VEHICLE_TRACK" },
|
||||
{CommonAssetType::ATTACHMENT, "ATTACHMENT" },
|
||||
{CommonAssetType::SURFACE_FX, "SURFACE_FX" },
|
||||
{CommonAssetType::SCRIPT, "SCRIPT" },
|
||||
{CommonAssetType::PHYS_CONSTRAINTS, "PHYS_CONSTRAINTS" },
|
||||
{CommonAssetType::DESTRUCTIBLE_DEF, "DESTRUCTIBLE_DEF" },
|
||||
{CommonAssetType::SOUND_PATCH, "SOUND_PATCH" },
|
||||
{CommonAssetType::WEAPON_DEF, "WEAPON_DEF" },
|
||||
{CommonAssetType::WEAPON_VARIANT, "WEAPON_VARIANT" },
|
||||
{CommonAssetType::MP_BODY, "MP_BODY" },
|
||||
{CommonAssetType::MP_HEAD, "MP_HEAD" },
|
||||
{CommonAssetType::PACK_INDEX, "PACK_INDEX" },
|
||||
{CommonAssetType::XGLOBALS, "XGLOBALS" },
|
||||
{CommonAssetType::DDL, "DDL" },
|
||||
{CommonAssetType::GLASSES, "GLASSES" },
|
||||
{CommonAssetType::EMBLEM_SET, "EMBLEM_SET" },
|
||||
{CommonAssetType::FONT_ICON, "FONT_ICON" },
|
||||
{CommonAssetType::WEAPON_FULL, "WEAPON_FULL" },
|
||||
{CommonAssetType::ATTACHMENT_UNIQUE, "ATTACHMENT_UNIQUE" },
|
||||
{CommonAssetType::WEAPON_CAMO, "WEAPON_CAMO" },
|
||||
{CommonAssetType::KEY_VALUE_PAIRS, "KEY_VALUE_PAIRS" },
|
||||
{CommonAssetType::MEMORY_BLOCK, "MEMORY_BLOCK" },
|
||||
{CommonAssetType::SKINNED_VERTS, "SKINNED_VERTS" },
|
||||
{CommonAssetType::QDB, "QDB" },
|
||||
{CommonAssetType::SLUG, "SLUG" },
|
||||
{CommonAssetType::FOOTSTEP_TABLE, "FOOTSTEP_TABLE" },
|
||||
{CommonAssetType::FOOTSTEP_FX_TABLE, "FOOTSTEP_FX_TABLE" },
|
||||
{CommonAssetType::ZBARRIER, "ZBARRIER" },
|
||||
});
|
||||
|
||||
namespace
|
||||
{
|
||||
class AssetDto
|
||||
{
|
||||
public:
|
||||
CommonAssetType type;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(AssetDto, type, name);
|
||||
|
||||
class ZoneAssetsDto
|
||||
{
|
||||
public:
|
||||
std::vector<AssetDto> assets;
|
||||
std::vector<AssetDto> references;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneAssetsDto, assets, references);
|
||||
|
||||
ZoneAssetsDto CreateZoneAssetsDto(const Zone& zone)
|
||||
{
|
||||
std::vector<AssetDto> assets;
|
||||
std::vector<AssetDto> references;
|
||||
|
||||
// Reserve some entries already. Numbers are arbitrary.
|
||||
const auto assetCount = zone.m_pools->GetTotalAssetCount();
|
||||
assets.reserve(assetCount / 2);
|
||||
references.reserve(assetCount / 8);
|
||||
|
||||
const auto& assetTypeMapper = *ICommonAssetTypeMapper::GetCommonAssetMapperByGame(zone.m_game_id);
|
||||
for (const auto& asset : *zone.m_pools)
|
||||
{
|
||||
if (asset->IsReference())
|
||||
{
|
||||
references.emplace_back(AssetDto{
|
||||
.type = assetTypeMapper.GameToCommonAssetType(asset->m_type),
|
||||
.name = asset->ReferencedAssetName(),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
assets.emplace_back(AssetDto{
|
||||
.type = assetTypeMapper.GameToCommonAssetType(asset->m_type),
|
||||
.name = asset->m_name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ZoneAssetsDto{
|
||||
.assets = std::move(assets),
|
||||
.references = std::move(references),
|
||||
};
|
||||
}
|
||||
|
||||
std::optional<ZoneAssetsDto> GetZonesForZone(const std::string& zoneName)
|
||||
{
|
||||
auto& context = ModManContext::Get().m_fast_file;
|
||||
|
||||
ZoneAssetsDto result;
|
||||
|
||||
{
|
||||
std::shared_lock lock(context.m_zone_lock);
|
||||
|
||||
for (const auto& loadedZone : context.m_loaded_zones)
|
||||
{
|
||||
const auto& zone = *loadedZone->m_zone;
|
||||
if (zone.m_name == zoneName)
|
||||
return CreateZoneAssetsDto(zone);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void RegisterAssetBinds(webview::webview& wv)
|
||||
{
|
||||
Bind<std::string, std::optional<ZoneAssetsDto>>(wv,
|
||||
"getAssetsForZone",
|
||||
[](const std::string& zoneName)
|
||||
{
|
||||
return GetZonesForZone(zoneName);
|
||||
});
|
||||
}
|
||||
} // namespace ui
|
||||
8
src/ModMan/Web/Binds/AssetBinds.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void RegisterAssetBinds(webview::webview& wv);
|
||||
} // namespace ui
|
||||
17
src/ModMan/Web/Binds/Binds.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "Binds.h"
|
||||
|
||||
#include "AssetBinds.h"
|
||||
#include "DialogBinds.h"
|
||||
#include "UnlinkingBinds.h"
|
||||
#include "ZoneBinds.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void RegisterAllBinds(webview::webview& wv)
|
||||
{
|
||||
RegisterAssetBinds(wv);
|
||||
RegisterDialogHandlerBinds(wv);
|
||||
RegisterUnlinkingBinds(wv);
|
||||
RegisterZoneBinds(wv);
|
||||
}
|
||||
} // namespace ui
|
||||
8
src/ModMan/Web/Binds/Binds.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void RegisterAllBinds(webview::webview& wv);
|
||||
}
|
||||
122
src/ModMan/Web/Binds/UnlinkingBinds.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "UnlinkingBinds.h"
|
||||
|
||||
#include "Context/ModManContext.h"
|
||||
#include "IObjWriter.h"
|
||||
#include "SearchPath/OutputPathFilesystem.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "Utils/PathUtils.h"
|
||||
#include "Web/UiCommunication.h"
|
||||
|
||||
#include "Json/JsonExtension.h"
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
class ZoneUnlinkProgressDto
|
||||
{
|
||||
public:
|
||||
std::string zoneName;
|
||||
double percentage;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnlinkProgressDto, zoneName, percentage);
|
||||
|
||||
constexpr double MIN_PROGRESS_TO_REPORT = 0.5;
|
||||
|
||||
class UnlinkingEventProgressReporter : public ProgressCallback
|
||||
{
|
||||
public:
|
||||
explicit UnlinkingEventProgressReporter(std::string zoneName)
|
||||
: m_zone_name(std::move(zoneName)),
|
||||
m_last_progress(0)
|
||||
{
|
||||
}
|
||||
|
||||
void OnProgress(const size_t current, const size_t total) override
|
||||
{
|
||||
const double percentage = static_cast<double>(current) / static_cast<double>(total) * 100.0;
|
||||
|
||||
if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT)
|
||||
{
|
||||
m_last_progress = percentage;
|
||||
ui::NotifyZoneUnlinkProgress(m_zone_name, percentage);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_zone_name;
|
||||
double m_last_progress;
|
||||
};
|
||||
|
||||
result::Expected<NoResult, std::string> UnlinkZoneInDbThread(const std::string& zoneName)
|
||||
{
|
||||
const auto& context = ModManContext::Get().m_fast_file;
|
||||
const auto existingZone = std::ranges::find_if(context.m_loaded_zones,
|
||||
[&zoneName](const std::unique_ptr<LoadedZone>& loadedZone)
|
||||
{
|
||||
return loadedZone->m_zone->m_name == zoneName;
|
||||
});
|
||||
|
||||
if (existingZone == context.m_loaded_zones.end())
|
||||
return result::Unexpected(std::format("No zone with name {} loaded", zoneName));
|
||||
|
||||
const auto& loadedZone = *existingZone->get();
|
||||
|
||||
const auto* objWriter = IObjWriter::GetObjWriterForGame(loadedZone.m_zone->m_game_id);
|
||||
|
||||
const auto outputFolderPath = fs::path(utils::GetExecutablePath()).parent_path() / "zone_dump" / zoneName;
|
||||
const auto outputFolderPathStr = outputFolderPath.string();
|
||||
|
||||
OutputPathFilesystem outputFolderOutputPath(outputFolderPath);
|
||||
SearchPaths searchPaths;
|
||||
AssetDumpingContext dumpingContext(
|
||||
*loadedZone.m_zone, outputFolderPathStr, outputFolderOutputPath, searchPaths, std::make_unique<UnlinkingEventProgressReporter>(zoneName));
|
||||
objWriter->DumpZone(dumpingContext);
|
||||
|
||||
return NoResult();
|
||||
}
|
||||
|
||||
void UnlinkZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety
|
||||
{
|
||||
ModManContext::Get().m_db_thread.Dispatch(
|
||||
[&wv, id, zoneName]
|
||||
{
|
||||
auto result = UnlinkZoneInDbThread(zoneName);
|
||||
|
||||
if (result)
|
||||
{
|
||||
con::debug("Unlinked zone \"{}\"", zoneName);
|
||||
ui::PromiseResolve(wv, id, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
con::warn("Failed to unlink zone \"{}\": {}", zoneName, result.error());
|
||||
ui::PromiseReject(wv, id, std::move(result).error());
|
||||
}
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void NotifyZoneUnlinkProgress(std::string zoneName, const double percentage)
|
||||
{
|
||||
const ZoneUnlinkProgressDto dto{
|
||||
.zoneName = std::move(zoneName),
|
||||
.percentage = percentage,
|
||||
};
|
||||
Notify(*ModManContext::Get().m_main_webview, "zoneUnlinkProgress", dto);
|
||||
}
|
||||
|
||||
void RegisterUnlinkingBinds(webview::webview& wv)
|
||||
{
|
||||
BindAsync<std::string>(wv,
|
||||
"unlinkZone",
|
||||
[&wv](const std::string& id, std::string zoneName)
|
||||
{
|
||||
UnlinkZone(wv, id, std::move(zoneName));
|
||||
});
|
||||
}
|
||||
} // namespace ui
|
||||
10
src/ModMan/Web/Binds/UnlinkingBinds.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void NotifyZoneUnlinkProgress(std::string zoneName, double percentage);
|
||||
|
||||
void RegisterUnlinkingBinds(webview::webview& wv);
|
||||
} // namespace ui
|
||||
185
src/ModMan/Web/Binds/ZoneBinds.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
#include "ZoneBinds.h"
|
||||
|
||||
#include "Context/ModManContext.h"
|
||||
#include "Web/UiCommunication.h"
|
||||
|
||||
#include "Json/JsonExtension.h"
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(GameId,
|
||||
{
|
||||
{GameId::IW3, "IW3"},
|
||||
{GameId::IW4, "IW4"},
|
||||
{GameId::IW5, "IW5"},
|
||||
{GameId::T5, "T5" },
|
||||
{GameId::T6, "T6" },
|
||||
});
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(GamePlatform,
|
||||
{
|
||||
{GamePlatform::PC, "PC" },
|
||||
{GamePlatform::XBOX, "XBOX"},
|
||||
{GamePlatform::PS3, "PS3" },
|
||||
});
|
||||
|
||||
namespace
|
||||
{
|
||||
class ZoneDto
|
||||
{
|
||||
public:
|
||||
std::string name;
|
||||
std::string filePath;
|
||||
GameId game;
|
||||
GamePlatform platform;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneDto, name, filePath, game, platform);
|
||||
|
||||
class ZoneLoadProgressDto
|
||||
{
|
||||
public:
|
||||
std::string zoneName;
|
||||
double percentage;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadProgressDto, zoneName, percentage);
|
||||
|
||||
class ZoneLoadedDto
|
||||
{
|
||||
public:
|
||||
ZoneDto zone;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zone);
|
||||
|
||||
class ZoneUnloadedDto
|
||||
{
|
||||
public:
|
||||
std::string zoneName;
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName);
|
||||
|
||||
ZoneDto CreateZoneDto(const LoadedZone& loadedZone)
|
||||
{
|
||||
return ZoneDto{
|
||||
.name = loadedZone.m_zone->m_name,
|
||||
.filePath = loadedZone.m_file_path,
|
||||
.game = loadedZone.m_zone->m_game_id,
|
||||
.platform = loadedZone.m_zone->m_platform,
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<ZoneDto> GetLoadedZones()
|
||||
{
|
||||
auto& context = ModManContext::Get().m_fast_file;
|
||||
|
||||
std::vector<ZoneDto> result;
|
||||
|
||||
{
|
||||
std::shared_lock lock(context.m_zone_lock);
|
||||
result.reserve(context.m_loaded_zones.size());
|
||||
|
||||
for (const auto& loadedZone : context.m_loaded_zones)
|
||||
{
|
||||
result.emplace_back(CreateZoneDto(*loadedZone));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void LoadFastFile(webview::webview& wv, std::string id, std::string path) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety
|
||||
{
|
||||
ModManContext::Get().m_db_thread.Dispatch(
|
||||
[&wv, id, path]
|
||||
{
|
||||
auto maybeZone = ModManContext::Get().m_fast_file.LoadFastFile(path);
|
||||
|
||||
if (maybeZone)
|
||||
{
|
||||
ui::PromiseResolve(wv,
|
||||
id,
|
||||
ZoneLoadedDto{
|
||||
.zone = CreateZoneDto(*maybeZone.value()),
|
||||
});
|
||||
con::debug("Loaded zone \"{}\"", maybeZone.value()->m_zone->m_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
con::warn("Failed to load zone \"{}\": {}", path, maybeZone.error());
|
||||
ui::PromiseReject(wv, id, std::move(maybeZone).error());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void UnloadZone(webview::webview& wv, std::string id, std::string zoneName) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety
|
||||
{
|
||||
ModManContext::Get().m_db_thread.Dispatch(
|
||||
[&wv, id, zoneName]
|
||||
{
|
||||
auto result = ModManContext::Get().m_fast_file.UnloadZone(zoneName);
|
||||
if (result)
|
||||
{
|
||||
con::debug("Unloaded zone \"{}\"", zoneName);
|
||||
ui::PromiseResolve(wv, id, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
con::warn("Failed unloading zone {}: {}", zoneName, result.error());
|
||||
ui::PromiseReject(wv, id, std::move(result).error());
|
||||
}
|
||||
});
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void NotifyZoneLoadProgress(std::string zoneName, const double percentage)
|
||||
{
|
||||
const ZoneLoadProgressDto dto{
|
||||
.zoneName = std::move(zoneName),
|
||||
.percentage = percentage,
|
||||
};
|
||||
Notify(*ModManContext::Get().m_main_webview, "zoneLoadProgress", dto);
|
||||
}
|
||||
|
||||
void NotifyZoneLoaded(const LoadedZone& loadedZone)
|
||||
{
|
||||
const ZoneLoadedDto dto{
|
||||
.zone = CreateZoneDto(loadedZone),
|
||||
};
|
||||
Notify(*ModManContext::Get().m_main_webview, "zoneLoaded", dto);
|
||||
}
|
||||
|
||||
void NotifyZoneUnloaded(std::string zoneName)
|
||||
{
|
||||
const ZoneUnloadedDto dto{
|
||||
.zoneName = std::move(zoneName),
|
||||
};
|
||||
Notify(*ModManContext::Get().m_main_webview, "zoneUnloaded", dto);
|
||||
}
|
||||
|
||||
void RegisterZoneBinds(webview::webview& wv)
|
||||
{
|
||||
BindRetOnly<std::vector<ZoneDto>>(wv,
|
||||
"getZones",
|
||||
[]
|
||||
{
|
||||
return GetLoadedZones();
|
||||
});
|
||||
|
||||
BindAsync<std::string>(wv,
|
||||
"loadFastFile",
|
||||
[&wv](const std::string& id, std::string path)
|
||||
{
|
||||
LoadFastFile(wv, id, std::move(path));
|
||||
});
|
||||
|
||||
BindAsync<std::string>(wv,
|
||||
"unloadZone",
|
||||
[&wv](const std::string& id, std::string zoneName)
|
||||
{
|
||||
UnloadZone(wv, id, std::move(zoneName));
|
||||
});
|
||||
}
|
||||
} // namespace ui
|
||||
13
src/ModMan/Web/Binds/ZoneBinds.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Context/FastFileContext.h"
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void NotifyZoneLoadProgress(std::string zoneName, double percentage);
|
||||
void NotifyZoneLoaded(const LoadedZone& loadedZone);
|
||||
void NotifyZoneUnloaded(std::string zoneName);
|
||||
|
||||
void RegisterZoneBinds(webview::webview& wv);
|
||||
} // namespace ui
|
||||
@@ -1,7 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Windows/AssetHandlerWindows.h"
|
||||
#elif defined(__linux__)
|
||||
#include "Linux/AssetHandlerLinux.h"
|
||||
#include "Web/Platform/Platform.h"
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
#include <webview/macros.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
#if defined(PLATFORM_WINDOWS)
|
||||
constexpr auto URL_PREFIX = "http://modman.local/";
|
||||
#elif defined(PLATFORM_LINUX)
|
||||
constexpr auto URL_PREFIX = "modman://localhost/";
|
||||
#endif
|
||||
|
||||
void InstallAssetHandler(webview::webview& wv);
|
||||
} // namespace ui
|
||||
|
||||
10
src/ModMan/Web/Platform/FaviconHandler.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
#include <webview/macros.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void InstallFaviconHandler(webview::webview& wv);
|
||||
} // namespace ui
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "AssetHandlerLinux.h"
|
||||
#include "Web/Platform/AssetHandler.h"
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
||||
#ifdef PLATFORM_LINUX
|
||||
|
||||
#include "Web/UiAssets.h"
|
||||
|
||||
@@ -8,8 +9,6 @@
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace PLATFORM_NAMESPACE_LINUX;
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<std::string, UiFile> assetLookup;
|
||||
@@ -37,7 +36,7 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace PLATFORM_NAMESPACE_LINUX
|
||||
namespace ui
|
||||
{
|
||||
void InstallAssetHandler(webview::webview& wv)
|
||||
{
|
||||
@@ -49,6 +48,6 @@ namespace PLATFORM_NAMESPACE_LINUX
|
||||
|
||||
webkit_web_context_register_uri_scheme(context, "modman", ModManUriSchemeRequestCb, NULL, nullptr);
|
||||
}
|
||||
} // namespace PLATFORM_NAMESPACE_LINUX
|
||||
} // namespace ui
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#include <webview/macros.h>
|
||||
|
||||
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
namespace PLATFORM_NAMESPACE_LINUX
|
||||
{
|
||||
constexpr auto URL_PREFIX = "modman://localhost/";
|
||||
|
||||
void InstallAssetHandler(webview::webview& wv);
|
||||
} // namespace PLATFORM_NAMESPACE_LINUX
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Web/Platform/DialogHandler.h"
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#ifdef PLATFORM_LINUX
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
@@ -37,8 +38,6 @@ namespace
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenFileDialog() {}
|
||||
|
||||
void SetFilters(GtkFileDialog* pDialog, const std::vector<ui::FileDialogFilter>& filters)
|
||||
{
|
||||
if (filters.empty())
|
||||
|
||||
22
src/ModMan/Web/Platform/Linux/FaviconHandlerLinux.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "Web/Platform/FaviconHandler.h"
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
|
||||
#include "Web/UiAssets.h"
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void InstallFaviconHandler(webview::webview& wv)
|
||||
{
|
||||
// The icon system on Linux works a bit different than on Windows
|
||||
// and doesn't really support this kind of dynamic icon setting
|
||||
// we skip it for now
|
||||
}
|
||||
} // namespace ui
|
||||
|
||||
#endif
|
||||
30
src/ModMan/Web/Platform/Linux/TitleHandlerLinux.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "Web/Platform/Platform.h"
|
||||
#include "Web/Platform/TitleHandler.h"
|
||||
|
||||
#ifdef PLATFORM_LINUX
|
||||
|
||||
#include "Web/UiAssets.h"
|
||||
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void InstallTitleHandler(webview::webview& wv)
|
||||
{
|
||||
const auto webViewWidget = static_cast<GtkWidget*>(wv.browser_controller().value());
|
||||
const auto webView = WEBKIT_WEB_VIEW(webViewWidget);
|
||||
const auto windowWidget = static_cast<GtkWidget*>(wv.window().value());
|
||||
const auto window = GTK_WINDOW(windowWidget);
|
||||
|
||||
auto on_title_changed = +[](GtkWidget* widget, GParamSpec paramSpec, GtkWindow* window)
|
||||
{
|
||||
gtk_window_set_title(window, webkit_web_view_get_title(WEBKIT_WEB_VIEW(widget)));
|
||||
};
|
||||
|
||||
g_signal_connect(G_OBJECT(webView), "notify::title", G_CALLBACK(on_title_changed), (gpointer)window);
|
||||
}
|
||||
} // namespace ui
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define PLATFORM_NAMESPACE_WINDOWS windows
|
||||
#define PLATFORM_NAMESPACE_LINUX linux
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PLATFORM_NAMESPACE PLATFORM_NAMESPACE_WINDOWS
|
||||
#define PLATFORM_WINDOWS
|
||||
#elif defined(__linux__)
|
||||
#define PLATFORM_NAMESPACE PLATFORM_NAMESPACE_LINUX
|
||||
#define PLATFORM_LINUX
|
||||
#endif
|
||||
|
||||
10
src/ModMan/Web/Platform/TitleHandler.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
#include <webview/macros.h>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void InstallTitleHandler(webview::webview& wv);
|
||||
} // namespace ui
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "AssetHandlerWindows.h"
|
||||
#include "Web/Platform/AssetHandler.h"
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
#include "PlatformUtilsWindows.h"
|
||||
#include "Web/UiAssets.h"
|
||||
@@ -12,8 +13,6 @@
|
||||
#include <webview/detail/backends/win32_edge.hh>
|
||||
#include <wrl/event.h>
|
||||
|
||||
using namespace PLATFORM_NAMESPACE_WINDOWS;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto LOCALHOST_PREFIX = "http://localhost:";
|
||||
@@ -25,7 +24,7 @@ namespace
|
||||
std::wstringstream wss;
|
||||
|
||||
wss << std::format(L"Content-Length: {}\n", contentLength);
|
||||
wss << L"Content-Type: " << StringToWideString(ui::GetMimeTypeForFileName(assetName));
|
||||
wss << L"Content-Type: " << utils::StringToWideString(ui::GetMimeTypeForFileName(assetName));
|
||||
|
||||
return wss.str();
|
||||
}
|
||||
@@ -65,7 +64,7 @@ namespace
|
||||
|
||||
Microsoft::WRL::ComPtr<ICoreWebView2WebResourceResponse> response;
|
||||
|
||||
const auto uri = WideStringToString(wUri);
|
||||
const auto uri = utils::WideStringToString(wUri);
|
||||
bool fileFound = false;
|
||||
|
||||
#ifdef _DEBUG
|
||||
@@ -74,9 +73,9 @@ namespace
|
||||
return S_OK;
|
||||
#endif
|
||||
|
||||
if (uri.starts_with(URL_PREFIX))
|
||||
if (uri.starts_with(ui::URL_PREFIX))
|
||||
{
|
||||
const auto asset = uri.substr(std::char_traits<char>::length(URL_PREFIX) - 1);
|
||||
const auto asset = uri.substr(std::char_traits<char>::length(ui::URL_PREFIX) - 1);
|
||||
|
||||
const auto foundUiFile = assetLookup.find(asset);
|
||||
if (foundUiFile != assetLookup.end())
|
||||
@@ -117,7 +116,7 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
namespace ui
|
||||
{
|
||||
void InstallAssetHandler(webview::webview& wv)
|
||||
{
|
||||
@@ -157,6 +156,6 @@ namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
std::cerr << "Failed to add resource requested filter\n";
|
||||
}
|
||||
}
|
||||
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
} // namespace ui
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#include <webview/macros.h>
|
||||
|
||||
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
||||
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
{
|
||||
constexpr auto URL_PREFIX = "http://modman.local/";
|
||||
|
||||
void InstallAssetHandler(webview::webview& wv);
|
||||
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Web/Platform/DialogHandler.h"
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
#include "PlatformUtilsWindows.h"
|
||||
|
||||
@@ -8,8 +9,6 @@
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
|
||||
using namespace PLATFORM_NAMESPACE_WINDOWS;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool SetFilters(IFileDialog* pFileOpen, const std::vector<ui::FileDialogFilter>& filters)
|
||||
@@ -29,8 +28,8 @@ namespace
|
||||
const auto& filter = filters[i];
|
||||
COMDLG_FILTERSPEC filterSpec;
|
||||
|
||||
const auto& wName = filterStrings.emplace_back(StringToWideString(filter.m_name));
|
||||
const auto& wSpec = filterStrings.emplace_back(StringToWideString(filter.m_filter));
|
||||
const auto& wName = filterStrings.emplace_back(utils::StringToWideString(filter.m_name));
|
||||
const auto& wSpec = filterStrings.emplace_back(utils::StringToWideString(filter.m_filter));
|
||||
|
||||
filterSpec.pszName = wName.c_str();
|
||||
filterSpec.pszSpec = wSpec.c_str();
|
||||
@@ -65,7 +64,7 @@ namespace
|
||||
// Display the file name to the user.
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
result = WideStringToString(pszFilePath);
|
||||
result = utils::WideStringToString(pszFilePath);
|
||||
CoTaskMemFree(pszFilePath);
|
||||
|
||||
resultType = ui::DialogCallbackResultType::SUCCESSFUL;
|
||||
|
||||
150
src/ModMan/Web/Platform/Windows/FaviconHandlerWindows.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "Web/Platform/FaviconHandler.h"
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
#include "PlatformUtilsWindows.h"
|
||||
#include "Web/UiAssets.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <format>
|
||||
#include <gdiplus.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <webview/detail/backends/win32_edge.hh>
|
||||
#include <wrl/event.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
class UniqueHIcon
|
||||
{
|
||||
public:
|
||||
UniqueHIcon()
|
||||
: m_icon(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~UniqueHIcon()
|
||||
{
|
||||
if (m_icon)
|
||||
{
|
||||
DestroyIcon(m_icon);
|
||||
m_icon = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
UniqueHIcon(const UniqueHIcon& other) = default;
|
||||
|
||||
UniqueHIcon(UniqueHIcon&& other) noexcept
|
||||
: m_icon(other.m_icon)
|
||||
{
|
||||
other.m_icon = nullptr;
|
||||
}
|
||||
|
||||
UniqueHIcon& operator=(const UniqueHIcon& other) = default;
|
||||
|
||||
UniqueHIcon& operator=(UniqueHIcon&& other) noexcept
|
||||
{
|
||||
m_icon = other.m_icon;
|
||||
other.m_icon = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HICON& get()
|
||||
{
|
||||
return m_icon;
|
||||
}
|
||||
|
||||
private:
|
||||
HICON m_icon;
|
||||
};
|
||||
|
||||
std::unordered_map<HWND, UniqueHIcon> icons;
|
||||
|
||||
HRESULT HandleFaviconChanged(ICoreWebView2_15* core15, HWND window)
|
||||
{
|
||||
LPWSTR url;
|
||||
|
||||
if (!SUCCEEDED(core15->get_FaviconUri(&url)))
|
||||
{
|
||||
std::cerr << "Failed to get favicon uri\n";
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
const std::wstring strUrl(url);
|
||||
CoTaskMemFree(url);
|
||||
|
||||
if (strUrl.empty())
|
||||
{
|
||||
icons.erase(icons.find(window));
|
||||
SendMessage(window, WM_SETICON, ICON_SMALL, (LPARAM)NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SUCCEEDED(core15->GetFavicon(COREWEBVIEW2_FAVICON_IMAGE_FORMAT_PNG,
|
||||
Microsoft::WRL::Callback<ICoreWebView2GetFaviconCompletedHandler>(
|
||||
[window](HRESULT errorCode, IStream* iconStream) -> HRESULT
|
||||
{
|
||||
Gdiplus::Bitmap iconBitmap(iconStream);
|
||||
UniqueHIcon icon;
|
||||
if (iconBitmap.GetHICON(&icon.get()) == Gdiplus::Status::Ok)
|
||||
{
|
||||
SendMessage(window, WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(icon.get()));
|
||||
icons.emplace(window, std::move(icon)).first->second.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
icons.erase(icons.find(window));
|
||||
SendMessage(window, WM_SETICON, ICON_SMALL, NULL);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
})
|
||||
.Get())))
|
||||
{
|
||||
std::cerr << "Failed to get favicon\n";
|
||||
return S_FALSE;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void InstallFaviconHandler(webview::webview& wv)
|
||||
{
|
||||
const auto controller = static_cast<ICoreWebView2Controller*>(wv.browser_controller().value());
|
||||
auto window = static_cast<HWND>(wv.window().value());
|
||||
Microsoft::WRL::ComPtr<ICoreWebView2> core;
|
||||
if (!SUCCEEDED(controller->get_CoreWebView2(&core)))
|
||||
{
|
||||
std::cerr << "Failed to get webview\n";
|
||||
return;
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<ICoreWebView2_15> core15;
|
||||
if (!SUCCEEDED(core->QueryInterface(IID_PPV_ARGS(&core15))))
|
||||
{
|
||||
std::cerr << "Failed to get core15\n";
|
||||
return;
|
||||
}
|
||||
|
||||
const Gdiplus::GdiplusStartupInput gdiPlusStartupInput;
|
||||
ULONG_PTR gdiPlusToken;
|
||||
Gdiplus::GdiplusStartup(&gdiPlusToken, &gdiPlusStartupInput, nullptr);
|
||||
EventRegistrationToken token;
|
||||
if (!SUCCEEDED(core15->add_FaviconChanged(Microsoft::WRL::Callback<ICoreWebView2FaviconChangedEventHandler>(
|
||||
[core15, window](ICoreWebView2* sender, IUnknown* args) -> HRESULT
|
||||
{
|
||||
return HandleFaviconChanged(core15.Get(), window);
|
||||
})
|
||||
.Get(),
|
||||
&token)))
|
||||
{
|
||||
std::cerr << "Failed to add favicon handler\n";
|
||||
}
|
||||
}
|
||||
} // namespace ui
|
||||
|
||||
#endif
|
||||
@@ -1,12 +1,12 @@
|
||||
#include "PlatformUtilsWindows.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
#include <Windows.h>
|
||||
#include <exception>
|
||||
#include <format>
|
||||
|
||||
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
namespace utils
|
||||
{
|
||||
std::string WideStringToString(const std::wstring& wideString)
|
||||
{
|
||||
@@ -35,6 +35,6 @@ namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast<int>(string.size()), result.data(), sizeNeeded);
|
||||
return result;
|
||||
}
|
||||
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
} // namespace utils
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Web/Platform/Platform.h"
|
||||
|
||||
#include <optional>
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
namespace utils
|
||||
{
|
||||
std::string WideStringToString(const std::wstring& wideString);
|
||||
std::wstring StringToWideString(const std::string& string);
|
||||
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||
} // namespace utils
|
||||
|
||||
#endif
|
||||
|
||||
62
src/ModMan/Web/Platform/Windows/TitleHandlerWindows.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "Web/Platform/Platform.h"
|
||||
#include "Web/Platform/TitleHandler.h"
|
||||
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
|
||||
#include "PlatformUtilsWindows.h"
|
||||
#include "Web/UiAssets.h"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <webview/detail/backends/win32_edge.hh>
|
||||
#include <wrl/event.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
HRESULT HandleTitleChanged(ICoreWebView2* core, HWND window)
|
||||
{
|
||||
LPWSTR title;
|
||||
|
||||
if (!SUCCEEDED(core->get_DocumentTitle(&title)))
|
||||
{
|
||||
std::cerr << "Failed to get title\n";
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
SetWindowTextW(window, title);
|
||||
CoTaskMemFree(title);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace ui
|
||||
{
|
||||
void InstallTitleHandler(webview::webview& wv)
|
||||
{
|
||||
const auto controller = static_cast<ICoreWebView2Controller*>(wv.browser_controller().value());
|
||||
auto window = static_cast<HWND>(wv.window().value());
|
||||
Microsoft::WRL::ComPtr<ICoreWebView2> core;
|
||||
if (!SUCCEEDED(controller->get_CoreWebView2(&core)))
|
||||
{
|
||||
std::cerr << "Failed to get webview\n";
|
||||
return;
|
||||
}
|
||||
|
||||
EventRegistrationToken token;
|
||||
if (!SUCCEEDED(core->add_DocumentTitleChanged(Microsoft::WRL::Callback<ICoreWebView2DocumentTitleChangedEventHandler>(
|
||||
[window](ICoreWebView2* sender, IUnknown* args) -> HRESULT
|
||||
{
|
||||
return HandleTitleChanged(sender, window);
|
||||
})
|
||||
.Get(),
|
||||
&token)))
|
||||
{
|
||||
std::cerr << "Failed to add title handler\n";
|
||||
}
|
||||
}
|
||||
} // namespace ui
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#define NOMINMAX
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
|
||||
@@ -1,32 +1,41 @@
|
||||
#include "GitVersion.h"
|
||||
#include "Web/Binds/DialogBinds.h"
|
||||
#include "Context/ModManContext.h"
|
||||
#include "GitVersion.h"
|
||||
#include "ModManArgs.h"
|
||||
#include "Web/Binds/Binds.h"
|
||||
#include "Web/Platform/AssetHandler.h"
|
||||
#include "Web/Platform/FaviconHandler.h"
|
||||
#include "Web/Platform/TitleHandler.h"
|
||||
#include "Web/UiCommunication.h"
|
||||
#include "Web/ViteAssets.h"
|
||||
#include "Web/WebViewLib.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
using namespace std::string_literals;
|
||||
using namespace PLATFORM_NAMESPACE;
|
||||
|
||||
namespace
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
std::optional<webview::webview> devToolWindow;
|
||||
|
||||
void RunDevToolsWindow()
|
||||
void SpawnDevToolsWindow()
|
||||
{
|
||||
con::debug("Creating dev tools window");
|
||||
|
||||
auto& context = ModManContext::Get();
|
||||
|
||||
try
|
||||
{
|
||||
auto& newWindow = devToolWindow.emplace(false, nullptr);
|
||||
context.m_dev_tools_webview = std::make_unique<webview::webview>(false, nullptr);
|
||||
auto& newWindow = *context.m_dev_tools_webview;
|
||||
ui::InstallFaviconHandler(newWindow);
|
||||
ui::InstallTitleHandler(newWindow);
|
||||
|
||||
newWindow.set_title("Devtools");
|
||||
newWindow.set_size(640, 480, WEBVIEW_HINT_NONE);
|
||||
newWindow.set_size(480, 320, WEBVIEW_HINT_MIN);
|
||||
@@ -39,59 +48,46 @@ namespace
|
||||
}
|
||||
#endif
|
||||
|
||||
int RunMainWindow()
|
||||
int SpawnMainWindow()
|
||||
{
|
||||
con::debug("Creating main window");
|
||||
|
||||
auto& context = ModManContext::Get();
|
||||
try
|
||||
{
|
||||
webview::webview w(
|
||||
context.m_main_webview = std::make_unique<webview::webview>(
|
||||
#ifdef _DEBUG
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
nullptr);
|
||||
w.set_title("OpenAssetTools ModMan");
|
||||
w.set_size(1280, 640, WEBVIEW_HINT_NONE);
|
||||
w.set_size(480, 320, WEBVIEW_HINT_MIN);
|
||||
auto& newWindow = *context.m_main_webview;
|
||||
|
||||
ui::RegisterDialogHandlerBinds(w);
|
||||
newWindow.set_title("OpenAssetTools ModMan");
|
||||
newWindow.set_size(1280, 640, WEBVIEW_HINT_NONE);
|
||||
newWindow.set_size(640, 480, WEBVIEW_HINT_MIN);
|
||||
|
||||
// A binding that counts up or down and immediately returns the new value.
|
||||
ui::Bind<std::string, std::string>(w,
|
||||
"greet",
|
||||
[&w](std::string name) -> std::string
|
||||
{
|
||||
ui::Notify(w, "greeting", name);
|
||||
return std::format("Hello from C++ {}!", name);
|
||||
});
|
||||
|
||||
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
||||
InstallAssetHandler(w);
|
||||
constexpr auto urlPrefix = URL_PREFIX;
|
||||
#elif defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
||||
InstallAssetHandler(w);
|
||||
constexpr auto urlPrefix = URL_PREFIX;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
ui::InstallAssetHandler(newWindow);
|
||||
ui::InstallFaviconHandler(newWindow);
|
||||
ui::InstallTitleHandler(newWindow);
|
||||
ui::RegisterAllBinds(newWindow);
|
||||
|
||||
#ifdef _DEBUG
|
||||
w.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", urlPrefix));
|
||||
newWindow.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", ui::URL_PREFIX));
|
||||
|
||||
if (VITE_DEV_SERVER)
|
||||
{
|
||||
w.dispatch(
|
||||
newWindow.dispatch(
|
||||
[]
|
||||
{
|
||||
RunDevToolsWindow();
|
||||
SpawnDevToolsWindow();
|
||||
});
|
||||
}
|
||||
#else
|
||||
w.navigate(std::format("{}index.html", urlPrefix));
|
||||
newWindow.navigate(std::format("{}index.html", ui::URL_PREFIX));
|
||||
#endif
|
||||
w.run();
|
||||
newWindow.run();
|
||||
}
|
||||
catch (const webview::exception& e)
|
||||
{
|
||||
@@ -104,16 +100,50 @@ namespace
|
||||
} // namespace
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MODMAN_ARGC __argc
|
||||
#define MODMAN_ARGV const_cast<const char**>(__argv)
|
||||
int WINAPI WinMain(HINSTANCE /*hInst*/, HINSTANCE /*hPrevInst*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
|
||||
{
|
||||
#else
|
||||
int main()
|
||||
{
|
||||
#define MODMAN_ARGC argc
|
||||
#define MODMAN_ARGV argv
|
||||
int main(int argc, const char** argv)
|
||||
#endif
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Attach console if possible on Windows for stdout/stderr in console
|
||||
if (AttachConsole(-1))
|
||||
{
|
||||
FILE* fDummy;
|
||||
(void)freopen_s(&fDummy, "CONOUT$", "w", stdout);
|
||||
(void)freopen_s(&fDummy, "CONOUT$", "w", stderr);
|
||||
(void)freopen_s(&fDummy, "CONIN$", "r", stdin);
|
||||
std::cout.clear();
|
||||
std::clog.clear();
|
||||
std::cerr.clear();
|
||||
std::cin.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
g_set_prgname("OpenAssetTools-ModMan");
|
||||
g_set_application_name("OpenAssetTools ModMan");
|
||||
#endif
|
||||
|
||||
ModManArgs args;
|
||||
auto shouldContinue = true;
|
||||
if (!args.ParseArgs(MODMAN_ARGC, MODMAN_ARGV, shouldContinue))
|
||||
return false;
|
||||
|
||||
if (!shouldContinue)
|
||||
return true;
|
||||
|
||||
con::info("Starting ModMan " GIT_VERSION);
|
||||
|
||||
const auto result = RunMainWindow();
|
||||
ModManContext::Get().Startup();
|
||||
|
||||
const auto result = SpawnMainWindow();
|
||||
|
||||
ModManContext::Get().Destroy();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Plugin } from "vite";
|
||||
import type { Plugin, UserConfig } from "vite";
|
||||
import type { OutputAsset, OutputChunk } from "rollup";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
@@ -7,8 +7,35 @@ type MinimalOutputAsset = Pick<OutputAsset, "type" | "fileName" | "source">;
|
||||
type MinimalOutputChunk = Pick<OutputChunk, "type" | "fileName" | "code">;
|
||||
type MinimalOutputBundle = Record<string, MinimalOutputAsset | MinimalOutputChunk>;
|
||||
|
||||
interface PublicDirFile {
|
||||
fullPath: string;
|
||||
relativePath: string;
|
||||
}
|
||||
|
||||
function getPublicDirFiles(publicDir?: string): PublicDirFile[] {
|
||||
if (!publicDir) return [];
|
||||
|
||||
const result: PublicDirFile[] = [];
|
||||
const files = fs.readdirSync(publicDir, { recursive: true, withFileTypes: true });
|
||||
for (const file of files) {
|
||||
if (!file.isFile()) continue;
|
||||
const fullPath = path.join(file.parentPath, file.name);
|
||||
let relativePath = path.relative(publicDir, fullPath).replaceAll(/\\/g, "/");
|
||||
if (relativePath.startsWith("./")) {
|
||||
relativePath = relativePath.substring(2);
|
||||
}
|
||||
|
||||
result.push({
|
||||
fullPath,
|
||||
relativePath,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function createVarName(fileName: string) {
|
||||
return fileName.replaceAll(".", "_").toUpperCase();
|
||||
return fileName.replaceAll(/[\\/]/g, "__").replaceAll(/[\.-]/g, "_").toUpperCase();
|
||||
}
|
||||
|
||||
function transformAsset(asset: MinimalOutputAsset) {
|
||||
@@ -33,10 +60,19 @@ function transformChunk(chunk: MinimalOutputChunk) {
|
||||
`;
|
||||
}
|
||||
|
||||
function transformPublicFile(publicFile: PublicDirFile) {
|
||||
const varName = createVarName(publicFile.relativePath);
|
||||
const bytes = [...fs.readFileSync(publicFile.fullPath)].map((v) => String(v)).join(",");
|
||||
|
||||
return `constexpr const unsigned char ${varName}[] {${bytes}};
|
||||
`;
|
||||
}
|
||||
|
||||
function writeHeader(
|
||||
bundle: MinimalOutputBundle,
|
||||
outputDir?: string,
|
||||
options?: HeaderTransformationPluginOptions,
|
||||
publicDir?: string,
|
||||
devServerPort?: number,
|
||||
) {
|
||||
const outputPath = options?.outputPath ?? path.join(outputDir ?? "dist", "ViteAssets.h");
|
||||
@@ -71,12 +107,18 @@ constexpr auto VITE_DEV_SERVER_PORT = ${devServerPort ? String(devServerPort) :
|
||||
`,
|
||||
);
|
||||
|
||||
const fileNames: string[] = [];
|
||||
for (const curBundle of Object.values(bundle)) {
|
||||
if (curBundle.type === "asset") {
|
||||
fs.writeSync(fd, transformAsset(curBundle));
|
||||
} else {
|
||||
fs.writeSync(fd, transformChunk(curBundle));
|
||||
}
|
||||
fileNames.push(curBundle.fileName);
|
||||
}
|
||||
for (const publicDirFile of getPublicDirFiles(publicDir)) {
|
||||
fs.writeSync(fd, transformPublicFile(publicDirFile));
|
||||
fileNames.push(publicDirFile.relativePath);
|
||||
}
|
||||
|
||||
if (includeFileEnumeration) {
|
||||
@@ -95,8 +137,7 @@ static inline const UiFile MOD_MAN_UI_FILES[] {
|
||||
);
|
||||
|
||||
let index = 0;
|
||||
for (const curBundle of Object.values(bundle)) {
|
||||
const fileName = curBundle.fileName;
|
||||
for (const fileName of fileNames) {
|
||||
const varName = createVarName(fileName);
|
||||
|
||||
let prefix = " ";
|
||||
@@ -133,16 +174,21 @@ export default function headerTransformationPlugin(
|
||||
): Plugin {
|
||||
let writeServerActive = false;
|
||||
let writeBundleActive = false;
|
||||
let publicDir: string | undefined = undefined;
|
||||
|
||||
return {
|
||||
name: "vite-plugin-header-transformation",
|
||||
enforce: "post",
|
||||
config(_userOptions, env) {
|
||||
config(userOptions: UserConfig, env) {
|
||||
if (env.command === "serve") {
|
||||
writeServerActive = true;
|
||||
} else {
|
||||
writeBundleActive = true;
|
||||
}
|
||||
|
||||
if (typeof userOptions.publicDir === "string") {
|
||||
publicDir = userOptions.publicDir;
|
||||
}
|
||||
},
|
||||
configureServer(server) {
|
||||
if (!writeServerActive) {
|
||||
@@ -156,6 +202,7 @@ export default function headerTransformationPlugin(
|
||||
},
|
||||
server.config.build.outDir,
|
||||
options,
|
||||
publicDir,
|
||||
server.config.server.port,
|
||||
);
|
||||
});
|
||||
@@ -165,7 +212,7 @@ export default function headerTransformationPlugin(
|
||||
return;
|
||||
}
|
||||
|
||||
writeHeader(bundle, outputOptions.dir, options);
|
||||
writeHeader(bundle, outputOptions.dir, options, publicDir);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="icon" href="/favicon.ico" sizes="512x512 256x256 128x128 64x64" />
|
||||
<link rel="icon" href="/logo_circle.svg" sizes="any" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Tauri + Vue + Typescript App</title>
|
||||
<title>OpenAssetTools ModMan</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
630
src/ModManUi/package-lock.json
generated
@@ -13,30 +13,34 @@
|
||||
"format": "prettier --write **/*.{js,ts,vue,html,json,yml,yaml,md}"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/inter": "^5.2.8",
|
||||
"@primeuix/themes": "^1.2.5",
|
||||
"pinia": "3.0.3",
|
||||
"vue": "3.5.22"
|
||||
"primevue": "^4.4.1",
|
||||
"vue": "3.5.22",
|
||||
"vue-router": "4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
"@types/jsdom": "^21.1.7",
|
||||
"@types/node": "^22.18.6",
|
||||
"@types/jsdom": "^27.0.0",
|
||||
"@types/node": "^24.9.2",
|
||||
"@vitejs/plugin-vue": "6.0.1",
|
||||
"@vitest/eslint-plugin": "^1.3.13",
|
||||
"@vue/eslint-config-prettier": "^10.2.0",
|
||||
"@vue/eslint-config-typescript": "^14.6.0",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"@vue/tsconfig": "^0.8.1",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-vue": "~10.4.0",
|
||||
"eslint": "^9.39.0",
|
||||
"eslint-plugin-vue": "~10.5.1",
|
||||
"jiti": "^2.5.1",
|
||||
"jsdom": "^27.0.0",
|
||||
"jsdom": "^27.1.0",
|
||||
"npm-run-all2": "^8.0.4",
|
||||
"prettier": "3.6.2",
|
||||
"sass": "1.93.2",
|
||||
"sass": "1.93.3",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "7.1.7",
|
||||
"vite-plugin-vue-devtools": "^8.0.2",
|
||||
"vite": "7.1.12",
|
||||
"vite-plugin-vue-devtools": "^8.0.3",
|
||||
"vitest": "^3.2.4",
|
||||
"vue-tsc": "3.1.0"
|
||||
"vue-tsc": "3.1.2"
|
||||
}
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 130 KiB |
77
src/ModManUi/public/logo_circle.svg
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="300mm"
|
||||
height="300mm"
|
||||
viewBox="0 0 300 300"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.4.2 (f4327f4, 2025-05-13)"
|
||||
sodipodi:docname="oat_circle.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.55748644"
|
||||
inkscape:cx="452.02893"
|
||||
inkscape:cy="637.68367"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1129"
|
||||
inkscape:window-x="1672"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" /><defs
|
||||
id="defs1"><filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter19"
|
||||
x="-0.16704432"
|
||||
y="-0.08986926"
|
||||
width="1.3601893"
|
||||
height="1.1937806"><feFlood
|
||||
result="flood"
|
||||
in="SourceGraphic"
|
||||
flood-opacity="0.403922"
|
||||
flood-color="rgb(0,0,0)"
|
||||
id="feFlood18" /><feGaussianBlur
|
||||
result="blur"
|
||||
in="SourceGraphic"
|
||||
stdDeviation="8.000000"
|
||||
id="feGaussianBlur18" /><feOffset
|
||||
result="offset"
|
||||
in="blur"
|
||||
dx="3.000000"
|
||||
dy="3.000000"
|
||||
id="feOffset18" /><feComposite
|
||||
result="comp1"
|
||||
operator="in"
|
||||
in="flood"
|
||||
in2="offset"
|
||||
id="feComposite18" /><feComposite
|
||||
result="comp2"
|
||||
operator="over"
|
||||
in="SourceGraphic"
|
||||
in2="comp1"
|
||||
id="feComposite19" /></filter></defs><g
|
||||
inkscape:label="Ebene 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"><circle
|
||||
style="fill:#ca893d;stroke-width:0.264583;fill-opacity:1"
|
||||
id="path2"
|
||||
cx="149.73645"
|
||||
cy="151.63484"
|
||||
r="128" /><path
|
||||
style="fill:#ffffff;stroke:none;stroke-width:0.724;fill-opacity:1;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter19)"
|
||||
d="m 157.15533,40.658523 c -7.16528,10.302747 -12.51327,22.617436 -16.6111,34.458658 -2.54917,7.366211 -4.77329,15.607101 -4.02076,23.482935 0.33817,3.539174 1.30988,7.672094 3.18904,10.720474 0.97925,1.58854 3.26203,2.91006 3.81397,4.59843 0.92967,2.84378 0.74731,6.23647 0.87645,9.18503 0.18554,4.23723 0.42148,8.50748 0.23514,12.76248 -0.30007,6.85199 -2.86584,14.43633 -5.6082,20.67519 -1.68147,3.82534 -4.42564,7.76492 -4.84697,11.99672 h -0.25525 c -0.30662,-7.14028 -1.15672,-13.81779 -4.08399,-20.41995 8.36198,-3.90966 7.24983,-15.18254 4.89917,-22.46194 -4.29583,-13.3031 -14.22445,-24.31606 -23.76379,-34.193176 -3.48045,-3.603683 -6.90971,-8.097499 -10.999551,-10.985956 0.986581,12.07896 2.622311,24.405512 5.251911,36.245412 1.24134,5.58915 2.55471,11.99777 5.22415,17.1017 2.58771,4.94766 5.31606,9.73231 10.19914,12.74592 1.43929,0.88826 2.9245,1.43029 4.59448,1.69122 0.9658,0.15091 2.16541,-0.0811 2.89736,0.70371 0.96354,1.03323 1.26089,2.94912 1.69713,4.2581 1.39158,4.17553 1.97461,8.38492 2.2744,12.76246 1.2066,17.61936 -5.6512,33.88753 -11.62428,50.02887 -1.81237,4.89764 -1.57442,10.20759 -3.63514,15.05971 -1.97356,4.64688 -4.14099,8.76759 -6.87617,13.01397 -1.95003,3.02741 -4.3974,6.00391 -4.89799,9.70322 2.02855,-0.15628 2.49496,-2.32202 3.60382,-3.75586 3.20156,-4.13988 6.3358,-8.3512 8.59123,-13.0906 2.73623,-5.74976 3.07562,-12.18903 5.18474,-18.12269 2.31458,-6.51166 6.40216,-12.47781 8.29107,-19.14371 1.96809,-6.94523 2.59545,-15.58781 6.73339,-21.68733 1.20177,-1.77146 5.24937,0.88445 7.15939,1.15809 5.04845,0.72321 10.70726,-0.88082 15.0597,-3.41613 12.21808,-7.11703 21.95698,-18.17281 30.34514,-29.37889 2.98928,-3.99351 6.48878,-7.93184 8.96326,-12.27481 -14.38224,4.6782 -29.79304,6.53244 -43.13713,13.95008 -6.39539,3.55503 -13.16101,8.2226 -16.65797,14.89309 -2.10395,4.01331 -3.43865,9.81381 -4.01723,14.29396 h -0.25524 c 0.50501,-6.34574 3.0145,-14.93411 6.77553,-20.12136 3.07929,-4.24698 7.25737,-7.62116 10.75375,-11.50671 1.94533,-2.16185 4.00931,-4.21297 5.91938,-6.40782 1.02315,-1.17568 2.09515,-2.89626 3.60777,-3.49746 0.98879,-0.393 2.11738,0.39715 3.06298,0.65289 1.52673,0.4129 3.27852,0.39838 4.84974,0.27199 4.6473,-0.37383 9.54534,-3.67807 13.00767,-6.56341 6.74442,-5.62047 11.67306,-14.04755 15.64624,-21.74543 4.64046,-8.990734 9.24016,-18.310406 12.69648,-27.822182 -6.07512,2.546043 -11.99754,6.635251 -17.6122,10.092786 -11.19017,6.890952 -23.12624,14.591936 -30.0038,26.152626 -1.81972,3.05881 -3.46108,6.64757 -3.90458,10.20997 -0.26495,2.12833 0.60659,5.94362 -0.46067,7.65728 -4.32991,6.95238 -12.89408,11.33384 -16.68082,18.6334 h -0.25524 c 2.92582,-7.9652 4.54429,-16.23824 4.33549,-24.75919 -0.0764,-3.12012 -0.19346,-6.34209 -0.57942,-9.44422 -0.17134,-1.37703 -0.87961,-3.43476 -0.46619,-4.79164 0.36474,-1.19717 4.30109,-2.11927 5.38859,-2.98795 3.08532,-2.46452 5.37666,-6.17729 6.78243,-9.83261 3.74161,-9.72902 3.6403,-20.912802 2.93989,-31.140425 -0.67532,-9.861296 -1.19822,-19.875516 -3.08584,-29.608922 h -0.5105 M 110.44471,144.0345 c -3.75903,11.55249 -8.14701,22.91376 -11.097041,34.71391 -1.534837,6.13935 -3.100637,12.50907 -3.015727,18.88845 0.0736,5.52842 0.41629,11.16231 3.261717,16.07972 0.917211,1.58511 2.191701,2.95005 3.704061,3.97665 0.86062,0.58419 2.09474,1.23975 1.85628,2.44889 -1.65212,8.37718 -6.089111,17.15329 -6.194528,25.73687 -0.0257,2.09216 -0.15445,4.03486 -0.007,6.12597 0.0519,0.73659 0.0476,2.07414 1.056448,2.15781 1.85965,0.15424 1.41636,-6.42184 1.50865,-7.77329 0.58862,-8.61903 3.54759,-17.30601 5.13276,-25.77544 0.33859,-1.80907 4.17634,-2.90995 5.57136,-4.04146 3.02114,-2.45045 5.08063,-5.89062 6.45548,-9.4915 5.74688,-15.05154 1.24022,-33.42794 -3.19868,-48.24212 -0.92396,-3.08358 -1.99799,-6.1122 -2.9391,-9.18897 -0.56904,-1.86034 -0.90105,-4.03706 -2.0944,-5.61549 m 56.15486,37.01115 c -5.76022,4.69086 -11.15297,9.97993 -16.59121,15.03687 -10.80773,10.04991 -24.26466,21.75848 -25.44202,37.54449 -0.19124,2.56417 0.42114,5.79387 1.38655,8.16798 0.44584,1.0964 1.53285,2.14209 1.69181,3.31824 0.41267,3.05318 -2.88452,5.79996 -1.88503,9.18898 h 0.25525 c 1.3389,-2.30487 2.3407,-5.50659 2.5525,-8.16798 2.19882,0.32473 4.09594,1.02573 6.38123,0.68894 6.47079,-0.95364 11.28708,-7.14598 14.68157,-12.17516 7.17889,-10.63603 10.7098,-23.84247 13.50438,-36.2454 0.93663,-4.15697 1.96349,-8.31019 2.72048,-12.50722 0.26038,-1.44356 1.17447,-3.43379 0.74449,-4.84974 z"
|
||||
id="path1" /></g></svg>
|
||||
|
After Width: | Height: | Size: 6.7 KiB |
@@ -1,54 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
import { onUnmounted, ref } from "vue";
|
||||
import { webviewBinds, webviewAddEventListener, webviewRemoveEventListener } from "./native";
|
||||
|
||||
const greetMsg = ref("");
|
||||
const lastPersonGreeted = ref("");
|
||||
const lastPath = ref("");
|
||||
const name = ref("");
|
||||
|
||||
async function greet() {
|
||||
greetMsg.value = await webviewBinds.greet(name.value);
|
||||
}
|
||||
|
||||
function onPersonGreeted(person: string) {
|
||||
lastPersonGreeted.value = person;
|
||||
}
|
||||
|
||||
async function onOpenFastfileClick() {
|
||||
lastPath.value =
|
||||
(await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] })) ?? "";
|
||||
}
|
||||
|
||||
webviewAddEventListener("greeting", onPersonGreeted);
|
||||
|
||||
onUnmounted(() => webviewRemoveEventListener("greeting", onPersonGreeted));
|
||||
import ModManHeader from "./components/header/ModManHeader.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main class="container">
|
||||
<h1>Welcome to ModMan</h1>
|
||||
<small>Nothing to see here yet, this is mainly for testing</small>
|
||||
<ModManHeader />
|
||||
|
||||
<form class="row" @submit.prevent="greet">
|
||||
<input id="greet-input" v-model="name" placeholder="Enter a name..." autocomplete="off" />
|
||||
<button type="submit">Greet</button>
|
||||
</form>
|
||||
<p>{{ greetMsg }}</p>
|
||||
<p>The last person greeted is: {{ lastPersonGreeted }}</p>
|
||||
<p>
|
||||
<button @click="onOpenFastfileClick">Open fastfile</button>
|
||||
<span>The last path: {{ lastPath }}</span>
|
||||
</p>
|
||||
<div class="router-wrapper">
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<Transition name="blend">
|
||||
<div class="router-rewrapper" :key="route.name">
|
||||
<component :is="Component" />
|
||||
</div>
|
||||
</Transition>
|
||||
</RouterView>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.logo.vite:hover {
|
||||
filter: drop-shadow(0 0 2em #747bff);
|
||||
<style scoped lang="scss">
|
||||
@use "@style/variables";
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
position: relative;
|
||||
flex-direction: column;
|
||||
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.logo.vue:hover {
|
||||
filter: drop-shadow(0 0 2em #249b73);
|
||||
.router-wrapper {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.router-rewrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.blend-enter-from,
|
||||
.blend-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.blend-enter-active,
|
||||
.blend-leave-active {
|
||||
transition: opacity 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
.blend-leave-active {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
51
src/ModManUi/src/PrimeVue.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import type { App } from "vue";
|
||||
import PrimeVue from "primevue/config";
|
||||
import Aura from "@primeuix/themes/aura";
|
||||
import { definePreset } from "@primeuix/themes";
|
||||
import type { ColorScale } from "@primeuix/styled";
|
||||
|
||||
const ModManTheme = definePreset(Aura, {
|
||||
primitive: {
|
||||
brand: {
|
||||
50: "var(--color-brand-50)",
|
||||
100: "var(--color-brand-100)",
|
||||
200: "var(--color-brand-200)",
|
||||
300: "var(--color-brand-300)",
|
||||
400: "var(--color-brand-400)",
|
||||
500: "var(--color-brand-500)",
|
||||
600: "var(--color-brand-600)",
|
||||
700: "var(--color-brand-700)",
|
||||
800: "var(--color-brand-800)",
|
||||
900: "var(--color-brand-900)",
|
||||
950: "var(--color-brand-950)",
|
||||
} satisfies ColorScale,
|
||||
},
|
||||
semantic: {
|
||||
primary: {
|
||||
50: "{orange.50}",
|
||||
100: "{orange.100}",
|
||||
200: "{orange.200}",
|
||||
300: "{orange.300}",
|
||||
400: "{orange.400}",
|
||||
500: "{orange.500}",
|
||||
600: "{orange.600}",
|
||||
700: "{orange.700}",
|
||||
800: "{orange.800}",
|
||||
900: "{orange.900}",
|
||||
950: "{orange.950}",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export function configurePrimeVue(app: App) {
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: ModManTheme,
|
||||
options: {
|
||||
darkModeSelector: ".dark",
|
||||
},
|
||||
},
|
||||
});
|
||||
// Always make dark mode for now
|
||||
document.documentElement.classList.add("dark");
|
||||
}
|
||||
30
src/ModManUi/src/components/SpinningLoader.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<svg viewBox="0 0 100 100" class="loading-spinner">
|
||||
<circle class="loading-spinner-box" cx="50" cy="50" r="40" fill="transparent" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.loading-spinner {
|
||||
width: 0.8em;
|
||||
height: 0.8em;
|
||||
display: inline-block;
|
||||
|
||||
animation: rotation 1s infinite linear;
|
||||
|
||||
.loading-spinner-box {
|
||||
stroke: currentColor;
|
||||
stroke-width: 10;
|
||||
stroke-dasharray: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
78
src/ModManUi/src/components/header/ModManHeader.vue
Normal file
@@ -0,0 +1,78 @@
|
||||
<script setup lang="ts">
|
||||
import Button from "primevue/button";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import IconArrowLeft from "../icons/IconArrowLeft.vue";
|
||||
import IconGear from "../icons/IconGear.vue";
|
||||
import { useRouteMeta } from "@/router/RouteMeta.ts";
|
||||
|
||||
const route = useRoute();
|
||||
const { headerTitle, routeNavigateBackTo } = useRouteMeta();
|
||||
const router = useRouter();
|
||||
|
||||
function onClickBack() {
|
||||
if (!routeNavigateBackTo.value) return;
|
||||
|
||||
router.push({ name: routeNavigateBackTo.value });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="header">
|
||||
<div class="header-section left">
|
||||
<Button
|
||||
v-if="routeNavigateBackTo"
|
||||
variant="text"
|
||||
severity="secondary"
|
||||
aria-label="Back"
|
||||
@click="onClickBack"
|
||||
>
|
||||
<template #icon>
|
||||
<IconArrowLeft class="icon" />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
<h1 class="title">
|
||||
<span v-if="typeof headerTitle === 'string'">{{ headerTitle }}</span>
|
||||
<component v-else :is="headerTitle" v-bind="route.params" />
|
||||
</h1>
|
||||
<div class="header-section right">
|
||||
<Button variant="text" severity="secondary" aria-label="Settings">
|
||||
<template #icon>
|
||||
<IconGear class="icon" />
|
||||
</template>
|
||||
</Button>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@style/colors";
|
||||
|
||||
.header {
|
||||
background-color: colors.$color-content-background;
|
||||
text-align: center;
|
||||
|
||||
padding: 0.25rem 0.25rem;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
border-bottom: 1px solid colors.$color-content-border;
|
||||
}
|
||||
|
||||
.header-section {
|
||||
width: 12em;
|
||||
|
||||
&.left {
|
||||
text-align: left;
|
||||
}
|
||||
&.right {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
9
src/ModManUi/src/components/icons/IconArrowLeft.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640">
|
||||
<!--!Font Awesome Free v7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
|
||||
<path
|
||||
d="M73.4 297.4C60.9 309.9 60.9 330.2 73.4 342.7L233.4 502.7C245.9 515.2 266.2 515.2 278.7 502.7C291.2 490.2 291.2 469.9 278.7 457.4L173.3 352L544 352C561.7 352 576 337.7 576 320C576 302.3 561.7 288 544 288L173.3 288L278.7 182.6C291.2 170.1 291.2 149.8 278.7 137.3C266.2 124.8 245.9 124.8 233.4 137.3L73.4 297.3z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
9
src/ModManUi/src/components/icons/IconGear.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640">
|
||||
<!--!Font Awesome Free v7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.-->
|
||||
<path
|
||||
d="M259.1 73.5C262.1 58.7 275.2 48 290.4 48L350.2 48C365.4 48 378.5 58.7 381.5 73.5L396 143.5C410.1 149.5 423.3 157.2 435.3 166.3L503.1 143.8C517.5 139 533.3 145 540.9 158.2L570.8 210C578.4 223.2 575.7 239.8 564.3 249.9L511 297.3C511.9 304.7 512.3 312.3 512.3 320C512.3 327.7 511.8 335.3 511 342.7L564.4 390.2C575.8 400.3 578.4 417 570.9 430.1L541 481.9C533.4 495 517.6 501.1 503.2 496.3L435.4 473.8C423.3 482.9 410.1 490.5 396.1 496.6L381.7 566.5C378.6 581.4 365.5 592 350.4 592L290.6 592C275.4 592 262.3 581.3 259.3 566.5L244.9 496.6C230.8 490.6 217.7 482.9 205.6 473.8L137.5 496.3C123.1 501.1 107.3 495.1 99.7 481.9L69.8 430.1C62.2 416.9 64.9 400.3 76.3 390.2L129.7 342.7C128.8 335.3 128.4 327.7 128.4 320C128.4 312.3 128.9 304.7 129.7 297.3L76.3 249.8C64.9 239.7 62.3 223 69.8 209.9L99.7 158.1C107.3 144.9 123.1 138.9 137.5 143.7L205.3 166.2C217.4 157.1 230.6 149.5 244.6 143.4L259.1 73.5zM320.3 400C364.5 399.8 400.2 363.9 400 319.7C399.8 275.5 363.9 239.8 319.7 240C275.5 240.2 239.8 276.1 240 320.3C240.2 364.5 276.1 400.2 320.3 400z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,109 +0,0 @@
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color: #0f0f0f;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
padding-top: 10vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: 0.75s;
|
||||
}
|
||||
|
||||
.logo.tauri:hover {
|
||||
filter: drop-shadow(0 0 2em #24c8db);
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
color: #0f0f0f;
|
||||
background-color: #ffffff;
|
||||
transition: border-color 0.25s;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: #396cd8;
|
||||
}
|
||||
button:active {
|
||||
border-color: #396cd8;
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#greet-input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
color: #f6f6f6;
|
||||
background-color: #2f2f2f;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #24c8db;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
color: #ffffff;
|
||||
background-color: #0f0f0f98;
|
||||
}
|
||||
button:active {
|
||||
background-color: #0f0f0f69;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
import "../public/favicon.ico";
|
||||
import "./main.scss";
|
||||
import "@style/main.scss";
|
||||
|
||||
import { createApp } from "vue";
|
||||
import { createPinia } from "pinia";
|
||||
import { configurePrimeVue } from "./PrimeVue.ts";
|
||||
|
||||
import App from "./App.vue";
|
||||
import { createModManRouter } from "./router/Router.ts";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(createPinia());
|
||||
app.use(createModManRouter());
|
||||
|
||||
configurePrimeVue(app);
|
||||
|
||||
app.mount("#app");
|
||||
|
||||
89
src/ModManUi/src/native/AssetBinds.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
export enum CommonAssetType {
|
||||
PHYS_PRESET = "PHYS_PRESET",
|
||||
XANIM = "XANIM",
|
||||
XMODEL = "XMODEL",
|
||||
MATERIAL = "MATERIAL",
|
||||
TECHNIQUE_SET = "TECHNIQUE_SET",
|
||||
IMAGE = "IMAGE",
|
||||
SOUND = "SOUND",
|
||||
SOUND_CURVE = "SOUND_CURVE",
|
||||
LOADED_SOUND = "LOADED_SOUND",
|
||||
CLIP_MAP = "CLIP_MAP",
|
||||
COM_WORLD = "COM_WORLD",
|
||||
GAME_WORLD_SP = "GAME_WORLD_SP",
|
||||
GAME_WORLD_MP = "GAME_WORLD_MP",
|
||||
MAP_ENTS = "MAP_ENTS",
|
||||
GFX_WORLD = "GFX_WORLD",
|
||||
LIGHT_DEF = "LIGHT_DEF",
|
||||
UI_MAP = "UI_MAP",
|
||||
FONT = "FONT",
|
||||
MENU_LIST = "MENU_LIST",
|
||||
MENU = "MENU",
|
||||
LOCALIZE_ENTRY = "LOCALIZE_ENTRY",
|
||||
WEAPON = "WEAPON",
|
||||
SOUND_DRIVER_GLOBALS = "SOUND_DRIVER_GLOBALS",
|
||||
FX = "FX",
|
||||
IMPACT_FX = "IMPACT_FX",
|
||||
AI_TYPE = "AI_TYPE",
|
||||
MP_TYPE = "MP_TYPE",
|
||||
CHARACTER = "CHARACTER",
|
||||
XMODEL_ALIAS = "XMODEL_ALIAS",
|
||||
RAW_FILE = "RAW_FILE",
|
||||
STRING_TABLE = "STRING_TABLE",
|
||||
XMODEL_PIECES = "XMODEL_PIECES",
|
||||
PHYS_COLL_MAP = "PHYS_COLL_MAP",
|
||||
XMODEL_SURFS = "XMODEL_SURFS",
|
||||
PIXEL_SHADER = "PIXEL_SHADER",
|
||||
VERTEX_SHADER = "VERTEX_SHADER",
|
||||
VERTEX_DECL = "VERTEX_DECL",
|
||||
FX_WORLD = "FX_WORLD",
|
||||
LEADERBOARD = "LEADERBOARD",
|
||||
STRUCTURED_DATA_DEF = "STRUCTURED_DATA_DEF",
|
||||
TRACER = "TRACER",
|
||||
VEHICLE = "VEHICLE",
|
||||
ADDON_MAP_ENTS = "ADDON_MAP_ENTS",
|
||||
GLASS_WORLD = "GLASS_WORLD",
|
||||
PATH_DATA = "PATH_DATA",
|
||||
VEHICLE_TRACK = "VEHICLE_TRACK",
|
||||
ATTACHMENT = "ATTACHMENT",
|
||||
SURFACE_FX = "SURFACE_FX",
|
||||
SCRIPT = "SCRIPT",
|
||||
PHYS_CONSTRAINTS = "PHYS_CONSTRAINTS",
|
||||
DESTRUCTIBLE_DEF = "DESTRUCTIBLE_DEF",
|
||||
SOUND_PATCH = "SOUND_PATCH",
|
||||
WEAPON_DEF = "WEAPON_DEF",
|
||||
WEAPON_VARIANT = "WEAPON_VARIANT",
|
||||
MP_BODY = "MP_BODY",
|
||||
MP_HEAD = "MP_HEAD",
|
||||
PACK_INDEX = "PACK_INDEX",
|
||||
XGLOBALS = "XGLOBALS",
|
||||
DDL = "DDL",
|
||||
GLASSES = "GLASSES",
|
||||
EMBLEM_SET = "EMBLEM_SET",
|
||||
FONT_ICON = "FONT_ICON",
|
||||
WEAPON_FULL = "WEAPON_FULL",
|
||||
ATTACHMENT_UNIQUE = "ATTACHMENT_UNIQUE",
|
||||
WEAPON_CAMO = "WEAPON_CAMO",
|
||||
KEY_VALUE_PAIRS = "KEY_VALUE_PAIRS",
|
||||
MEMORY_BLOCK = "MEMORY_BLOCK",
|
||||
SKINNED_VERTS = "SKINNED_VERTS",
|
||||
QDB = "QDB",
|
||||
SLUG = "SLUG",
|
||||
FOOTSTEP_TABLE = "FOOTSTEP_TABLE",
|
||||
FOOTSTEP_FX_TABLE = "FOOTSTEP_FX_TABLE",
|
||||
ZBARRIER = "ZBARRIER",
|
||||
}
|
||||
|
||||
export interface AssetDto {
|
||||
type: CommonAssetType;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ZoneAssetsDto {
|
||||
assets: AssetDto[];
|
||||
references: AssetDto[];
|
||||
}
|
||||
|
||||
export interface AssetBinds {
|
||||
getAssetsForZone(zoneName: string): Promise<ZoneAssetsDto | null>;
|
||||
}
|
||||
@@ -12,7 +12,7 @@ export interface SaveFileDialogDto {
|
||||
}
|
||||
|
||||
export interface DialogBinds {
|
||||
openFileDialog(options?: OpenFileDialogDto): string | null;
|
||||
saveFileDialog(options?: SaveFileDialogDto): string | null;
|
||||
folderSelectDialog(): string | null;
|
||||
openFileDialog(options?: OpenFileDialogDto): Promise<string | null>;
|
||||
saveFileDialog(options?: SaveFileDialogDto): Promise<string | null>;
|
||||
folderSelectDialog(): Promise<string | null>;
|
||||
}
|
||||
|
||||
15
src/ModManUi/src/native/UnlinkingBinds.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export interface ZoneUnlinkProgressDto {
|
||||
zoneName: string;
|
||||
/**
|
||||
* Between 0-100
|
||||
*/
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export interface UnlinkingBinds {
|
||||
unlinkZone(zoneName: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface UnlinkingEventMap {
|
||||
zoneUnlinkProgress: ZoneUnlinkProgressDto;
|
||||
}
|
||||
48
src/ModManUi/src/native/ZoneBinds.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
export enum GameId {
|
||||
IW3 = "IW3",
|
||||
IW4 = "IW4",
|
||||
IW5 = "IW5",
|
||||
T5 = "T5",
|
||||
T6 = "T6",
|
||||
}
|
||||
|
||||
export enum GamePlatform {
|
||||
PC = "PC",
|
||||
XBOX = "XBOX",
|
||||
PS3 = "PS3",
|
||||
}
|
||||
|
||||
export interface ZoneDto {
|
||||
name: string;
|
||||
filePath: string;
|
||||
game: GameId;
|
||||
platform: GamePlatform;
|
||||
}
|
||||
|
||||
export interface ZoneLoadProgressDto {
|
||||
zoneName: string;
|
||||
/**
|
||||
* Between 0-100
|
||||
*/
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export interface ZoneLoadedDto {
|
||||
zone: ZoneDto;
|
||||
}
|
||||
|
||||
export interface ZoneUnloadedDto {
|
||||
zoneName: string;
|
||||
}
|
||||
|
||||
export interface ZoneBinds {
|
||||
getZones(): Promise<ZoneDto[]>;
|
||||
loadFastFile(path: string): Promise<ZoneLoadedDto>;
|
||||
unloadZone(zoneName: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface ZoneEventMap {
|
||||
zoneLoadProgress: ZoneLoadProgressDto;
|
||||
zoneLoaded: ZoneLoadedDto;
|
||||
zoneUnloaded: ZoneUnloadedDto;
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
import type { AssetBinds } from "./AssetBinds";
|
||||
import type { DialogBinds } from "./DialogBinds";
|
||||
import type { UnlinkingBinds, UnlinkingEventMap } from "./UnlinkingBinds";
|
||||
import type { ZoneBinds, ZoneEventMap } from "./ZoneBinds";
|
||||
|
||||
export type NativeMethods = AssetBinds & DialogBinds & UnlinkingBinds & ZoneBinds;
|
||||
|
||||
export type NativeMethods = {
|
||||
greet(name: string): Promise<string>;
|
||||
} & DialogBinds;
|
||||
|
||||
interface NativeEventMap {
|
||||
greeting: string;
|
||||
}
|
||||
type NativeEventMap = UnlinkingEventMap & ZoneEventMap;
|
||||
|
||||
type WebViewExtensions = {
|
||||
webviewBinds: NativeMethods;
|
||||
|
||||
8
src/ModManUi/src/router/Page.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export const PAGE = {
|
||||
HOME: "Home",
|
||||
INSPECT: {
|
||||
SELECT_ZONE: "Inspect zone",
|
||||
ZONE_DETAILS: "Zone details",
|
||||
},
|
||||
OPTIONS: "Options",
|
||||
};
|
||||
30
src/ModManUi/src/router/RouteMeta.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { type Component, computed } from "vue";
|
||||
import { useRoute, type RouteLocationNormalizedGeneric } from "vue-router";
|
||||
|
||||
type HeaderTitleFunc = (route: RouteLocationNormalizedGeneric) => string;
|
||||
export interface TypedRouteMeta {
|
||||
backTo?: string;
|
||||
headerTitleFunc?: HeaderTitleFunc;
|
||||
headerTitleComponent?: Component;
|
||||
}
|
||||
|
||||
export type RouteMeta = TypedRouteMeta & Record<string, unknown>;
|
||||
|
||||
export function useRouteMeta() {
|
||||
const route = useRoute();
|
||||
const meta = computed<RouteMeta>(() => route.meta as RouteMeta);
|
||||
|
||||
const headerTitle = computed<string | Component>(() => {
|
||||
if (meta.value.headerTitleFunc) {
|
||||
return meta.value.headerTitleFunc(route);
|
||||
} else if (meta.value.headerTitleComponent) {
|
||||
return meta.value.headerTitleComponent;
|
||||
}
|
||||
|
||||
return String(route.name);
|
||||
});
|
||||
|
||||
const routeNavigateBackTo = computed<string | null>(() => meta.value.backTo ?? null);
|
||||
|
||||
return { headerTitle, routeNavigateBackTo };
|
||||
}
|
||||
36
src/ModManUi/src/router/Router.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { createRouter, createWebHashHistory, type RouteRecordRaw } from "vue-router";
|
||||
import { PAGE } from "./Page";
|
||||
import ZoneInspector from "@/view/inspect/ZoneInspector.vue";
|
||||
import InspectDetails from "@/view/inspect_details/InspectDetails.vue";
|
||||
import type { RouteMeta } from "./RouteMeta";
|
||||
import InspectDetailsHeader from "@/view/inspect_details/InspectDetailsHeader.vue";
|
||||
|
||||
const ROUTES: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
name: PAGE.INSPECT.SELECT_ZONE,
|
||||
component: ZoneInspector,
|
||||
},
|
||||
{
|
||||
path: ":zoneName",
|
||||
name: PAGE.INSPECT.ZONE_DETAILS,
|
||||
component: InspectDetails,
|
||||
meta: {
|
||||
backTo: PAGE.INSPECT.SELECT_ZONE,
|
||||
headerTitleComponent: InspectDetailsHeader,
|
||||
} satisfies RouteMeta,
|
||||
props: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export function createModManRouter() {
|
||||
return createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: ROUTES,
|
||||
});
|
||||
}
|
||||
32
src/ModManUi/src/stores/AssetStore.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import type { ZoneAssetsDto } from "@/native/AssetBinds";
|
||||
import { webviewBinds } from "@/native";
|
||||
|
||||
export const useAssetStore = defineStore("asset", () => {
|
||||
const zoneName = ref<string | null>(null);
|
||||
const assetsOfZone = ref<ZoneAssetsDto | null>(null);
|
||||
|
||||
const isLoading = computed(() => Boolean(assetsOfZone.value));
|
||||
const assetCount = computed(() => assetsOfZone.value?.assets.length ?? 0);
|
||||
const referenceCount = computed(() => assetsOfZone.value?.references.length ?? 0);
|
||||
|
||||
function loadAssetsForZone(newZoneName: string | null) {
|
||||
// Skip if assets are already loaded
|
||||
if (newZoneName === zoneName.value) return;
|
||||
|
||||
// Reset current state
|
||||
zoneName.value = newZoneName;
|
||||
assetsOfZone.value = null;
|
||||
|
||||
// Only load assets when there is a new zone name specified
|
||||
if (!newZoneName) return;
|
||||
webviewBinds.getAssetsForZone(newZoneName).then((res) => {
|
||||
if (zoneName.value === newZoneName) {
|
||||
assetsOfZone.value = res;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { zoneName, isLoading, assetsOfZone, assetCount, referenceCount, loadAssetsForZone };
|
||||
});
|
||||
31
src/ModManUi/src/stores/UnlinkingStore.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { webviewAddEventListener, webviewBinds } from "@/native";
|
||||
|
||||
export const useUnlinkingStore = defineStore("unlinking", () => {
|
||||
const isUnlinking = ref(false);
|
||||
const lastPercentage = ref<number>(0);
|
||||
const failureMessage = ref<string | null>(null);
|
||||
|
||||
function unlinkZone(zoneName: string) {
|
||||
isUnlinking.value = true;
|
||||
lastPercentage.value = 0;
|
||||
failureMessage.value = null;
|
||||
return webviewBinds
|
||||
.unlinkZone(zoneName)
|
||||
.catch((e: string) => {
|
||||
console.error("Failed to unlink fastfile:", e);
|
||||
failureMessage.value = e;
|
||||
})
|
||||
.finally(() => {
|
||||
isUnlinking.value = false;
|
||||
lastPercentage.value = 100;
|
||||
});
|
||||
}
|
||||
|
||||
webviewAddEventListener("zoneUnlinkProgress", (dto) => {
|
||||
lastPercentage.value = dto.percentage;
|
||||
});
|
||||
|
||||
return { isUnlinking, lastPercentage, unlinkZone };
|
||||
});
|
||||
77
src/ModManUi/src/stores/ZoneStore.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { computed, ref } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
import { webviewAddEventListener, webviewBinds } from "@/native";
|
||||
import type { ZoneDto, ZoneLoadedDto } from "@/native/ZoneBinds";
|
||||
|
||||
export const useZoneStore = defineStore("zone", () => {
|
||||
const loadedZones = ref<ZoneDto[]>([]);
|
||||
const zonesCurrentlyBeingLoaded = ref<string[]>([]);
|
||||
const lastPercentageByZoneName = ref<Record<string, number>>({});
|
||||
|
||||
const isLoadingZone = computed(() => zonesCurrentlyBeingLoaded.value.length > 0);
|
||||
|
||||
function loadFastFile(fastFilePath: string): Promise<ZoneLoadedDto> {
|
||||
const lastDirectorySeparator = fastFilePath.replace(/\\/g, "/").lastIndexOf("/");
|
||||
const lastDot = fastFilePath.lastIndexOf(".");
|
||||
const expectedZoneName = fastFilePath.substring(
|
||||
lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0,
|
||||
lastDot > lastDirectorySeparator ? lastDot : fastFilePath.length,
|
||||
);
|
||||
|
||||
zonesCurrentlyBeingLoaded.value.push(expectedZoneName);
|
||||
lastPercentageByZoneName.value[expectedZoneName] = 0;
|
||||
|
||||
return webviewBinds
|
||||
.loadFastFile(fastFilePath)
|
||||
.catch((e: string) => {
|
||||
console.error("Failed to load fastfile:", e);
|
||||
})
|
||||
.finally(() => {
|
||||
zonesCurrentlyBeingLoaded.value.splice(
|
||||
zonesCurrentlyBeingLoaded.value.indexOf(expectedZoneName),
|
||||
1,
|
||||
);
|
||||
delete lastPercentageByZoneName.value[expectedZoneName];
|
||||
}) as Promise<ZoneLoadedDto>;
|
||||
}
|
||||
|
||||
function getPercentageForZoneBeingLoaded(zoneName: string) {
|
||||
return lastPercentageByZoneName.value[zoneName] ?? 100;
|
||||
}
|
||||
|
||||
function getLoadedZoneByName(zoneName: string) {
|
||||
return loadedZones.value.find((zone) => zone.name === zoneName) ?? null;
|
||||
}
|
||||
|
||||
// Initially get all loaded zones
|
||||
webviewBinds.getZones().then((allZones) => {
|
||||
loadedZones.value = allZones;
|
||||
});
|
||||
|
||||
webviewAddEventListener("zoneLoadProgress", (dto) => {
|
||||
if (lastPercentageByZoneName.value[dto.zoneName] !== undefined) {
|
||||
lastPercentageByZoneName.value[dto.zoneName] = dto.percentage;
|
||||
}
|
||||
});
|
||||
|
||||
webviewAddEventListener("zoneLoaded", (dto) => {
|
||||
loadedZones.value.push(dto.zone);
|
||||
});
|
||||
|
||||
webviewAddEventListener("zoneUnloaded", (dto) => {
|
||||
const index = loadedZones.value.findIndex((zone) => zone.name === dto.zoneName);
|
||||
if (index >= 0) {
|
||||
loadedZones.value.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
loadedZones,
|
||||
zonesCurrentlyBeingLoaded,
|
||||
isLoadingZone,
|
||||
lastPercentageByZoneName,
|
||||
loadFastFile,
|
||||
getPercentageForZoneBeingLoaded,
|
||||
getLoadedZoneByName,
|
||||
};
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
import { ref, computed } from "vue";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export const useCounterStore = defineStore("counter", () => {
|
||||
const count = ref(0);
|
||||
const doubleCount = computed(() => count.value * 2);
|
||||
function increment() {
|
||||
count.value++;
|
||||
}
|
||||
|
||||
return { count, doubleCount, increment };
|
||||
});
|
||||
5
src/ModManUi/src/style/_colors.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
@use "colors_primitive";
|
||||
@forward "colors_primitive";
|
||||
|
||||
@use "colors_semantic";
|
||||
@forward "colors_semantic";
|
||||
278
src/ModManUi/src/style/_colors_primitive.scss
Normal file
@@ -0,0 +1,278 @@
|
||||
// Primitive colors are colors that have literal values and they do not change depending on the context.
|
||||
$color-brand-50: #f7ebdf;
|
||||
$color-brand-100: #f1dfca;
|
||||
$color-brand-200: #eaceae;
|
||||
$color-brand-300: #e0b685;
|
||||
$color-brand-400: #d59d5d;
|
||||
$color-brand-500: #cc8838;
|
||||
$color-brand-600: #b9772c;
|
||||
$color-brand-700: #784818;
|
||||
$color-brand-800: #573816;
|
||||
$color-brand-900: #3d2810;
|
||||
$color-brand-950: #291b0a;
|
||||
$color-brand: $color-brand-500;
|
||||
|
||||
// These colors are taken from PrimeVue Aura theme
|
||||
$color-emerald-50: var(--p-emerald-50);
|
||||
$color-emerald-100: var(--p-emerald-100);
|
||||
$color-emerald-200: var(--p-emerald-200);
|
||||
$color-emerald-300: var(--p-emerald-300);
|
||||
$color-emerald-400: var(--p-emerald-400);
|
||||
$color-emerald-500: var(--p-emerald-500);
|
||||
$color-emerald-600: var(--p-emerald-600);
|
||||
$color-emerald-700: var(--p-emerald-700);
|
||||
$color-emerald-800: var(--p-emerald-800);
|
||||
$color-emerald-900: var(--p-emerald-900);
|
||||
$color-emerald-950: var(--p-emerald-950);
|
||||
|
||||
$color-green-50: var(--p-green-50);
|
||||
$color-green-100: var(--p-green-100);
|
||||
$color-green-200: var(--p-green-200);
|
||||
$color-green-300: var(--p-green-300);
|
||||
$color-green-400: var(--p-green-400);
|
||||
$color-green-500: var(--p-green-500);
|
||||
$color-green-600: var(--p-green-600);
|
||||
$color-green-700: var(--p-green-700);
|
||||
$color-green-800: var(--p-green-800);
|
||||
$color-green-900: var(--p-green-900);
|
||||
$color-green-950: var(--p-green-950);
|
||||
|
||||
$color-lime-50: var(--p-lime-50);
|
||||
$color-lime-100: var(--p-lime-100);
|
||||
$color-lime-200: var(--p-lime-200);
|
||||
$color-lime-300: var(--p-lime-300);
|
||||
$color-lime-400: var(--p-lime-400);
|
||||
$color-lime-500: var(--p-lime-500);
|
||||
$color-lime-600: var(--p-lime-600);
|
||||
$color-lime-700: var(--p-lime-700);
|
||||
$color-lime-800: var(--p-lime-800);
|
||||
$color-lime-900: var(--p-lime-900);
|
||||
$color-lime-950: var(--p-lime-950);
|
||||
|
||||
$color-red-50: var(--p-red-50);
|
||||
$color-red-100: var(--p-red-100);
|
||||
$color-red-200: var(--p-red-200);
|
||||
$color-red-300: var(--p-red-300);
|
||||
$color-red-400: var(--p-red-400);
|
||||
$color-red-500: var(--p-red-500);
|
||||
$color-red-600: var(--p-red-600);
|
||||
$color-red-700: var(--p-red-700);
|
||||
$color-red-800: var(--p-red-800);
|
||||
$color-red-900: var(--p-red-900);
|
||||
$color-red-950: var(--p-red-950);
|
||||
|
||||
$color-orange-50: var(--p-orange-50);
|
||||
$color-orange-100: var(--p-orange-100);
|
||||
$color-orange-200: var(--p-orange-200);
|
||||
$color-orange-300: var(--p-orange-300);
|
||||
$color-orange-400: var(--p-orange-400);
|
||||
$color-orange-500: var(--p-orange-500);
|
||||
$color-orange-600: var(--p-orange-600);
|
||||
$color-orange-700: var(--p-orange-700);
|
||||
$color-orange-800: var(--p-orange-800);
|
||||
$color-orange-900: var(--p-orange-900);
|
||||
$color-orange-950: var(--p-orange-950);
|
||||
|
||||
$color-amber-50: var(--p-amber-50);
|
||||
$color-amber-100: var(--p-amber-100);
|
||||
$color-amber-200: var(--p-amber-200);
|
||||
$color-amber-300: var(--p-amber-300);
|
||||
$color-amber-400: var(--p-amber-400);
|
||||
$color-amber-500: var(--p-amber-500);
|
||||
$color-amber-600: var(--p-amber-600);
|
||||
$color-amber-700: var(--p-amber-700);
|
||||
$color-amber-800: var(--p-amber-800);
|
||||
$color-amber-900: var(--p-amber-900);
|
||||
$color-amber-950: var(--p-amber-950);
|
||||
|
||||
$color-yellow-50: var(--p-yellow-50);
|
||||
$color-yellow-100: var(--p-yellow-100);
|
||||
$color-yellow-200: var(--p-yellow-200);
|
||||
$color-yellow-300: var(--p-yellow-300);
|
||||
$color-yellow-400: var(--p-yellow-400);
|
||||
$color-yellow-500: var(--p-yellow-500);
|
||||
$color-yellow-600: var(--p-yellow-600);
|
||||
$color-yellow-700: var(--p-yellow-700);
|
||||
$color-yellow-800: var(--p-yellow-800);
|
||||
$color-yellow-900: var(--p-yellow-900);
|
||||
$color-yellow-950: var(--p-yellow-950);
|
||||
|
||||
$color-teal-50: var(--p-teal-50);
|
||||
$color-teal-100: var(--p-teal-100);
|
||||
$color-teal-200: var(--p-teal-200);
|
||||
$color-teal-300: var(--p-teal-300);
|
||||
$color-teal-400: var(--p-teal-400);
|
||||
$color-teal-500: var(--p-teal-500);
|
||||
$color-teal-600: var(--p-teal-600);
|
||||
$color-teal-700: var(--p-teal-700);
|
||||
$color-teal-800: var(--p-teal-800);
|
||||
$color-teal-900: var(--p-teal-900);
|
||||
$color-teal-950: var(--p-teal-950);
|
||||
|
||||
$color-cyan-50: var(--p-cyan-50);
|
||||
$color-cyan-100: var(--p-cyan-100);
|
||||
$color-cyan-200: var(--p-cyan-200);
|
||||
$color-cyan-300: var(--p-cyan-300);
|
||||
$color-cyan-400: var(--p-cyan-400);
|
||||
$color-cyan-500: var(--p-cyan-500);
|
||||
$color-cyan-600: var(--p-cyan-600);
|
||||
$color-cyan-700: var(--p-cyan-700);
|
||||
$color-cyan-800: var(--p-cyan-800);
|
||||
$color-cyan-900: var(--p-cyan-900);
|
||||
$color-cyan-950: var(--p-cyan-950);
|
||||
|
||||
$color-sky-50: var(--p-sky-50);
|
||||
$color-sky-100: var(--p-sky-100);
|
||||
$color-sky-200: var(--p-sky-200);
|
||||
$color-sky-300: var(--p-sky-300);
|
||||
$color-sky-400: var(--p-sky-400);
|
||||
$color-sky-500: var(--p-sky-500);
|
||||
$color-sky-600: var(--p-sky-600);
|
||||
$color-sky-700: var(--p-sky-700);
|
||||
$color-sky-800: var(--p-sky-800);
|
||||
$color-sky-900: var(--p-sky-900);
|
||||
$color-sky-950: var(--p-sky-950);
|
||||
|
||||
$color-blue-50: var(--p-blue-50);
|
||||
$color-blue-100: var(--p-blue-100);
|
||||
$color-blue-200: var(--p-blue-200);
|
||||
$color-blue-300: var(--p-blue-300);
|
||||
$color-blue-400: var(--p-blue-400);
|
||||
$color-blue-500: var(--p-blue-500);
|
||||
$color-blue-600: var(--p-blue-600);
|
||||
$color-blue-700: var(--p-blue-700);
|
||||
$color-blue-800: var(--p-blue-800);
|
||||
$color-blue-900: var(--p-blue-900);
|
||||
$color-blue-950: var(--p-blue-950);
|
||||
|
||||
$color-indigo-50: var(--p-indigo-50);
|
||||
$color-indigo-100: var(--p-indigo-100);
|
||||
$color-indigo-200: var(--p-indigo-200);
|
||||
$color-indigo-300: var(--p-indigo-300);
|
||||
$color-indigo-400: var(--p-indigo-400);
|
||||
$color-indigo-500: var(--p-indigo-500);
|
||||
$color-indigo-600: var(--p-indigo-600);
|
||||
$color-indigo-700: var(--p-indigo-700);
|
||||
$color-indigo-800: var(--p-indigo-800);
|
||||
$color-indigo-900: var(--p-indigo-900);
|
||||
$color-indigo-950: var(--p-indigo-950);
|
||||
|
||||
$color-violet-50: var(--p-violet-50);
|
||||
$color-violet-100: var(--p-violet-100);
|
||||
$color-violet-200: var(--p-violet-200);
|
||||
$color-violet-300: var(--p-violet-300);
|
||||
$color-violet-400: var(--p-violet-400);
|
||||
$color-violet-500: var(--p-violet-500);
|
||||
$color-violet-600: var(--p-violet-600);
|
||||
$color-violet-700: var(--p-violet-700);
|
||||
$color-violet-800: var(--p-violet-800);
|
||||
$color-violet-900: var(--p-violet-900);
|
||||
$color-violet-950: var(--p-violet-950);
|
||||
|
||||
$color-purple-50: var(--p-purple-50);
|
||||
$color-purple-100: var(--p-purple-100);
|
||||
$color-purple-200: var(--p-purple-200);
|
||||
$color-purple-300: var(--p-purple-300);
|
||||
$color-purple-400: var(--p-purple-400);
|
||||
$color-purple-500: var(--p-purple-500);
|
||||
$color-purple-600: var(--p-purple-600);
|
||||
$color-purple-700: var(--p-purple-700);
|
||||
$color-purple-800: var(--p-purple-800);
|
||||
$color-purple-900: var(--p-purple-900);
|
||||
$color-purple-950: var(--p-purple-950);
|
||||
|
||||
$color-fuchsia-50: var(--p-fuchsia-50);
|
||||
$color-fuchsia-100: var(--p-fuchsia-100);
|
||||
$color-fuchsia-200: var(--p-fuchsia-200);
|
||||
$color-fuchsia-300: var(--p-fuchsia-300);
|
||||
$color-fuchsia-400: var(--p-fuchsia-400);
|
||||
$color-fuchsia-500: var(--p-fuchsia-500);
|
||||
$color-fuchsia-600: var(--p-fuchsia-600);
|
||||
$color-fuchsia-700: var(--p-fuchsia-700);
|
||||
$color-fuchsia-800: var(--p-fuchsia-800);
|
||||
$color-fuchsia-900: var(--p-fuchsia-900);
|
||||
$color-fuchsia-950: var(--p-fuchsia-950);
|
||||
|
||||
$color-pink-50: var(--p-pink-50);
|
||||
$color-pink-100: var(--p-pink-100);
|
||||
$color-pink-200: var(--p-pink-200);
|
||||
$color-pink-300: var(--p-pink-300);
|
||||
$color-pink-400: var(--p-pink-400);
|
||||
$color-pink-500: var(--p-pink-500);
|
||||
$color-pink-600: var(--p-pink-600);
|
||||
$color-pink-700: var(--p-pink-700);
|
||||
$color-pink-800: var(--p-pink-800);
|
||||
$color-pink-900: var(--p-pink-900);
|
||||
$color-pink-950: var(--p-pink-950);
|
||||
|
||||
$color-rose-50: var(--p-rose-50);
|
||||
$color-rose-100: var(--p-rose-100);
|
||||
$color-rose-200: var(--p-rose-200);
|
||||
$color-rose-300: var(--p-rose-300);
|
||||
$color-rose-400: var(--p-rose-400);
|
||||
$color-rose-500: var(--p-rose-500);
|
||||
$color-rose-600: var(--p-rose-600);
|
||||
$color-rose-700: var(--p-rose-700);
|
||||
$color-rose-800: var(--p-rose-800);
|
||||
$color-rose-900: var(--p-rose-900);
|
||||
$color-rose-950: var(--p-rose-950);
|
||||
|
||||
$color-slate-50: var(--p-slate-50);
|
||||
$color-slate-100: var(--p-slate-100);
|
||||
$color-slate-200: var(--p-slate-200);
|
||||
$color-slate-300: var(--p-slate-300);
|
||||
$color-slate-400: var(--p-slate-400);
|
||||
$color-slate-500: var(--p-slate-500);
|
||||
$color-slate-600: var(--p-slate-600);
|
||||
$color-slate-700: var(--p-slate-700);
|
||||
$color-slate-800: var(--p-slate-800);
|
||||
$color-slate-900: var(--p-slate-900);
|
||||
$color-slate-950: var(--p-slate-950);
|
||||
|
||||
$color-gray-50: var(--p-gray-50);
|
||||
$color-gray-100: var(--p-gray-100);
|
||||
$color-gray-200: var(--p-gray-200);
|
||||
$color-gray-300: var(--p-gray-300);
|
||||
$color-gray-400: var(--p-gray-400);
|
||||
$color-gray-500: var(--p-gray-500);
|
||||
$color-gray-600: var(--p-gray-600);
|
||||
$color-gray-700: var(--p-gray-700);
|
||||
$color-gray-800: var(--p-gray-800);
|
||||
$color-gray-900: var(--p-gray-900);
|
||||
$color-gray-950: var(--p-gray-950);
|
||||
|
||||
$color-zinc-50: var(--p-zinc-50);
|
||||
$color-zinc-100: var(--p-zinc-100);
|
||||
$color-zinc-200: var(--p-zinc-200);
|
||||
$color-zinc-300: var(--p-zinc-300);
|
||||
$color-zinc-400: var(--p-zinc-400);
|
||||
$color-zinc-500: var(--p-zinc-500);
|
||||
$color-zinc-600: var(--p-zinc-600);
|
||||
$color-zinc-700: var(--p-zinc-700);
|
||||
$color-zinc-800: var(--p-zinc-800);
|
||||
$color-zinc-900: var(--p-zinc-900);
|
||||
$color-zinc-950: var(--p-zinc-950);
|
||||
|
||||
$color-neutral-50: var(--p-neutral-50);
|
||||
$color-neutral-100: var(--p-neutral-100);
|
||||
$color-neutral-200: var(--p-neutral-200);
|
||||
$color-neutral-300: var(--p-neutral-300);
|
||||
$color-neutral-400: var(--p-neutral-400);
|
||||
$color-neutral-500: var(--p-neutral-500);
|
||||
$color-neutral-600: var(--p-neutral-600);
|
||||
$color-neutral-700: var(--p-neutral-700);
|
||||
$color-neutral-800: var(--p-neutral-800);
|
||||
$color-neutral-900: var(--p-neutral-900);
|
||||
$color-neutral-950: var(--p-neutral-950);
|
||||
|
||||
$color-stone-50: var(--p-stone-50);
|
||||
$color-stone-100: var(--p-stone-100);
|
||||
$color-stone-200: var(--p-stone-200);
|
||||
$color-stone-300: var(--p-stone-300);
|
||||
$color-stone-400: var(--p-stone-400);
|
||||
$color-stone-500: var(--p-stone-500);
|
||||
$color-stone-600: var(--p-stone-600);
|
||||
$color-stone-700: var(--p-stone-700);
|
||||
$color-stone-800: var(--p-stone-800);
|
||||
$color-stone-900: var(--p-stone-900);
|
||||
$color-stone-950: var(--p-stone-950);
|
||||
28
src/ModManUi/src/style/_colors_semantic.scss
Normal file
@@ -0,0 +1,28 @@
|
||||
// Semantic colors are colors that may be different depending on the context.
|
||||
// They have to be used via CSS variables (which is what the SASS variables access).
|
||||
$color-surface-0: var(--color-surface-0);
|
||||
$color-surface-50: var(--color-surface-50);
|
||||
$color-surface-100: var(--color-surface-100);
|
||||
$color-surface-200: var(--color-surface-200);
|
||||
$color-surface-300: var(--color-surface-300);
|
||||
$color-surface-400: var(--color-surface-400);
|
||||
$color-surface-500: var(--color-surface-500);
|
||||
$color-surface-600: var(--color-surface-600);
|
||||
$color-surface-700: var(--color-surface-700);
|
||||
$color-surface-800: var(--color-surface-800);
|
||||
$color-surface-900: var(--color-surface-900);
|
||||
$color-surface-950: var(--color-surface-950);
|
||||
|
||||
$color-primary: var(--color-primary);
|
||||
$color-primary-contrast: var(--color-primary-contrast);
|
||||
$color-primary-hover: var(--color-primary-hover);
|
||||
$color-primary-active: var(--color-primary-active);
|
||||
|
||||
$color-content-background: var(--color-content-background);
|
||||
$color-content-hover-background: var(--color-content-hover-background);
|
||||
$color-content-border: var(--color-content-border);
|
||||
|
||||
$color-text: var(--color-text);
|
||||
$color-text-hover: var(--color-text-hover);
|
||||
$color-text-muted: var(--color-text-muted);
|
||||
$color-text-hover-muted: var(--color-text-hover-muted);
|
||||
7
src/ModManUi/src/style/_variables.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
$border-radius-xs: 0.125rem;
|
||||
$border-radius-sm: 0.25rem;
|
||||
$border-radius-md: 0.375rem;
|
||||
$border-radius-lg: 0.5rem;
|
||||
$border-radius-xl: 0.75rem;
|
||||
|
||||
$transition-duration: 0.2s;
|
||||
68
src/ModManUi/src/style/css_vars.scss
Normal file
@@ -0,0 +1,68 @@
|
||||
@use "colors";
|
||||
@use "colors_primitive";
|
||||
@use "sass:meta";
|
||||
@use "sass:string";
|
||||
|
||||
:root {
|
||||
@each $name, $value in meta.module-variables(colors_primitive) {
|
||||
--#{string.to-lower-case($name)}: #{$value};
|
||||
}
|
||||
}
|
||||
|
||||
:root:not(.dark) {
|
||||
--color-surface-0: #ffffff;
|
||||
--color-surface-50: #{colors.$color-slate-50};
|
||||
--color-surface-100: #{colors.$color-slate-100};
|
||||
--color-surface-200: #{colors.$color-slate-200};
|
||||
--color-surface-300: #{colors.$color-slate-300};
|
||||
--color-surface-400: #{colors.$color-slate-400};
|
||||
--color-surface-500: #{colors.$color-slate-500};
|
||||
--color-surface-600: #{colors.$color-slate-600};
|
||||
--color-surface-700: #{colors.$color-slate-700};
|
||||
--color-surface-800: #{colors.$color-slate-800};
|
||||
--color-surface-900: #{colors.$color-slate-900};
|
||||
--color-surface-950: #{colors.$color-slate-950};
|
||||
|
||||
--color-primary: #{colors.$color-brand-500};
|
||||
--color-primary-contrast: #ffffff;
|
||||
--color-primary-hover: var(--color-brand-600);
|
||||
--color-primary-active: var(--color-brand-700);
|
||||
|
||||
--color-content-background: var(--color-surface-0);
|
||||
--color-content-hover-background: var(--color-surface-100);
|
||||
--color-content-border: var(--color-surface-200);
|
||||
|
||||
--color-text: var(--color-surface-700);
|
||||
--color-text-hover: var(--color-surface-800);
|
||||
--color-text-muted: var(--color-surface-500);
|
||||
--color-text-hover-muted: var(--color-surface-600);
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
--color-surface-0: #ffffff;
|
||||
--color-surface-50: #{colors.$color-zinc-50};
|
||||
--color-surface-100: #{colors.$color-zinc-100};
|
||||
--color-surface-200: #{colors.$color-zinc-200};
|
||||
--color-surface-300: #{colors.$color-zinc-300};
|
||||
--color-surface-400: #{colors.$color-zinc-400};
|
||||
--color-surface-500: #{colors.$color-zinc-500};
|
||||
--color-surface-600: #{colors.$color-zinc-600};
|
||||
--color-surface-700: #{colors.$color-zinc-700};
|
||||
--color-surface-800: #{colors.$color-zinc-800};
|
||||
--color-surface-900: #{colors.$color-zinc-900};
|
||||
--color-surface-950: #{colors.$color-zinc-950};
|
||||
|
||||
--color-primary: #{colors.$color-brand-400};
|
||||
--color-primary-contrast: var(--color-surface-900);
|
||||
--color-primary-hover: var(--color-brand-300);
|
||||
--color-primary-active: var(--color-brand-200);
|
||||
|
||||
--color-content-background: var(--color-surface-900);
|
||||
--color-content-hover-background: var(--color-surface-800);
|
||||
--color-content-border: var(--color-surface-700);
|
||||
|
||||
--color-text: var(--color-surface-0);
|
||||
--color-text-hover: var(--color-surface-0);
|
||||
--color-text-muted: var(--color-surface-400);
|
||||
--color-text-hover-muted: var(--color-surface-300);
|
||||
}
|
||||
36
src/ModManUi/src/style/main.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
@use "colors";
|
||||
@use "css_vars";
|
||||
@use "utils.scss";
|
||||
|
||||
@import "@fontsource/inter/latin-300.css";
|
||||
@import "@fontsource/inter/latin-300-italic.css";
|
||||
@import "@fontsource/inter/latin-400.css";
|
||||
@import "@fontsource/inter/latin-400-italic.css";
|
||||
@import "@fontsource/inter/latin-600.css";
|
||||
@import "@fontsource/inter/latin-600-italic.css";
|
||||
@import "@fontsource/inter/latin-700.css";
|
||||
@import "@fontsource/inter/latin-700-italic.css";
|
||||
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color: colors.$color-text;
|
||||
background-color: colors.$color-content-background;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
3
src/ModManUi/src/style/utils.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.icon {
|
||||
height: 1lh;
|
||||
}
|
||||
85
src/ModManUi/src/utils/AssetTypeName.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { CommonAssetType } from "@/native/AssetBinds";
|
||||
|
||||
const LOOKUP_CAPITALIZED: Record<CommonAssetType, string> = {
|
||||
[CommonAssetType.PHYS_PRESET]: "Phys preset",
|
||||
[CommonAssetType.XANIM]: "XAnim",
|
||||
[CommonAssetType.XMODEL]: "XModel",
|
||||
[CommonAssetType.MATERIAL]: "Material",
|
||||
[CommonAssetType.TECHNIQUE_SET]: "Technique set",
|
||||
[CommonAssetType.IMAGE]: "Image",
|
||||
[CommonAssetType.SOUND]: "Sound",
|
||||
[CommonAssetType.SOUND_CURVE]: "Sound curve",
|
||||
[CommonAssetType.LOADED_SOUND]: "Loaded sound",
|
||||
[CommonAssetType.CLIP_MAP]: "Clip map",
|
||||
[CommonAssetType.COM_WORLD]: "Com world",
|
||||
[CommonAssetType.GAME_WORLD_SP]: "Game world SP",
|
||||
[CommonAssetType.GAME_WORLD_MP]: "Game world MP",
|
||||
[CommonAssetType.MAP_ENTS]: "Map ents",
|
||||
[CommonAssetType.GFX_WORLD]: "Gfx world",
|
||||
[CommonAssetType.LIGHT_DEF]: "Light def",
|
||||
[CommonAssetType.UI_MAP]: "UI map",
|
||||
[CommonAssetType.FONT]: "Font",
|
||||
[CommonAssetType.MENU_LIST]: "Menu list",
|
||||
[CommonAssetType.MENU]: "Menu",
|
||||
[CommonAssetType.LOCALIZE_ENTRY]: "Localize entry",
|
||||
[CommonAssetType.WEAPON]: "Weapon",
|
||||
[CommonAssetType.SOUND_DRIVER_GLOBALS]: "Sound driver globals",
|
||||
[CommonAssetType.FX]: "FX",
|
||||
[CommonAssetType.IMPACT_FX]: "Impact FX",
|
||||
[CommonAssetType.AI_TYPE]: "AI type",
|
||||
[CommonAssetType.MP_TYPE]: "MP type",
|
||||
[CommonAssetType.CHARACTER]: "Character",
|
||||
[CommonAssetType.XMODEL_ALIAS]: "XModel alias",
|
||||
[CommonAssetType.RAW_FILE]: "Raw file",
|
||||
[CommonAssetType.STRING_TABLE]: "String table",
|
||||
[CommonAssetType.XMODEL_PIECES]: "XModel pieces",
|
||||
[CommonAssetType.PHYS_COLL_MAP]: "Phys coll map",
|
||||
[CommonAssetType.XMODEL_SURFS]: "XModel surfs",
|
||||
[CommonAssetType.PIXEL_SHADER]: "Pixel shader",
|
||||
[CommonAssetType.VERTEX_SHADER]: "Vertex shader",
|
||||
[CommonAssetType.VERTEX_DECL]: "Vertex decl",
|
||||
[CommonAssetType.FX_WORLD]: "FX world",
|
||||
[CommonAssetType.LEADERBOARD]: "Leaderboard",
|
||||
[CommonAssetType.STRUCTURED_DATA_DEF]: "Structured data def",
|
||||
[CommonAssetType.TRACER]: "Tracer",
|
||||
[CommonAssetType.VEHICLE]: "Vehicle",
|
||||
[CommonAssetType.ADDON_MAP_ENTS]: "Addon map ents",
|
||||
[CommonAssetType.GLASS_WORLD]: "Glass world",
|
||||
[CommonAssetType.PATH_DATA]: "Path data",
|
||||
[CommonAssetType.VEHICLE_TRACK]: "Vehicle track",
|
||||
[CommonAssetType.ATTACHMENT]: "Attachment",
|
||||
[CommonAssetType.SURFACE_FX]: "Surface FX",
|
||||
[CommonAssetType.SCRIPT]: "Script",
|
||||
[CommonAssetType.PHYS_CONSTRAINTS]: "Phys constraints",
|
||||
[CommonAssetType.DESTRUCTIBLE_DEF]: "Destructible def",
|
||||
[CommonAssetType.SOUND_PATCH]: "Sound patch",
|
||||
[CommonAssetType.WEAPON_DEF]: "Weapon def",
|
||||
[CommonAssetType.WEAPON_VARIANT]: "Weapon variant",
|
||||
[CommonAssetType.MP_BODY]: "MP body",
|
||||
[CommonAssetType.MP_HEAD]: "MP head",
|
||||
[CommonAssetType.PACK_INDEX]: "Pack index",
|
||||
[CommonAssetType.XGLOBALS]: "XGlobals",
|
||||
[CommonAssetType.DDL]: "DDL",
|
||||
[CommonAssetType.GLASSES]: "Glasses",
|
||||
[CommonAssetType.EMBLEM_SET]: "Emblem set",
|
||||
[CommonAssetType.FONT_ICON]: "Font icon",
|
||||
[CommonAssetType.WEAPON_FULL]: "Weapon full",
|
||||
[CommonAssetType.ATTACHMENT_UNIQUE]: "Attachment unique",
|
||||
[CommonAssetType.WEAPON_CAMO]: "Weapon camo",
|
||||
[CommonAssetType.KEY_VALUE_PAIRS]: "Key value pairs",
|
||||
[CommonAssetType.MEMORY_BLOCK]: "Memory block",
|
||||
[CommonAssetType.SKINNED_VERTS]: "Skinned verts",
|
||||
[CommonAssetType.QDB]: "Qdb",
|
||||
[CommonAssetType.SLUG]: "Slug",
|
||||
[CommonAssetType.FOOTSTEP_TABLE]: "Footstep table",
|
||||
[CommonAssetType.FOOTSTEP_FX_TABLE]: "Footstep FX table",
|
||||
[CommonAssetType.ZBARRIER]: "ZBarrier",
|
||||
};
|
||||
|
||||
export function getAssetTypeNameCapitalized(assetType: CommonAssetType): string {
|
||||
return LOOKUP_CAPITALIZED[assetType];
|
||||
}
|
||||
|
||||
export function getAssetTypeNameLower(assetType: CommonAssetType): string {
|
||||
return getAssetTypeNameCapitalized(assetType).toLocaleLowerCase();
|
||||
}
|
||||
52
src/ModManUi/src/view/inspect/ZoneInspector.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from "vue";
|
||||
import { useZoneStore } from "@/stores/ZoneStore";
|
||||
import ZoneInspectorDetails from "./ZoneInspectorDetails.vue";
|
||||
import ZoneInspectorZoneList from "./ZoneInspectorZoneList.vue";
|
||||
import { useAssetStore } from "@/stores/AssetStore";
|
||||
|
||||
const assetStore = useAssetStore();
|
||||
const zoneStore = useZoneStore();
|
||||
|
||||
const selectedZone = ref<string | null>(null);
|
||||
|
||||
// Make sure we preselect the zone that was last loaded assets for
|
||||
// if there is one
|
||||
if (
|
||||
assetStore.zoneName &&
|
||||
zoneStore.loadedZones.findIndex((zone) => zone.name === assetStore.zoneName) >= 0
|
||||
) {
|
||||
selectedZone.value = assetStore.zoneName;
|
||||
}
|
||||
|
||||
watch(
|
||||
() => zoneStore.loadedZones,
|
||||
(newValue) => {
|
||||
// Reset selection if unloaded
|
||||
if (!selectedZone.value) return;
|
||||
if (newValue.findIndex((loadedZone) => loadedZone.name === selectedZone.value) >= 0) return;
|
||||
selectedZone.value = null;
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="zone-inspector">
|
||||
<ZoneInspectorZoneList v-model:selected-zone="selectedZone" />
|
||||
<ZoneInspectorDetails :selected-zone="selectedZone" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.zone-inspector {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
|
||||
& > * {
|
||||
width: 50%;
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
151
src/ModManUi/src/view/inspect/ZoneInspectorDetails.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
import Button from "primevue/button";
|
||||
import Tag from "primevue/tag";
|
||||
import MeterGroup, { type MeterItem } from "primevue/metergroup";
|
||||
import Skeleton from "primevue/skeleton";
|
||||
import { dt } from "@primeuix/themes";
|
||||
import type { ZoneDto } from "@/native/ZoneBinds";
|
||||
import { useZoneStore } from "@/stores/ZoneStore";
|
||||
import { computed, watch } from "vue";
|
||||
import type { CommonAssetType } from "@/native/AssetBinds";
|
||||
import { getAssetTypeNameCapitalized } from "@/utils/AssetTypeName";
|
||||
import { useRouter } from "vue-router";
|
||||
import { PAGE } from "@/router/Page";
|
||||
import { useAssetStore } from "@/stores/AssetStore";
|
||||
import { storeToRefs } from "pinia";
|
||||
|
||||
const assetStore = useAssetStore();
|
||||
const zoneStore = useZoneStore();
|
||||
|
||||
const { assetsOfZone, assetCount, referenceCount } = storeToRefs(assetStore);
|
||||
|
||||
const props = defineProps<{
|
||||
selectedZone: string | null;
|
||||
}>();
|
||||
|
||||
const METER_COLORS = [
|
||||
dt("blue.600"),
|
||||
dt("red.600"),
|
||||
dt("yellow.600"),
|
||||
dt("green.600"),
|
||||
dt("purple.600"),
|
||||
dt("orange.600"),
|
||||
dt("teal.600"),
|
||||
dt("lime.600"),
|
||||
dt("pink.600"),
|
||||
];
|
||||
const meterItems = computed<MeterItem[]>(() => {
|
||||
const assetTypeCounts: Partial<Record<CommonAssetType, number>> = {};
|
||||
|
||||
for (const asset of assetsOfZone.value?.assets ?? []) {
|
||||
if (!assetTypeCounts[asset.type]) {
|
||||
assetTypeCounts[asset.type] = 1;
|
||||
} else {
|
||||
assetTypeCounts[asset.type]!++;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not display asset types with under 3 percent representation
|
||||
const minItemCountForDisplay = Math.floor(assetCount.value * 0.03);
|
||||
|
||||
const assetMeterItems: MeterItem[] = Object.entries(assetTypeCounts)
|
||||
.filter((entry) => entry[1] > minItemCountForDisplay)
|
||||
.sort((e0, e1) => e1[1] - e0[1])
|
||||
.map((entry) => ({
|
||||
label: getAssetTypeNameCapitalized(entry[0] as CommonAssetType),
|
||||
value: Math.round((entry[1] / assetCount.value) * 100),
|
||||
}));
|
||||
|
||||
// Since the PrimeVue component rounds to percent we want to fill up the bar completely
|
||||
const otherCount = 100 - assetMeterItems.reduce((val, entry) => val + entry.value, 0);
|
||||
|
||||
if (otherCount > 0) {
|
||||
assetMeterItems.push({
|
||||
label: "Other",
|
||||
value: otherCount,
|
||||
});
|
||||
}
|
||||
|
||||
return assetMeterItems.map(
|
||||
(item, index) =>
|
||||
({
|
||||
...item,
|
||||
color: METER_COLORS[index % METER_COLORS.length],
|
||||
}) satisfies MeterItem,
|
||||
);
|
||||
});
|
||||
|
||||
const selectedZoneDetails = computed<ZoneDto | null>(() =>
|
||||
props.selectedZone ? zoneStore.getLoadedZoneByName(props.selectedZone) : null,
|
||||
);
|
||||
|
||||
const router = useRouter();
|
||||
function onClickShowAssets() {
|
||||
if (!props.selectedZone) return;
|
||||
|
||||
router.push({
|
||||
name: PAGE.INSPECT.ZONE_DETAILS,
|
||||
params: {
|
||||
zoneName: props.selectedZone,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.selectedZone,
|
||||
(newValue) => {
|
||||
assetStore.loadAssetsForZone(newValue);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="zone-details">
|
||||
<h2>{{ selectedZone ?? "No zone selected" }}</h2>
|
||||
<Button label="Show assets" :disabled="!selectedZone" @click="onClickShowAssets" />
|
||||
<div v-if="selectedZoneDetails" class="zone-tags">
|
||||
<Tag :value="selectedZoneDetails.game" />
|
||||
<Tag :value="selectedZoneDetails.platform" />
|
||||
</div>
|
||||
<div class="zone-assets">
|
||||
<template v-if="assetsOfZone">
|
||||
<div>{{ assetCount }} assets</div>
|
||||
<div>{{ referenceCount }} references</div>
|
||||
<MeterGroup class="asset-meter" :value="meterItems" />
|
||||
</template>
|
||||
<template v-else-if="selectedZone">
|
||||
<Skeleton class="count-skeleton" width="10em" />
|
||||
<Skeleton class="count-skeleton" width="10em" />
|
||||
<Skeleton class="count-skeleton" height="0.5lh" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.zone-tags {
|
||||
display: flex;
|
||||
margin-top: 0.5em;
|
||||
column-gap: 0.5em;
|
||||
row-gap: 0.5em;
|
||||
}
|
||||
|
||||
.zone-assets {
|
||||
margin-top: 0.5em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.asset-meter {
|
||||
padding-top: 0.5em;
|
||||
font-size: 0.8em;
|
||||
line-height: 1.25;
|
||||
--p-metergroup-gap: 1em;
|
||||
--p-metergroup-label-list-horizontal-gap: 0.8em;
|
||||
}
|
||||
|
||||
.count-skeleton {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
</style>
|
||||
111
src/ModManUi/src/view/inspect/ZoneInspectorZoneList.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<script setup lang="ts">
|
||||
import Button from "primevue/button";
|
||||
import ProgressBar from "primevue/progressbar";
|
||||
import Listbox from "primevue/listbox";
|
||||
import { computed } from "vue";
|
||||
import { useZoneStore } from "@/stores/ZoneStore";
|
||||
import { webviewBinds } from "@/native";
|
||||
|
||||
interface SelectableZone {
|
||||
isLoading: boolean;
|
||||
zoneName: string;
|
||||
}
|
||||
|
||||
const zoneStore = useZoneStore();
|
||||
const selectedZone = defineModel<string | null>("selectedZone");
|
||||
|
||||
async function openFastFileSelect() {
|
||||
return await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] });
|
||||
}
|
||||
|
||||
async function onOpenFastFileClick() {
|
||||
const fastFilePath = await openFastFileSelect();
|
||||
if (!fastFilePath) return;
|
||||
|
||||
zoneStore.loadFastFile(fastFilePath);
|
||||
}
|
||||
|
||||
const availableZones = computed<SelectableZone[]>(() => {
|
||||
const result = [
|
||||
...zoneStore.zonesCurrentlyBeingLoaded.map(
|
||||
(zoneBeingLoaded) =>
|
||||
({
|
||||
isLoading: true,
|
||||
zoneName: zoneBeingLoaded,
|
||||
}) satisfies SelectableZone,
|
||||
),
|
||||
...zoneStore.loadedZones.map(
|
||||
(loadedZone) =>
|
||||
({
|
||||
isLoading: false,
|
||||
zoneName: loadedZone.name,
|
||||
}) satisfies SelectableZone,
|
||||
),
|
||||
];
|
||||
|
||||
result.sort((a, b) => a.zoneName.localeCompare(b.zoneName));
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
function onUnloadClicked() {
|
||||
if (!selectedZone.value) return;
|
||||
|
||||
webviewBinds.unloadZone(selectedZone.value).catch((e: string) => {
|
||||
console.error("Failed to unload zone:", e);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="zone-list">
|
||||
<div class="zone-list-actions">
|
||||
<Button label="Load fastfile" @click="onOpenFastFileClick" />
|
||||
<Button label="Unload" :disabled="!selectedZone" @click="onUnloadClicked" />
|
||||
</div>
|
||||
<Listbox
|
||||
v-model="selectedZone"
|
||||
:options="availableZones"
|
||||
option-disabled="isLoading"
|
||||
option-value="zoneName"
|
||||
data-key="zoneName"
|
||||
emptyMessage="No zones loaded"
|
||||
class="zone"
|
||||
>
|
||||
<template #option="{ option }: { option: SelectableZone }">
|
||||
<div class="selectable-zone">
|
||||
<span>{{ option.zoneName }}</span>
|
||||
<ProgressBar
|
||||
v-if="option.isLoading"
|
||||
class="zone-progressbar"
|
||||
:value="zoneStore.getPercentageForZoneBeingLoaded(option.zoneName)"
|
||||
:show-value="false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Listbox>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.zone-list-actions {
|
||||
display: flex;
|
||||
column-gap: 0.25em;
|
||||
row-gap: 0.25em;
|
||||
padding: 0.25em 0;
|
||||
}
|
||||
|
||||
.selectable-zone {
|
||||
position: relative;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.zone-progressbar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
height: 0.2em;
|
||||
}
|
||||
</style>
|
||||
87
src/ModManUi/src/view/inspect_details/InspectDetails.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import Skeleton from "primevue/skeleton";
|
||||
import { useAssetStore } from "@/stores/AssetStore";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { ref, watch } from "vue";
|
||||
import InspectPreview from "./components/InspectPreview.vue";
|
||||
import InspectAssetDetails from "./components/InspectAssetDetails.vue";
|
||||
import InspectZoneAssets from "./components/InspectZoneAssets.vue";
|
||||
import type { AssetDto } from "@/native/AssetBinds.ts";
|
||||
|
||||
const assetStore = useAssetStore();
|
||||
|
||||
const { assetsOfZone } = storeToRefs(assetStore);
|
||||
|
||||
const props = defineProps<{
|
||||
zoneName: string;
|
||||
}>();
|
||||
|
||||
const selectedAsset = ref<AssetDto | null>(null);
|
||||
|
||||
watch(
|
||||
() => props.zoneName,
|
||||
(newValue) => {
|
||||
assetStore.loadAssetsForZone(newValue);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="inspect-details">
|
||||
<template v-if="assetsOfZone">
|
||||
<InspectPreview class="inspect-area-preview" />
|
||||
<InspectAssetDetails :selected-asset="selectedAsset" class="inspect-area-details" />
|
||||
<InspectZoneAssets
|
||||
v-model:selected-asset="selectedAsset"
|
||||
:assets="assetsOfZone.assets"
|
||||
class="inspect-area-assets"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="skeleton-wrapper">
|
||||
<Skeleton class="count-skeleton" width="100%" height="100%" />
|
||||
</div>
|
||||
<div class="skeleton-wrapper">
|
||||
<Skeleton class="count-skeleton" width="100%" height="100%" />
|
||||
</div>
|
||||
<div class="skeleton-wrapper list">
|
||||
<Skeleton v-for="i in 3" :key="i" class="count-skeleton" width="80%" height="1em" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.inspect-details {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 30% 1fr;
|
||||
grid-template-rows: 30% 1fr;
|
||||
grid-template-areas:
|
||||
"preview details"
|
||||
"assets details";
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.inspect-area-preview {
|
||||
grid-area: preview;
|
||||
}
|
||||
|
||||
.inspect-area-details {
|
||||
grid-area: details;
|
||||
}
|
||||
|
||||
.inspect-area-assets {
|
||||
grid-area: assets;
|
||||
}
|
||||
|
||||
.skeleton-wrapper {
|
||||
&.list > *:not(:first-child) {
|
||||
margin-top: calc(1lh - 1em);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import type { ZoneDto } from "@/native/ZoneBinds.ts";
|
||||
import { useZoneStore } from "@/stores/ZoneStore.ts";
|
||||
import Tag from "primevue/tag";
|
||||
|
||||
const zoneStore = useZoneStore();
|
||||
const props = defineProps<{
|
||||
zoneName: string;
|
||||
}>();
|
||||
|
||||
const zoneDetails = computed<ZoneDto | null>(() =>
|
||||
props.zoneName ? zoneStore.getLoadedZoneByName(props.zoneName) : null,
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<span>
|
||||
<span>Inspect zone: {{ zoneName }}</span>
|
||||
<template v-if="zoneDetails">
|
||||
<Tag class="zone-header-tag" :value="zoneDetails.game" severity="secondary" />
|
||||
<Tag class="zone-header-tag" :value="zoneDetails.platform" severity="secondary" />
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.zone-header-tag {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import type { AssetDto } from "@/native/AssetBinds.ts";
|
||||
import { getAssetTypeNameCapitalized } from "@/utils/AssetTypeName.ts";
|
||||
|
||||
defineProps<{
|
||||
asset: AssetDto;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="asset-option">
|
||||
<span class="asset-type">{{ getAssetTypeNameCapitalized(asset.type) }}</span>
|
||||
<span class="asset-name">{{ asset.name }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use "@style/colors";
|
||||
|
||||
.asset-option {
|
||||
font-size: 0.95rem;
|
||||
max-width: 100%;
|
||||
overflow-x: clip;
|
||||
white-space: nowrap;
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
|
||||
.asset-type {
|
||||
display: inline-block;
|
||||
color: colors.$color-text-muted;
|
||||
min-width: 8em;
|
||||
}
|
||||
|
||||
.asset-name {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
</style>
|
||||