mirror of
https://github.com/4sval/FModel.git
synced 2026-03-22 01:34:37 -05:00
Compare commits
No commits in common. "master" and "3.1.1.2" have entirely different histories.
164
.editorconfig
164
.editorconfig
|
|
@ -1,164 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
# C# files
|
||||
[*.cs]
|
||||
indent_size = 4
|
||||
# New line preferences
|
||||
csharp_new_line_before_open_brace = all
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_within_query_expression_clauses = true
|
||||
|
||||
# Code files
|
||||
[*.{cs,csx,vb,vbx}]
|
||||
indent_size = 4
|
||||
|
||||
# Indentation preferences
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_switch_labels = true
|
||||
csharp_indent_labels = one_less_than_current
|
||||
|
||||
# avoid this. unless absolutely necessary
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
|
||||
# only use var when it's obvious what the variable type is
|
||||
csharp_style_var_for_built_in_types = false:none
|
||||
csharp_style_var_when_type_is_apparent = false:none
|
||||
csharp_style_var_elsewhere = false:suggestion
|
||||
|
||||
# use language keywords instead of BCL types
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
||||
|
||||
# name all constant fields using PascalCase
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
||||
|
||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
||||
|
||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
||||
|
||||
# internal and private fields should be _camelCase
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
|
||||
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
|
||||
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
|
||||
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
|
||||
|
||||
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
|
||||
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
|
||||
|
||||
# Code style defaults
|
||||
dotnet_sort_system_directives_first = true
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = false
|
||||
|
||||
# Expression-level preferences
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
|
||||
# Expression-bodied members
|
||||
csharp_style_expression_bodied_methods = false:none
|
||||
csharp_style_expression_bodied_constructors = false:none
|
||||
csharp_style_expression_bodied_operators = false:none
|
||||
csharp_style_expression_bodied_properties = true:none
|
||||
csharp_style_expression_bodied_indexers = true:none
|
||||
csharp_style_expression_bodied_accessors = true:none
|
||||
|
||||
# Pattern matching
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
|
||||
# Null checking preferences
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
|
||||
# Space preferences
|
||||
csharp_space_after_cast = true
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = do_not_ignore
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses = false
|
||||
csharp_space_between_square_brackets = false
|
||||
|
||||
[*.{asm,inc}]
|
||||
indent_size = 8
|
||||
|
||||
# Visual Studio Solution Files
|
||||
[*.sln]
|
||||
indent_style = tab
|
||||
|
||||
# Visual Studio XML Project Files
|
||||
[*.{csproj,vbproj,vcxproj.filters,proj,projitems,shproj}]
|
||||
indent_size = 2
|
||||
|
||||
# XML Configuration Files
|
||||
[*.{xml,config,props,targets,nuspec,resx,ruleset,vsixmanifest,vsct}]
|
||||
indent_size = 2
|
||||
|
||||
[CMakeLists.txt]
|
||||
indent_size = 2
|
||||
|
||||
# Makefiles
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# Batch Files
|
||||
[*.{cmd,bat}]
|
||||
indent_size = 2
|
||||
end_of_line = crlf
|
||||
|
||||
# Bash Files
|
||||
[*.sh]
|
||||
end_of_line = lf
|
||||
|
||||
# Web Files
|
||||
[*.{htm,html,js,jsm,ts,tsx,css,sass,scss,less,pcss,svg,vue}]
|
||||
indent_size = 2
|
||||
|
||||
# Markdown Files
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# JSON Files
|
||||
[*.{json,json5,webmanifest}]
|
||||
indent_size = 2
|
||||
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
|
|
@ -1 +0,0 @@
|
|||
custom: "https://fmodel.app/donate"
|
||||
45
.github/ISSUE_TEMPLATE/bug.yml
vendored
45
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
|
@ -1,45 +0,0 @@
|
|||
name: Bug Report
|
||||
description: File a bug report
|
||||
title: "Bug Title"
|
||||
labels: [bug]
|
||||
assignees:
|
||||
- iAmAsval
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report! Keep in mind that screenshots and log files help us a lot so don't forget to provide one or both of those (drag and drop files in a text area).
|
||||
Your bug report will be closed without explanation if you don't follow the following rules:
|
||||
- Bad bug explanation will result in bad support and probably on a negative tone
|
||||
- This template shouldn't be used to ask how to use FModel or a certain feature FModel provides
|
||||
- Bug reports must always use the latest FModel with the latest available version of the game you use
|
||||
- If you can't load files, it's probably because of your AES key, no need to file a report
|
||||
- We absolutely do not support modding
|
||||
- type: input
|
||||
id: game
|
||||
attributes:
|
||||
label: Game
|
||||
placeholder: ex. Fortnite, Valorant, ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: error
|
||||
attributes:
|
||||
label: Error
|
||||
description: Tell us what FModel says about the error, from the console and / or the log file
|
||||
placeholder: ex. [ERR] Could not export 'EditorClientAssetRegistry.bin'
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction steps
|
||||
description: How do you trigger this bug? Please walk us through it step by step.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
|
|
@ -1,5 +0,0 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Discord Server
|
||||
url: https://fmodel.app/discord
|
||||
about: Please ask and answer questions here.
|
||||
22
.github/ISSUE_TEMPLATE/feature.yml
vendored
22
.github/ISSUE_TEMPLATE/feature.yml
vendored
|
|
@ -1,22 +0,0 @@
|
|||
name: Feature Request
|
||||
description: Submit a new feature request
|
||||
title: "Feature Title"
|
||||
labels: [suggestion]
|
||||
assignees:
|
||||
- iAmAsval
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request! Before going any further, make sure what you're about to submit doesn't already exist.
|
||||
Your feature request will be closed without explanation if you don't follow the following rules:
|
||||
- This template shouldn't be used to ask how to use FModel or a certain feature FModel provides
|
||||
- We absolutely do not support modding
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Tell us what you want FModel to be able to do
|
||||
placeholder: Please describe with details and how it could be done if possible...
|
||||
validations:
|
||||
required: true
|
||||
48
.github/workflows/main.yml
vendored
48
.github/workflows/main.yml
vendored
|
|
@ -1,48 +0,0 @@
|
|||
name: FModel Builder
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
appVersion:
|
||||
description: 'FModel Version And Release Tag'
|
||||
required: true
|
||||
default: '4.0.X.X'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: GIT Checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: 'true'
|
||||
|
||||
- name: Fetch Submodules Recursively
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: .NET 8 Setup
|
||||
uses: actions/setup-dotnet@v2
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: .NET Restore
|
||||
run: dotnet restore FModel
|
||||
|
||||
- name: .NET Publish
|
||||
run: dotnet publish FModel -c Release --no-self-contained -r win-x64 -f net8.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false -p:AssemblyVersion=${{ github.event.inputs.appVersion }} -p:FileVersion=${{ github.event.inputs.appVersion }}
|
||||
|
||||
- name: ZIP File
|
||||
uses: papeloto/action-zip@v1
|
||||
with:
|
||||
files: ./FModel/bin/Publish/FModel.exe
|
||||
dest: FModel.zip # will end up in working directory not the Publish folder
|
||||
|
||||
- name: GIT Release
|
||||
uses: marvinpinto/action-automatic-releases@latest
|
||||
with:
|
||||
title: "FModel v${{ github.event.inputs.appVersion }}"
|
||||
automatic_release_tag: ${{ github.event.inputs.appVersion }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
prerelease: false
|
||||
files: FModel.zip
|
||||
65
.github/workflows/qa.yml
vendored
65
.github/workflows/qa.yml
vendored
|
|
@ -1,65 +0,0 @@
|
|||
name: FModel QA Builder
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ dev ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: GIT Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: .NET 8 Setup
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.x'
|
||||
|
||||
- name: .NET Restore
|
||||
run: dotnet restore FModel
|
||||
|
||||
- name: .NET Publish
|
||||
run: dotnet publish "./FModel/FModel.csproj" -c Release --no-restore --no-self-contained -r win-x64 -f net8.0-windows -o "./FModel/bin/Publish/" -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:DebugType=None -p:GenerateDocumentationFile=false -p:DebugSymbols=false
|
||||
|
||||
- name: ZIP File
|
||||
uses: thedoctor0/zip-release@0.7.6
|
||||
with:
|
||||
type: zip
|
||||
filename: ${{ github.sha }}.zip # will end up in working directory not the Publish folder
|
||||
path: ./FModel/bin/Publish/FModel.exe
|
||||
|
||||
- name: Edit QA Artifact
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: 'FModel QA Testing'
|
||||
body: 'Dev builds'
|
||||
tag: 'qa'
|
||||
artifacts: ${{ github.sha }}.zip
|
||||
prerelease: true
|
||||
allowUpdates: true
|
||||
|
||||
- name: Get Version
|
||||
id: package_version
|
||||
uses: kzrnm/get-net-sdk-project-versions-action@v2
|
||||
with:
|
||||
proj-path: ./FModel/FModel.csproj
|
||||
|
||||
- name: FModel Auth
|
||||
id: fmodel_auth
|
||||
uses: fjogeleit/http-request-action@v1.15.5
|
||||
with:
|
||||
url: "https://api.fmodel.app/v1/oauth/token"
|
||||
data: '{"username": "${{ secrets.API_USERNAME }}", "password": "${{ secrets.API_PASSWORD }}"}'
|
||||
|
||||
- name: FModel Deploy Build
|
||||
uses: fjogeleit/http-request-action@v1.15.5
|
||||
with:
|
||||
url: "https://api.fmodel.app/v1/infos/${{ secrets.QA_ID }}"
|
||||
method: "PATCH"
|
||||
bearerToken: ${{ fromJson(steps.fmodel_auth.outputs.response).accessToken }}
|
||||
data: '{"version": "${{ steps.package_version.outputs.version }}-dev+${{ github.sha }}", "downloadUrl": "https://github.com/4sval/FModel/releases/download/qa/${{ github.sha }}.zip"}'
|
||||
55
.gitignore
vendored
55
.gitignore
vendored
|
|
@ -4,7 +4,6 @@
|
|||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
|
|
@ -13,9 +12,6 @@
|
|||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
|
|
@ -23,17 +19,13 @@ mono_crash.*
|
|||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
.idea/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
|
|
@ -44,10 +36,9 @@ Generated\ Files/
|
|||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
# NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
|
|
@ -61,6 +52,7 @@ BenchmarkDotNet.Artifacts/
|
|||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
**/Properties/launchSettings.json
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
|
@ -68,7 +60,7 @@ StyleCopReport.xml
|
|||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
|
|
@ -85,7 +77,6 @@ StyleCopReport.xml
|
|||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
|
|
@ -128,6 +119,9 @@ _ReSharper*/
|
|||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding add-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
|
|
@ -185,8 +179,6 @@ PublishScripts/
|
|||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
|
|
@ -211,14 +203,12 @@ BundleArtifacts/
|
|||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
!*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
|
|
@ -231,7 +221,7 @@ ClientBin/
|
|||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
|
|
@ -246,7 +236,7 @@ Generated_Code/
|
|||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
#Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
|
|
@ -262,9 +252,6 @@ ServiceFabricBackup/
|
|||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
|
@ -300,8 +287,12 @@ paket-files/
|
|||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
# JetBrains Rider
|
||||
.idea/
|
||||
*.sln.iml
|
||||
|
||||
# CodeRush
|
||||
.cr/
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
|
|
@ -326,7 +317,7 @@ __pycache__/
|
|||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
|
|
@ -335,17 +326,5 @@ ASALocalRun/
|
|||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
|
|
|||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -1,3 +0,0 @@
|
|||
[submodule "CUE4Parse"]
|
||||
path = CUE4Parse
|
||||
url = https://github.com/FabianFG/CUE4Parse
|
||||
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 455b72e5e38bfe9476b5823bc642fc8ef488347f
|
||||
25
FModel.sln
Normal file
25
FModel.sln
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29806.167
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FModel", "FModel\FModel.csproj", "{A42F8737-D056-4FA5-BEB5-AA96E1639F8A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A42F8737-D056-4FA5-BEB5-AA96E1639F8A}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A42F8737-D056-4FA5-BEB5-AA96E1639F8A}.Debug|x64.Build.0 = Debug|x64
|
||||
{A42F8737-D056-4FA5-BEB5-AA96E1639F8A}.Release|x64.ActiveCfg = Release|x64
|
||||
{A42F8737-D056-4FA5-BEB5-AA96E1639F8A}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {FE6EA91D-BBB8-4FBC-875C-25AD92EDB519}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
@ -1,19 +1,14 @@
|
|||
<Application x:Class="FModel.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:adonisUi="clr-namespace:AdonisUI;assembly=AdonisUI"
|
||||
StartupUri="MainWindow.xaml" ShutdownMode="OnMainWindowClose"
|
||||
Exit="AppExit" DispatcherUnhandledException="OnUnhandledException">
|
||||
DispatcherUnhandledException="OnDispatcherUnhandledException">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/AdonisUI;component/ColorSchemes/Dark.xaml"/>
|
||||
<ResourceDictionary Source="pack://application:,,,/AdonisUI.ClassicTheme;component/Resources.xaml"/>
|
||||
<ResourceDictionary Source="pack://application:,,,/Theme/Style.xaml" />
|
||||
<ResourceDictionary Source="pack://application:,,,/ToastNotifications.Messages;component/Themes/Default.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Color x:Key="{x:Static adonisUi:Colors.AccentColor}">#206BD4</Color>
|
||||
<Color x:Key="{x:Static adonisUi:Colors.AlertColor}">#D49220</Color>
|
||||
<Color x:Key="{x:Static adonisUi:Colors.ErrorColor}">#C22B2B</Color>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
|
|
|||
|
|
@ -1,178 +1,80 @@
|
|||
using AdonisUI.Controls;
|
||||
using Microsoft.Win32;
|
||||
using Serilog;
|
||||
using FModel.Logger;
|
||||
using FModel.Utils;
|
||||
using FModel.ViewModels.ComboBox;
|
||||
using FModel.ViewModels.StatusBar;
|
||||
using FModel.Windows.DarkMessageBox;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog.Sinks.SystemConsole.Themes;
|
||||
using MessageBox = AdonisUI.Controls.MessageBox;
|
||||
using MessageBoxImage = AdonisUI.Controls.MessageBoxImage;
|
||||
using MessageBoxResult = AdonisUI.Controls.MessageBoxResult;
|
||||
|
||||
namespace FModel;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App
|
||||
namespace FModel
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool AttachConsole(int dwProcessId);
|
||||
|
||||
[DllImport("winbrand.dll", CharSet = CharSet.Unicode)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
|
||||
static extern string BrandingFormatString(string format);
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
#if DEBUG
|
||||
AttachConsole(-1);
|
||||
#endif
|
||||
base.OnStartup(e);
|
||||
internal static Stopwatch StartTimer { get; private set; }
|
||||
static bool framerateSet = false;
|
||||
|
||||
try
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
UserSettings.Default = JsonConvert.DeserializeObject<UserSettings>(
|
||||
File.ReadAllText(UserSettings.FilePath), JsonNetSerializer.SerializerSettings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
UserSettings.Default = new UserSettings();
|
||||
StartTimer = Stopwatch.StartNew();
|
||||
|
||||
DebugHelper.Init(LogsFilePath); // get old settings too
|
||||
|
||||
Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(ProgramLang.GetProgramLang());
|
||||
|
||||
DebugHelper.WriteLine("{0} {1}", "[FModel]", "––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––");
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Version]", Assembly.GetExecutingAssembly().GetName().Version.ToString());
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Build]", Globals.Build);
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[OS]", Logger.Logger.GetOperatingSystemProductName(true));
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Runtime]", RuntimeInformation.FrameworkDescription);
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Culture]", Thread.CurrentThread.CurrentUICulture);
|
||||
|
||||
StatusBarVm.statusBarViewModel.Set(FModel.Properties.Resources.Initializing, FModel.Properties.Resources.Loading);
|
||||
|
||||
base.OnStartup(e);
|
||||
}
|
||||
|
||||
var createMe = false;
|
||||
if (!Directory.Exists(UserSettings.Default.OutputDirectory))
|
||||
public static string LogsFilePath
|
||||
{
|
||||
var currentDir = Directory.GetCurrentDirectory();
|
||||
var dirInfo = new DirectoryInfo(currentDir);
|
||||
if (dirInfo.Attributes.HasFlag(FileAttributes.Archive))
|
||||
throw new Exception("FModel cannot be run from an archive file. Please extract it and try again.");
|
||||
if (dirInfo.Attributes.HasFlag(FileAttributes.ReadOnly))
|
||||
throw new Exception("FModel cannot be run from a read-only directory. Please move it to a writable location.");
|
||||
|
||||
UserSettings.Default.OutputDirectory = Path.Combine(currentDir, "Output");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(UserSettings.Default.RawDataDirectory))
|
||||
{
|
||||
createMe = true;
|
||||
UserSettings.Default.RawDataDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(UserSettings.Default.PropertiesDirectory))
|
||||
{
|
||||
createMe = true;
|
||||
UserSettings.Default.PropertiesDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(UserSettings.Default.TextureDirectory))
|
||||
{
|
||||
createMe = true;
|
||||
UserSettings.Default.TextureDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(UserSettings.Default.AudioDirectory))
|
||||
{
|
||||
createMe = true;
|
||||
UserSettings.Default.AudioDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(UserSettings.Default.ModelDirectory))
|
||||
{
|
||||
createMe = true;
|
||||
UserSettings.Default.ModelDirectory = Path.Combine(UserSettings.Default.OutputDirectory, "Exports");
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FModel"));
|
||||
Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Backups"));
|
||||
if (createMe) Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Exports"));
|
||||
Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, "Logs"));
|
||||
Directory.CreateDirectory(Path.Combine(UserSettings.Default.OutputDirectory, ".data"));
|
||||
|
||||
#if DEBUG
|
||||
Log.Logger = new LoggerConfiguration().WriteTo.Console(theme: AnsiConsoleTheme.Literate).WriteTo.File(
|
||||
Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Debug-Log-{DateTime.Now:yyyy-MM-dd}.txt"),
|
||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [FModel] [{Level:u3}] {Message:lj}{NewLine}{Exception}").CreateLogger();
|
||||
#else
|
||||
Log.Logger = new LoggerConfiguration().WriteTo.Console(theme: AnsiConsoleTheme.Literate).WriteTo.File(
|
||||
Path.Combine(UserSettings.Default.OutputDirectory, "Logs", $"FModel-Log-{DateTime.Now:yyyy-MM-dd}.txt"),
|
||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss} [FModel] [{Level:u3}] {Message:lj}{NewLine}{Exception}").CreateLogger();
|
||||
#endif
|
||||
|
||||
Log.Information("Version {Version} ({CommitId})", Constants.APP_VERSION, Constants.APP_COMMIT_ID);
|
||||
Log.Information("{OS}", GetOperatingSystemProductName());
|
||||
Log.Information("{RuntimeVer}", RuntimeInformation.FrameworkDescription);
|
||||
Log.Information("Culture {SysLang}", CultureInfo.CurrentCulture);
|
||||
}
|
||||
|
||||
private void AppExit(object sender, ExitEventArgs e)
|
||||
{
|
||||
Log.Information("––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––");
|
||||
Log.CloseAndFlush();
|
||||
UserSettings.Save();
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
private void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
Log.Error("{Exception}", e.Exception);
|
||||
|
||||
var messageBox = new MessageBoxModel
|
||||
{
|
||||
Text = $"An unhandled exception occurred: {e.Exception.Message}",
|
||||
Caption = "Fatal Error",
|
||||
Icon = MessageBoxImage.Error,
|
||||
Buttons = new[]
|
||||
get
|
||||
{
|
||||
MessageBoxButtons.Custom("Reset Settings", EErrorKind.ResetSettings),
|
||||
MessageBoxButtons.Custom("Restart", EErrorKind.Restart),
|
||||
MessageBoxButtons.Custom("OK", EErrorKind.Ignore)
|
||||
},
|
||||
IsSoundEnabled = false
|
||||
};
|
||||
string filename = string.Format("FModel-Log-{0:yyyy-MM-dd}.txt", DateTime.Now);
|
||||
|
||||
MessageBox.Show(messageBox);
|
||||
if (messageBox.Result == MessageBoxResult.Custom && (EErrorKind) messageBox.ButtonPressed.Id != EErrorKind.Ignore)
|
||||
{
|
||||
if ((EErrorKind) messageBox.ButtonPressed.Id == EErrorKind.ResetSettings)
|
||||
UserSettings.Delete();
|
||||
// Copy user settings from previous application version if necessary
|
||||
if (FModel.Properties.Settings.Default.UpdateSettings)
|
||||
FModel.Properties.Settings.Default.Upgrade();
|
||||
|
||||
ApplicationService.ApplicationView.Restart();
|
||||
Folders.LoadFolders();
|
||||
|
||||
return Path.Combine(FModel.Properties.Settings.Default.OutputPath + "\\Logs", filename);
|
||||
}
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private string GetOperatingSystemProductName()
|
||||
{
|
||||
var productName = string.Empty;
|
||||
try
|
||||
private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
productName = BrandingFormatString("%WINDOWS_LONG%");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
string errorMessage = string.Format(FModel.Properties.Resources.UnhandledExceptionOccured, e.Exception.Message);
|
||||
DebugHelper.WriteException(e.Exception, "thrown in App.xaml.cs by OnDispatcherUnhandledException");
|
||||
DarkMessageBoxHelper.Show(errorMessage, FModel.Properties.Resources.Error, MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(productName))
|
||||
productName = Environment.OSVersion.VersionString;
|
||||
|
||||
return $"{productName} ({(Environment.Is64BitOperatingSystem ? "64" : "32")}-bit)";
|
||||
}
|
||||
|
||||
public static string GetRegistryValue(string path, string name = null, RegistryHive root = RegistryHive.CurrentUser)
|
||||
{
|
||||
using var rk = RegistryKey.OpenBaseKey(root, RegistryView.Default).OpenSubKey(path);
|
||||
if (rk != null)
|
||||
return rk.GetValue(name, null) as string;
|
||||
return string.Empty;
|
||||
internal static void SetFramerate()
|
||||
{
|
||||
if (!framerateSet)
|
||||
{
|
||||
System.Windows.Media.Animation.Timeline.DesiredFrameRateProperty.OverrideMetadata(
|
||||
typeof(System.Windows.Media.Animation.Timeline),
|
||||
new FrameworkPropertyMetadata { DefaultValue = 10 });
|
||||
framerateSet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using CUE4Parse.UE4.Objects.Core.Misc;
|
||||
using FModel.Extensions;
|
||||
|
||||
namespace FModel;
|
||||
|
||||
public static class Constants
|
||||
{
|
||||
public static readonly string APP_PATH = Path.GetFullPath(Environment.GetCommandLineArgs()[0]);
|
||||
public static readonly string APP_VERSION = FileVersionInfo.GetVersionInfo(APP_PATH).FileVersion;
|
||||
public static readonly string APP_COMMIT_ID = FileVersionInfo.GetVersionInfo(APP_PATH).ProductVersion.SubstringAfter('+');
|
||||
public static readonly string APP_SHORT_COMMIT_ID = APP_COMMIT_ID[..7];
|
||||
|
||||
public const string ZERO_64_CHAR = "0000000000000000000000000000000000000000000000000000000000000000";
|
||||
public static readonly FGuid ZERO_GUID = new(0U);
|
||||
|
||||
public const float SCALE_DOWN_RATIO = 0.01F;
|
||||
public const int SAMPLES_COUNT = 4;
|
||||
|
||||
public const string WHITE = "#DAE5F2";
|
||||
public const string GRAY = "#BBBBBB";
|
||||
public const string RED = "#E06C75";
|
||||
public const string GREEN = "#98C379";
|
||||
public const string YELLOW = "#E5C07B";
|
||||
public const string BLUE = "#528BCC";
|
||||
|
||||
public const string ISSUE_LINK = "https://github.com/4sval/FModel/discussions/categories/q-a";
|
||||
public const string GH_REPO = "https://api.github.com/repos/4sval/FModel";
|
||||
public const string GH_COMMITS_HISTORY = GH_REPO + "/commits";
|
||||
public const string GH_RELEASES = GH_REPO + "/releases";
|
||||
public const string DONATE_LINK = "https://fmodel.app/donate";
|
||||
public const string DISCORD_LINK = "https://fmodel.app/discord";
|
||||
|
||||
public const string _FN_LIVE_TRIGGER = "fortnite-live.manifest";
|
||||
public const string _VAL_LIVE_TRIGGER = "valorant-live.manifest";
|
||||
|
||||
public const string _NO_PRESET_TRIGGER = "Hand Made";
|
||||
|
||||
public static int PALETTE_LENGTH => COLOR_PALETTE.Length;
|
||||
public static readonly Vector3[] COLOR_PALETTE =
|
||||
{
|
||||
new (0.231f, 0.231f, 0.231f), // Dark gray
|
||||
new (0.376f, 0.490f, 0.545f), // Teal
|
||||
new (0.957f, 0.263f, 0.212f), // Red
|
||||
new (0.196f, 0.804f, 0.196f), // Green
|
||||
new (0.957f, 0.647f, 0.212f), // Orange
|
||||
new (0.612f, 0.153f, 0.690f), // Purple
|
||||
new (0.129f, 0.588f, 0.953f), // Blue
|
||||
new (1.000f, 0.920f, 0.424f), // Yellow
|
||||
new (0.824f, 0.412f, 0.118f), // Brown
|
||||
new (0.612f, 0.800f, 0.922f) // Light blue
|
||||
};
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.BB;
|
||||
|
||||
public class BaseBreakersIcon : BaseIcon
|
||||
{
|
||||
public BaseBreakersIcon(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
SeriesBackground = Utils.GetBitmap("WorldExplorers/Content/UMG/Materials/t_TextGradient.t_TextGradient");
|
||||
Background = new[] { SKColor.Parse("D0D0D0"), SKColor.Parse("636363") };
|
||||
Border = new[] { SKColor.Parse("D0D0D0"), SKColor.Parse("FFFFFF") };
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FSoftObjectPath iconTextureAssetData, "IconTextureAssetData", "UnlockPortraitGuideImage"))
|
||||
Preview = Utils.GetBitmap(iconTextureAssetData);
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "RegionDisplayName", "ZoneName"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "Description", "RegionShortName", "ZoneDescription"))
|
||||
Description = description.Text;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
}
|
||||
102
FModel/Creator/Bases/BaseBundle.cs
Normal file
102
FModel/Creator/Bases/BaseBundle.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
using FModel.Creator.Bundles;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
public class BaseBundle
|
||||
{
|
||||
public Header DisplayStyle;
|
||||
public string DisplayName;
|
||||
public string FolderName;
|
||||
public string Watermark;
|
||||
public int Width = 1024;
|
||||
public int HeaderHeight = 261; // height is the header basically
|
||||
public int AdditionalSize = 50; // must be increased depending on the number of quests to draw
|
||||
public bool IsDisplayNameShifted;
|
||||
public List<Quest> Quests;
|
||||
public List<CompletionReward> CompletionRewards;
|
||||
|
||||
public BaseBundle()
|
||||
{
|
||||
DisplayStyle = new Header();
|
||||
DisplayName = "";
|
||||
FolderName = "";
|
||||
Watermark = Properties.Settings.Default.ChallengeBannerWatermark;
|
||||
Quests = new List<Quest>();
|
||||
CompletionRewards = new List<CompletionReward>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// used for the settings
|
||||
/// </summary>
|
||||
public BaseBundle(string watermark) : this()
|
||||
{
|
||||
DisplayName = "{DisplayName}";
|
||||
FolderName = "{FolderName}";
|
||||
Watermark = watermark;
|
||||
Quests.Add(new Quest { Description = "", Count = 999, Reward = null });
|
||||
AdditionalSize += 89;
|
||||
}
|
||||
|
||||
public BaseBundle(IUExport export, string assetFolder) : this()
|
||||
{
|
||||
if (export.GetExport<StructProperty>("DisplayStyle") is StructProperty displayStyle)
|
||||
DisplayStyle = new Header(displayStyle, assetFolder);
|
||||
if (export.GetExport<TextProperty>("DisplayName") is TextProperty displayName)
|
||||
DisplayName = Text.GetTextPropertyBase(displayName);
|
||||
|
||||
if (export.GetExport<ArrayProperty>("CareerQuestBitShifts") is ArrayProperty careerQuestBitShifts)
|
||||
{
|
||||
foreach (SoftObjectProperty questPath in careerQuestBitShifts.Value)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(questPath.Value.AssetPathName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
if (obj != null)
|
||||
Quests.Add(new Quest(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (export.GetExport<ArrayProperty>("BundleCompletionRewards") is ArrayProperty bundleCompletionRewards)
|
||||
{
|
||||
foreach (StructProperty completionReward in bundleCompletionRewards.Value)
|
||||
{
|
||||
if (completionReward.Value is UObject reward &&
|
||||
reward.TryGetValue("CompletionCount", out var c) && c is IntProperty completionCount &&
|
||||
reward.TryGetValue("Rewards", out var r) && r is ArrayProperty rewards)
|
||||
{
|
||||
foreach (StructProperty rew in rewards.Value)
|
||||
{
|
||||
if (rew.Value is UObject re &&
|
||||
re.TryGetValue("Quantity", out var q) && q is IntProperty quantity &&
|
||||
re.TryGetValue("TemplateId", out var t) && t is StrProperty templateId &&
|
||||
re.TryGetValue("ItemDefinition", out var d) && d is SoftObjectProperty itemDefinition)
|
||||
{
|
||||
if (!itemDefinition.Value.AssetPathName.IsNone &&
|
||||
!itemDefinition.Value.AssetPathName.String.StartsWith("/Game/Items/Tokens/") &&
|
||||
!itemDefinition.Value.AssetPathName.String.StartsWith("/Game/Athena/Items/Quests"))
|
||||
{
|
||||
CompletionRewards.Add(new CompletionReward(completionCount, quantity, itemDefinition));
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(templateId.Value))
|
||||
{
|
||||
CompletionRewards.Add(new CompletionReward(completionCount, quantity, templateId.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FolderName = assetFolder;
|
||||
AdditionalSize += 95 * Quests.Count;
|
||||
if (CompletionRewards.Count > 0) AdditionalSize += 50 + (95 * CompletionRewards.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
FModel/Creator/Bases/BaseIcon.cs
Normal file
130
FModel/Creator/Bases/BaseIcon.cs
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
using FModel.Creator.Icons;
|
||||
using FModel.Creator.Rarities;
|
||||
using FModel.Creator.Stats;
|
||||
using FModel.Creator.Texts;
|
||||
using FModel.Utils;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
public class BaseIcon
|
||||
{
|
||||
public SKBitmap FallbackImage;
|
||||
public SKBitmap IconImage;
|
||||
public SKBitmap RarityBackgroundImage;
|
||||
public SKBitmap[] UserFacingFlags;
|
||||
public SKColor[] RarityBackgroundColors;
|
||||
public SKColor[] RarityBorderColor;
|
||||
public string DisplayName;
|
||||
public string Description;
|
||||
public string ShortDescription;
|
||||
public string CosmeticSource;
|
||||
public int Size = 512; // keep it 512 (or a multiple of 512) if you don't want blurry icons
|
||||
public int AdditionalSize = 0; // must be increased if there are weapon stats, hero abilities or more to draw/show
|
||||
public int Margin = 2;
|
||||
public List<Statistic> Stats;
|
||||
|
||||
public BaseIcon()
|
||||
{
|
||||
FallbackImage = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_Placeholder_Item_Image.png")).Stream);
|
||||
IconImage = FallbackImage;
|
||||
RarityBackgroundImage = null;
|
||||
UserFacingFlags = null;
|
||||
RarityBackgroundColors = new SKColor[2] { SKColor.Parse("5EBC36"), SKColor.Parse("305C15") };
|
||||
RarityBorderColor = new SKColor[2] { SKColor.Parse("74EF52"), SKColor.Parse("74EF52") };
|
||||
DisplayName = "";
|
||||
Description = "";
|
||||
ShortDescription = "";
|
||||
CosmeticSource = "";
|
||||
Stats = new List<Statistic>();
|
||||
}
|
||||
|
||||
public BaseIcon(IUExport export, string assetName, bool forceHR) : this()
|
||||
{
|
||||
if (export.GetExport<ObjectProperty>("Series") is ObjectProperty series)
|
||||
Serie.GetRarity(this, series);
|
||||
else if (Properties.Settings.Default.UseGameColors) // override default green
|
||||
Rarity.GetInGameRarity(this, export.GetExport<EnumProperty>("Rarity")); // uncommon will be triggered by Rarity being null
|
||||
else if (export.GetExport<EnumProperty>("Rarity") is EnumProperty rarity)
|
||||
Rarity.GetHardCodedRarity(this, rarity);
|
||||
|
||||
if (export.GetExport<ObjectProperty>("HeroDefinition", "WeaponDefinition") is ObjectProperty itemDef)
|
||||
LargeSmallImage.GetPreviewImage(this, itemDef, assetName, forceHR);
|
||||
else if (export.GetExport<SoftObjectProperty>(forceHR ? "LargePreviewImage" : "SmallPreviewImage", forceHR ? "ItemDisplayAsset" : "SmallImage") is SoftObjectProperty previewImage)
|
||||
LargeSmallImage.GetPreviewImage(this, previewImage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order:
|
||||
/// 1. Rarity
|
||||
/// 2. Image
|
||||
/// 3. Text
|
||||
/// 1. DisplayName
|
||||
/// 2. Description
|
||||
/// 3. Misc
|
||||
/// 4. GameplayTags
|
||||
/// 1. order doesn't matter
|
||||
/// 2. the importance here is to get the description before gameplay tags
|
||||
/// </summary>
|
||||
public BaseIcon(IUExport export, string exportType, ref string assetName) : this()
|
||||
{
|
||||
// rarity
|
||||
if (export.GetExport<ObjectProperty>("Series") is ObjectProperty series)
|
||||
Serie.GetRarity(this, series);
|
||||
else if (Properties.Settings.Default.UseGameColors) // override default green
|
||||
Rarity.GetInGameRarity(this, export.GetExport<EnumProperty>("Rarity")); // uncommon will be triggered by Rarity being null
|
||||
else if (export.GetExport<EnumProperty>("Rarity") is EnumProperty rarity)
|
||||
Rarity.GetHardCodedRarity(this, rarity);
|
||||
|
||||
// image
|
||||
if (Properties.Settings.Default.UseItemShopIcon &&
|
||||
DisplayAssetImage.GetDisplayAssetImage(this, export.GetExport<SoftObjectProperty>("DisplayAssetPath"), ref assetName))
|
||||
{ } // ^^^^ will return false if image not found, if so, we try to get the normal icon
|
||||
else if (export.GetExport<ObjectProperty>("HeroDefinition", "WeaponDefinition") is ObjectProperty itemDef)
|
||||
LargeSmallImage.GetPreviewImage(this, itemDef, assetName);
|
||||
else if (export.GetExport<SoftObjectProperty>("LargePreviewImage", "SmallPreviewImage", "ItemDisplayAsset") is SoftObjectProperty previewImage)
|
||||
LargeSmallImage.GetPreviewImage(this, previewImage);
|
||||
else if (export.GetExport<ObjectProperty>("SmallPreviewImage") is ObjectProperty smallPreviewImage)
|
||||
this.IconImage = Utils.GetObjectTexture(smallPreviewImage);
|
||||
else if (export.GetExport<StructProperty>("IconBrush") is StructProperty iconBrush) // abilities
|
||||
LargeSmallImage.GetPreviewImage(this, iconBrush);
|
||||
|
||||
// text
|
||||
if (export.GetExport<TextProperty>("DisplayName", "DefaultHeaderText", "UIDisplayName") is TextProperty displayName)
|
||||
DisplayName = Text.GetTextPropertyBase(displayName);
|
||||
if (export.GetExport<TextProperty>("Description", "DefaultBodyText") is TextProperty description)
|
||||
Description = Text.GetTextPropertyBase(description);
|
||||
else if (export.GetExport<ArrayProperty>("Description") is ArrayProperty arrayDescription) // abilities
|
||||
Description = Text.GetTextPropertyBase(arrayDescription);
|
||||
if (export.GetExport<StructProperty>("MaxStackSize") is StructProperty maxStackSize)
|
||||
ShortDescription = Text.GetMaxStackSize(maxStackSize);
|
||||
else if (export.GetExport<TextProperty>("ShortDescription") is TextProperty shortDescription)
|
||||
ShortDescription = Text.GetTextPropertyBase(shortDescription);
|
||||
else if (exportType.Equals("AthenaItemWrapDefinition")) // if no ShortDescription it's most likely a wrap
|
||||
ShortDescription = Localizations.GetLocalization("Fort.Cosmetics", "ItemWrapShortDescription", "Wrap");
|
||||
|
||||
// gameplaytags
|
||||
if (export.GetExport<StructProperty>("GameplayTags") is StructProperty gameplayTags)
|
||||
GameplayTag.GetGameplayTags(this, gameplayTags, exportType);
|
||||
else if (export.GetExport<ObjectProperty>("cosmetic_item") is ObjectProperty cosmeticItem) // variants
|
||||
CosmeticSource = cosmeticItem.Value.Resource.ObjectName.String;
|
||||
|
||||
if (export.GetExport<SoftObjectProperty>("AmmoData") is SoftObjectProperty ammoData)
|
||||
Statistics.GetAmmoData(this, ammoData);
|
||||
if (export.GetExport<StructProperty>("WeaponStatHandle") is StructProperty weaponStatHandle)
|
||||
Statistics.GetWeaponStats(this, weaponStatHandle);
|
||||
if (export.GetExport<ObjectProperty>("HeroGameplayDefinition") is ObjectProperty heroGameplayDefinition)
|
||||
Statistics.GetHeroStats(this, heroGameplayDefinition);
|
||||
|
||||
/* Please do not add Schematics support because it takes way too much memory */
|
||||
/* Thank the STW Dev Team for using a 5,69Mb file to get... Oh nvm, they all left */
|
||||
|
||||
AdditionalSize = 48 * Stats.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
FModel/Creator/Bases/BaseItemAccess.cs
Normal file
112
FModel/Creator/Bases/BaseItemAccess.cs
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
using FModel.Creator.Rarities;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
public class BaseItemAccess
|
||||
{
|
||||
private readonly SKPaint descriptionPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DescriptionTypeface,
|
||||
TextSize = 13,
|
||||
Color = SKColors.White,
|
||||
};
|
||||
|
||||
public BaseIcon Item;
|
||||
public string SItem;
|
||||
public SKBitmap Lock;
|
||||
public SKBitmap Unlock;
|
||||
public string DisplayName;
|
||||
public string Description;
|
||||
public string UnlockDescription;
|
||||
public int Size = 512; // keep it 512 (or a multiple of 512) if you don't want blurry icons
|
||||
|
||||
public BaseItemAccess()
|
||||
{
|
||||
Item = new BaseIcon();
|
||||
SItem = "";
|
||||
Lock = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Locks/T-Icon-Lock-128").Resize(24, 24);
|
||||
Unlock = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Locks/T-Icon-Unlocked-128").Resize(24, 24);
|
||||
DisplayName = "";
|
||||
Description = "";
|
||||
UnlockDescription = "";
|
||||
}
|
||||
|
||||
public BaseItemAccess(IUExport export) : this()
|
||||
{
|
||||
if (export.GetExport<ObjectProperty>("access_item") is ObjectProperty accessItem)
|
||||
{
|
||||
SItem = accessItem.Value.Resource.ObjectName.String;
|
||||
PakPackage p = Utils.GetPropertyPakPackage(accessItem.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
if (d != null)
|
||||
{
|
||||
Item = new BaseIcon(d, SItem + ".uasset", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (export.GetExport<TextProperty>("DisplayName") is TextProperty displayName)
|
||||
DisplayName = Text.GetTextPropertyBase(displayName);
|
||||
if (export.GetExport<TextProperty>("Description") is TextProperty description)
|
||||
Description = Text.GetTextPropertyBase(description);
|
||||
if (export.GetExport<TextProperty>("UnlockDescription") is TextProperty unlockDescription)
|
||||
UnlockDescription = Text.GetTextPropertyBase(unlockDescription);
|
||||
}
|
||||
|
||||
public void Draw(SKCanvas c)
|
||||
{
|
||||
Rarity.DrawRarity(c, Item);
|
||||
|
||||
int size = 45;
|
||||
int left = Size / 2;
|
||||
SKPaint namePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = size,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Center,
|
||||
};
|
||||
while (namePaint.MeasureText(DisplayName) > (Size - (Item.Margin * 2)))
|
||||
{
|
||||
namePaint.TextSize = size -= 2;
|
||||
}
|
||||
c.DrawText(DisplayName, left, Item.Margin * 8 + size, namePaint);
|
||||
|
||||
int topBase = Item.Margin + size * 2;
|
||||
c.DrawBitmap(Lock, new SKRect(50, topBase, 50 + Lock.Width, topBase + Lock.Height),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
Helper.DrawMultilineText(c, UnlockDescription, Size, Item.Margin, ETextSide.Left,
|
||||
new SKRect(70 + Lock.Width, topBase + 10, Size - 50, 256), descriptionPaint, out topBase);
|
||||
|
||||
c.DrawBitmap(Unlock, new SKRect(50, topBase, 50 + Unlock.Width, topBase + Unlock.Height),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
Helper.DrawMultilineText(c, Description, Size, Item.Margin, ETextSide.Left,
|
||||
new SKRect(70 + Unlock.Width, topBase + 10, Size - 50, 256), descriptionPaint, out topBase);
|
||||
|
||||
int h = Size - Item.Margin - topBase;
|
||||
c.DrawBitmap(Item.IconImage ?? Item.FallbackImage, new SKRect(left - h / 2, topBase, left + h / 2, Size - Item.Margin),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
|
||||
c.DrawText(SItem, Size - (Item.Margin * 2.5f), Size - (Item.Margin * 2.5f), new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DefaultTypeface,
|
||||
TextSize = 15,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Right,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
96
FModel/Creator/Bases/BaseMapUIData.cs
Normal file
96
FModel/Creator/Bases/BaseMapUIData.cs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
public class BaseMapUIData
|
||||
{
|
||||
public SKBitmap Splash;
|
||||
public SKBitmap VLogo;
|
||||
public string DisplayName;
|
||||
public string Description;
|
||||
public string Coordinates;
|
||||
public int Width = 1920;
|
||||
public int Height = 1080;
|
||||
|
||||
public BaseMapUIData()
|
||||
{
|
||||
Splash = null;
|
||||
VLogo = null;
|
||||
DisplayName = "";
|
||||
Description = "";
|
||||
Coordinates = "";
|
||||
}
|
||||
|
||||
public BaseMapUIData(IUExport export) : this()
|
||||
{
|
||||
if (export.GetExport<TextProperty>("DisplayName") is TextProperty displayName)
|
||||
DisplayName = Text.GetTextPropertyBase(displayName) ?? "";
|
||||
if (export.GetExport<TextProperty>("Description") is TextProperty description)
|
||||
Description = Text.GetTextPropertyBase(description) ?? "";
|
||||
if (export.GetExport<TextProperty>("Coordinates") is TextProperty coordinates)
|
||||
Coordinates = Text.GetTextPropertyBase(coordinates) ?? "";
|
||||
|
||||
if (export.GetExport<ObjectProperty>("Splash") is ObjectProperty icon)
|
||||
Splash = Utils.GetObjectTexture(icon);
|
||||
|
||||
VLogo = Utils.GetTexture("/Game/UI/Shared/Icons/Valorant_logo_cutout").Resize(48, 48);
|
||||
|
||||
if (Splash != null)
|
||||
{
|
||||
Width = Splash.Width;
|
||||
Height = Splash.Height;
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SKCanvas c)
|
||||
{
|
||||
int paddingLR = 80;
|
||||
int paddingTB = 35;
|
||||
int nameSize = 200;
|
||||
int descriptionSize = 30;
|
||||
using var namePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = nameSize,
|
||||
TextAlign = SKTextAlign.Left,
|
||||
Color = SKColor.Parse("FFFBFA")
|
||||
};
|
||||
while (namePaint.MeasureText(DisplayName) > Width - (paddingLR * 2))
|
||||
{
|
||||
namePaint.TextSize = nameSize -= 2;
|
||||
}
|
||||
using var descriptionPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DescriptionTypeface,
|
||||
TextSize = descriptionSize,
|
||||
TextAlign = SKTextAlign.Left,
|
||||
Color = SKColor.Parse("FFFBFA")
|
||||
};
|
||||
while (descriptionPaint.MeasureText(Description) > Width - (paddingLR * 2))
|
||||
{
|
||||
descriptionPaint.TextSize = descriptionSize -= 2;
|
||||
}
|
||||
|
||||
c.DrawBitmap(Splash, new SKRect(0, 0, Width, Height), new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
c.DrawText(DisplayName.ToUpper(), paddingLR, paddingTB + namePaint.TextSize, namePaint);
|
||||
c.DrawRect(new SKRect(paddingLR + 2.5f, paddingTB + 25 + namePaint.TextSize, paddingLR + 202.5f, paddingTB + 27.5f + namePaint.TextSize), new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true, Color = SKColor.Parse("5AFFFBFA") });
|
||||
c.DrawText(Description, paddingLR + 2.5f, paddingTB + 40 + namePaint.TextSize + descriptionPaint.TextSize, descriptionPaint);
|
||||
|
||||
descriptionPaint.Typeface = Text.TypeFaces.BundleDefaultTypeface;
|
||||
c.DrawText(Coordinates.ToUpper(), paddingLR, Height - paddingTB - descriptionPaint.TextSize, descriptionPaint);
|
||||
|
||||
if (VLogo != null)
|
||||
{
|
||||
c.DrawBitmap(VLogo, new SKRect(Width - VLogo.Width - paddingLR, paddingLR, Width - paddingLR, paddingLR + VLogo.Height), new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
c.DrawRect(new SKRect(Width - VLogo.Width - paddingLR, paddingLR + VLogo.Height + 5, Width - paddingLR, paddingLR + VLogo.Height + 7.5f), new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true, Color = SKColor.Parse("FFFBFA") });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
93
FModel/Creator/Bases/BaseOffer.cs
Normal file
93
FModel/Creator/Bases/BaseOffer.cs
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
public class BaseOffer
|
||||
{
|
||||
public SKBitmap FallbackImage;
|
||||
public SKBitmap IconImage;
|
||||
public SKColor[] RarityBackgroundColors;
|
||||
public SKColor RarityBorderColor;
|
||||
public int Size = 512;
|
||||
public int Margin = 2;
|
||||
|
||||
public BaseOffer()
|
||||
{
|
||||
FallbackImage = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_Placeholder_Item_Image.png")).Stream);
|
||||
IconImage = FallbackImage;
|
||||
RarityBackgroundColors = new SKColor[2] { SKColor.Parse("4F4F69"), SKColor.Parse("4F4F69") };
|
||||
RarityBorderColor = SKColor.Parse("9092AB");
|
||||
}
|
||||
|
||||
public BaseOffer(IUExport export) : this()
|
||||
{
|
||||
if (export.GetExport<StructProperty>("DetailsImage", "TileImage") is StructProperty typeImage)
|
||||
{
|
||||
if (typeImage.Value is UObject t && t.TryGetValue("ResourceObject", out var v) && v is ObjectProperty resourceObject)
|
||||
{
|
||||
IconImage = Utils.GetObjectTexture(resourceObject);
|
||||
}
|
||||
}
|
||||
|
||||
if (export.GetExport<StructProperty>("Gradient") is StructProperty gradient)
|
||||
{
|
||||
if (gradient.Value is UObject g &&
|
||||
g.TryGetValue("Start", out var s1) && s1 is StructProperty t1 && t1.Value is FLinearColor start &&
|
||||
g.TryGetValue("Stop", out var s2) && s2 is StructProperty t2 && t2.Value is FLinearColor stop)
|
||||
{
|
||||
RarityBackgroundColors = new SKColor[2] { SKColor.Parse(start.Hex), SKColor.Parse(stop.Hex) };
|
||||
}
|
||||
}
|
||||
|
||||
if (export.GetExport<StructProperty>("Background") is StructProperty background)
|
||||
{
|
||||
if (background.Value is FLinearColor b)
|
||||
{
|
||||
RarityBorderColor = SKColor.Parse(b.Hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawBackground(SKCanvas c)
|
||||
{
|
||||
if (RarityBackgroundColors[0] == RarityBackgroundColors[1])
|
||||
RarityBackgroundColors[0] = RarityBorderColor;
|
||||
|
||||
RarityBackgroundColors[0].ToHsl(out var _, out var _, out var l1);
|
||||
RarityBackgroundColors[1].ToHsl(out var _, out var _, out var l2);
|
||||
bool reverse = l1 > l2;
|
||||
|
||||
// border
|
||||
c.DrawRect(new SKRect(0, 0, Size, Size),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = RarityBorderColor
|
||||
});
|
||||
|
||||
c.DrawRect(new SKRect(Margin, Margin, Size - Margin, Size - Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateRadialGradient(
|
||||
new SKPoint(Size / 2, Size / 2),
|
||||
Size / 5 * 4,
|
||||
new SKColor[2] { reverse ? RarityBackgroundColors[0] : RarityBackgroundColors[1], reverse ? RarityBackgroundColors[1] : RarityBackgroundColors[0] },
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
}
|
||||
|
||||
public void DrawImage(SKCanvas c)
|
||||
{
|
||||
c.DrawBitmap(IconImage ?? FallbackImage, new SKRect(Margin, Margin, Size - Margin, Size - Margin),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
177
FModel/Creator/Bases/BaseUIData.cs
Normal file
177
FModel/Creator/Bases/BaseUIData.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
using FModel.Creator.Stats;
|
||||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
public class BaseUIData
|
||||
{
|
||||
private readonly SKPaint descriptionPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DescriptionTypeface,
|
||||
TextSize = 19.5f,
|
||||
Color = SKColor.Parse("939498"),
|
||||
};
|
||||
|
||||
public SKBitmap IconImage;
|
||||
public string DisplayName;
|
||||
public string Description;
|
||||
public List<Statistic> Abilities;
|
||||
public int Width = 768; // keep it 512 (or a multiple of 512) if you don't want blurry icons
|
||||
public int AdditionalWidth = 0;
|
||||
public int Height = 96;
|
||||
public int Margin = 3;
|
||||
|
||||
public BaseUIData()
|
||||
{
|
||||
IconImage = null;
|
||||
DisplayName = "";
|
||||
Description = "";
|
||||
Abilities = new List<Statistic>();
|
||||
}
|
||||
|
||||
public BaseUIData(IUExport[] exports, int baseIndex) : this()
|
||||
{
|
||||
if (exports[baseIndex].GetExport<TextProperty>("DisplayName") is TextProperty displayName)
|
||||
DisplayName = Text.GetTextPropertyBase(displayName) ?? "";
|
||||
if (exports[baseIndex].GetExport<TextProperty>("Description") is TextProperty description)
|
||||
{
|
||||
Description = Text.GetTextPropertyBase(description) ?? "";
|
||||
if (Description.Equals(DisplayName)) Description = string.Empty;
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
Height += (int)descriptionPaint.TextSize * Helper.SplitLines(Description, descriptionPaint, Width - Margin).Length;
|
||||
Height += (int)descriptionPaint.TextSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (exports[baseIndex].GetExport<ObjectProperty>("StoreFeaturedImage", "FullRender", "VerticalPromoImage", "LargeIcon", "DisplayIcon2", "DisplayIcon") is ObjectProperty icon)
|
||||
{
|
||||
SKBitmap raw = Utils.GetObjectTexture(icon);
|
||||
if (raw != null)
|
||||
{
|
||||
float coef = (float)Width / (float)raw.Width;
|
||||
int sizeX = (int)(raw.Width * coef);
|
||||
int sizeY = (int)(raw.Height * coef);
|
||||
Height += sizeY;
|
||||
IconImage = raw.Resize(sizeX, sizeY);
|
||||
}
|
||||
}
|
||||
|
||||
if (exports[baseIndex].GetExport<MapProperty>("Abilities") is MapProperty abilities)
|
||||
{
|
||||
AdditionalWidth = 768;
|
||||
foreach (var (_, value) in abilities.Value)
|
||||
{
|
||||
if (value is ObjectProperty o && o.Value.Resource == null && o.Value.Index > 0)
|
||||
{
|
||||
Statistic s = new Statistic();
|
||||
if (exports[o.Value.Index - 1].GetExport<TextProperty>("DisplayName") is TextProperty aDisplayName)
|
||||
s.DisplayName = Text.GetTextPropertyBase(aDisplayName) ?? "";
|
||||
if (exports[o.Value.Index - 1].GetExport<TextProperty>("Description") is TextProperty aDescription)
|
||||
{
|
||||
s.Description = Text.GetTextPropertyBase(aDescription) ?? "";
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
s.Height += (int)descriptionPaint.TextSize * Helper.SplitLines(s.Description, descriptionPaint, Width - Margin).Length;
|
||||
s.Height += (int)descriptionPaint.TextSize * 3;
|
||||
}
|
||||
}
|
||||
if (exports[o.Value.Index - 1].GetExport<ObjectProperty>("DisplayIcon") is ObjectProperty displayIcon)
|
||||
{
|
||||
SKBitmap raw = Utils.GetObjectTexture(displayIcon);
|
||||
if (raw != null) s.Icon = raw.Resize(128, 128);
|
||||
}
|
||||
Abilities.Add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Draw(SKCanvas c)
|
||||
{
|
||||
DrawCenteredTitle(c, DisplayName, 67.5f, out var textSize);
|
||||
|
||||
Helper.DrawMultilineText(c, Description, Width, Margin, ETextSide.Center,
|
||||
new SKRect(Margin, textSize + 56.25f, Width - Margin, Height - 37.5f), descriptionPaint, out var yPos);
|
||||
|
||||
if (IconImage != null)
|
||||
c.DrawBitmap(IconImage, new SKRect(0, yPos, Width, Height),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
|
||||
int yaPos = 0;
|
||||
foreach (Statistic ability in Abilities)
|
||||
{
|
||||
int xToAdd = ability.Icon != null ? ability.Icon.Width : 0;
|
||||
textSize = 42.5f;
|
||||
var namePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = textSize,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Left,
|
||||
};
|
||||
|
||||
// resize if too long
|
||||
while (namePaint.MeasureText(ability.DisplayName) > Width - 128)
|
||||
{
|
||||
namePaint.TextSize = textSize -= 2;
|
||||
}
|
||||
|
||||
c.DrawText(ability.DisplayName, Width + Margin + xToAdd + 10, yaPos + Margin + textSize, namePaint);
|
||||
|
||||
Helper.DrawMultilineText(c, ability.Description, Width, Width + Margin + xToAdd + 10, ETextSide.Left,
|
||||
new SKRect(Width + Margin + xToAdd + 10, textSize + yaPos + 27.5f, Width + AdditionalWidth - Margin, Height - 27.5f), descriptionPaint, out var _);
|
||||
|
||||
if (ability.Icon != null)
|
||||
c.DrawBitmap(ability.Icon, new SKRect(Width + Margin, yaPos, Width + Margin + ability.Icon.Width, yaPos + ability.Icon.Height),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
|
||||
yaPos += ability.Height + 48 + (Margin * 2);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCenteredTitle(SKCanvas c, string title, float textSize, out float outTextSize)
|
||||
{
|
||||
SKPaint namePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = textSize,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Center,
|
||||
};
|
||||
float textWidth = namePaint.MeasureText(title);
|
||||
while (textWidth > Width) // resize if too long
|
||||
{
|
||||
namePaint.TextSize = textSize -= 2;
|
||||
textWidth = namePaint.MeasureText(title);
|
||||
}
|
||||
outTextSize = textSize;
|
||||
|
||||
float x1 = (Width / 2 - (textWidth / 2)) - 20;
|
||||
float x2 = (x1 + textWidth) + 40;
|
||||
float y1 = Margin + 5;
|
||||
float y2 = Margin + namePaint.TextSize + 10;
|
||||
|
||||
c.DrawLine(new SKPoint(30, y1 + 5 + (namePaint.TextSize / 2)), new SKPoint(x1 - 30, y1 + 5 + (namePaint.TextSize / 2)), new SKPaint { Color = SKColor.Parse("E2E8E6") });
|
||||
c.DrawLine(new SKPoint(x2 + 30, y1 + 5 + (namePaint.TextSize / 2)), new SKPoint(Width - 30, y1 + 5 + (namePaint.TextSize / 2)), new SKPaint { Color = SKColor.Parse("E2E8E6") });
|
||||
|
||||
c.DrawLine(new SKPoint(x1, y1), new SKPoint(x2, y1), new SKPaint { Color = SKColor.Parse("E2E8E6") }); // top
|
||||
c.DrawLine(new SKPoint(x1, y2 + 5), new SKPoint(x2, y2 + 5), new SKPaint { Color = SKColor.Parse("E2E8E6") }); // bottom
|
||||
c.DrawLine(new SKPoint(x1, y1), new SKPoint(x1, y2 + 5), new SKPaint { Color = SKColor.Parse("E2E8E6") }); // left
|
||||
c.DrawLine(new SKPoint(x2, y1), new SKPoint(x2, y2 + 5), new SKPaint { Color = SKColor.Parse("E2E8E6") }); // right
|
||||
c.DrawRect(new SKRect(x1 + 5, y1 + 5, x2 - 5, y2), new SKPaint { Color = SKColor.Parse("949598") });
|
||||
|
||||
c.DrawText(title, Width / 2, Margin + namePaint.TextSize, namePaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
169
FModel/Creator/Bases/BaseUserOption.cs
Normal file
169
FModel/Creator/Bases/BaseUserOption.cs
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
using FModel.Creator.Texts;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Creator.Bases
|
||||
{
|
||||
public class Options
|
||||
{
|
||||
public string Option;
|
||||
public SKColor Color = SKColor.Parse("55C5FC").WithAlpha(150);
|
||||
}
|
||||
|
||||
public class BaseUserOption
|
||||
{
|
||||
private readonly SKPaint descriptionPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = 25,
|
||||
Color = SKColor.Parse("88DBFF"),
|
||||
};
|
||||
|
||||
public string OptionDisplayName;
|
||||
public string OptionDescription;
|
||||
public List<Options> OptionValues = new List<Options>();
|
||||
public int Width = 512;
|
||||
public int Height = 128;
|
||||
public int Margin = 32;
|
||||
|
||||
public BaseUserOption(IUExport export)
|
||||
{
|
||||
if (export.GetExport<TextProperty>("OptionDisplayName") is TextProperty optionDisplayName)
|
||||
OptionDisplayName = Text.GetTextPropertyBase(optionDisplayName).ToUpperInvariant();
|
||||
if (export.GetExport<TextProperty>("OptionDescription") is TextProperty optionDescription)
|
||||
{
|
||||
OptionDescription = Text.GetTextPropertyBase(optionDescription);
|
||||
if (!string.IsNullOrEmpty(OptionDescription))
|
||||
{
|
||||
Height += (int)descriptionPaint.TextSize * Helper.SplitLines(OptionDescription, descriptionPaint, Width - Margin).Length;
|
||||
Height += (int)descriptionPaint.TextSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (export.GetExport<ArrayProperty>("OptionValues") is ArrayProperty optionValues)
|
||||
{
|
||||
OptionValues = new List<Options>(optionValues.Value.Length);
|
||||
for (int i = 0; i < OptionValues.Capacity; i++)
|
||||
{
|
||||
if (optionValues.Value[i] is StructProperty s && s.Value is UObject option)
|
||||
{
|
||||
if (option.TryGetValue("DisplayName", out var v1) && v1 is TextProperty displayName)
|
||||
{
|
||||
var opt = new Options { Option = Text.GetTextPropertyBase(displayName).ToUpperInvariant() };
|
||||
if (option.TryGetValue("Value", out var v) && v is StructProperty value && value.Value is FLinearColor color)
|
||||
opt.Color = SKColor.Parse(color.Hex).WithAlpha(150);
|
||||
OptionValues.Add(opt);
|
||||
}
|
||||
else if (option.TryGetValue("PrimaryAssetName", out var v2) && v2 is NameProperty primaryAssetName)
|
||||
OptionValues.Add(new Options { Option = primaryAssetName.Value.String });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (export.GetExport<TextProperty>("OptionOnText") is TextProperty optionOnText)
|
||||
OptionValues.Add(new Options { Option = Text.GetTextPropertyBase(optionOnText).ToUpperInvariant() });
|
||||
if (export.GetExport<TextProperty>("OptionOffText") is TextProperty optionOffText)
|
||||
OptionValues.Add(new Options { Option = Text.GetTextPropertyBase(optionOffText).ToUpperInvariant() });
|
||||
|
||||
if (export.GetExport<IntProperty>("Min", "DefaultValue") is IntProperty iMin &&
|
||||
export.GetExport<IntProperty>("Max") is IntProperty iMax)
|
||||
{
|
||||
int increment = iMin.Value;
|
||||
if (export.GetExport<IntProperty>("IncrementValue") is IntProperty incrementValue)
|
||||
increment = incrementValue.Value;
|
||||
|
||||
for (int i = iMin.Value; i <= iMax.Value; i += increment)
|
||||
{
|
||||
OptionValues.Add(new Options { Option = i.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
if (export.GetExport<FloatProperty>("Min") is FloatProperty fMin &&
|
||||
export.GetExport<FloatProperty>("Max") is FloatProperty fMax)
|
||||
{
|
||||
float increment = fMin.Value;
|
||||
if (export.GetExport<FloatProperty>("IncrementValue") is FloatProperty incrementValue)
|
||||
increment = incrementValue.Value;
|
||||
|
||||
for (float i = fMin.Value; i <= fMax.Value; i += increment)
|
||||
{
|
||||
OptionValues.Add(new Options { Option = i.ToString() });
|
||||
}
|
||||
}
|
||||
|
||||
Height += Margin;
|
||||
Height += 35 * OptionValues.Count;
|
||||
}
|
||||
|
||||
public void Draw(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateLinearGradient(
|
||||
new SKPoint(Width / 2, Height),
|
||||
new SKPoint(Width, Height / 4),
|
||||
new SKColor[2] { SKColor.Parse("01369C"), SKColor.Parse("1273C8") },
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
|
||||
int textSize = 45;
|
||||
SKPaint namePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = textSize,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Left
|
||||
};
|
||||
|
||||
// resize if too long
|
||||
while (namePaint.MeasureText(OptionDisplayName) > (Width - (Margin * 2)))
|
||||
{
|
||||
namePaint.TextSize = textSize -= 2;
|
||||
}
|
||||
int y = Margin + textSize;
|
||||
c.DrawText(OptionDisplayName, Margin, y, namePaint);
|
||||
y += (int)descriptionPaint.TextSize + (Margin / 2);
|
||||
|
||||
// wrap if too long
|
||||
Helper.DrawMultilineText(c, OptionDescription, Width, Margin, ETextSide.Left,
|
||||
new SKRect(Margin, y, Width - Margin, 256), descriptionPaint, out int top);
|
||||
|
||||
int height = 30;
|
||||
int space = 5;
|
||||
foreach (Options option in OptionValues)
|
||||
{
|
||||
c.DrawRect(new SKRect(Margin, top, Width - Margin, top + height),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = option.Color
|
||||
});
|
||||
|
||||
int ts = 20;
|
||||
c.DrawText(option.Option, Margin + (space * 2), top + (ts * 1.1f),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = ts,
|
||||
Color = SKColor.Parse("EEFFFF"),
|
||||
TextAlign = SKTextAlign.Left
|
||||
});
|
||||
|
||||
top += height + space;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseBundle : UCreator
|
||||
{
|
||||
private IList<BaseQuest> _quests;
|
||||
private const int _headerHeight = 100;
|
||||
|
||||
public BaseBundle(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Width = 1024;
|
||||
Height = _headerHeight;
|
||||
Margin = 0;
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
_quests = new List<BaseQuest>();
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName"))
|
||||
DisplayName = displayName.Text.ToUpperInvariant();
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback[] quests, "QuestInfos")) // prout :)
|
||||
{
|
||||
foreach (var quest in quests)
|
||||
{
|
||||
if (!quest.TryGetValue(out FSoftObjectPath questDefinition, "QuestDefinition")) continue;
|
||||
|
||||
BaseQuest q;
|
||||
var path = questDefinition.AssetPathName.Text;
|
||||
do
|
||||
{
|
||||
if (!Utils.TryLoadObject(path, out UObject uObject)) break;
|
||||
|
||||
q = new BaseQuest(uObject, Style);
|
||||
q.ParseForInfo();
|
||||
_quests.Add(q);
|
||||
path = path.SubstringBeforeWithLast('/') + q.NextQuestName + "." + q.NextQuestName;
|
||||
} while (!string.IsNullOrEmpty(q.NextQuestName));
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback[] completionRewards, "BundleCompletionRewards"))
|
||||
{
|
||||
foreach (var completionReward in completionRewards)
|
||||
{
|
||||
if (!completionReward.TryGetValue(out int completionCount, "CompletionCount") ||
|
||||
!completionReward.TryGetValue(out FStructFallback[] rewards, "Rewards")) continue;
|
||||
|
||||
foreach (var reward in rewards)
|
||||
{
|
||||
if (!reward.TryGetValue(out int quantity, "Quantity") ||
|
||||
!reward.TryGetValue(out string templateId, "TemplateId") ||
|
||||
!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition")) continue;
|
||||
|
||||
if (!itemDefinition.AssetPathName.IsNone &&
|
||||
!itemDefinition.AssetPathName.Text.Contains("/Items/Tokens/") &&
|
||||
!itemDefinition.AssetPathName.Text.Contains("/Items/Quests"))
|
||||
{
|
||||
_quests.Add(new BaseQuest(completionCount, itemDefinition, Style));
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(templateId))
|
||||
{
|
||||
_quests.Add(new BaseQuest(completionCount, quantity, templateId, Style));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Height += 256 * _quests.Count;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawHeader(c);
|
||||
DrawDisplayName(c);
|
||||
DrawQuests(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private readonly SKPaint _headerPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.Bundle, TextSize = 50,
|
||||
TextAlign = SKTextAlign.Center, Color = SKColor.Parse("#262630")
|
||||
};
|
||||
|
||||
private void DrawHeader(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, Width, _headerHeight), _headerPaint);
|
||||
|
||||
var background = _quests.Count > 0 ? _quests[0].Background : Background;
|
||||
_headerPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
|
||||
new[] { background[0].WithAlpha(50), background[1].WithAlpha(50) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(0, 0, Width, _headerHeight), _headerPaint);
|
||||
|
||||
_headerPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
|
||||
new[] { SKColors.Black.WithAlpha(25), background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _headerPaint);
|
||||
}
|
||||
|
||||
private new void DrawDisplayName(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DisplayName)) return;
|
||||
|
||||
_headerPaint.Shader = null;
|
||||
_headerPaint.Color = SKColors.White;
|
||||
while (_headerPaint.MeasureText(DisplayName) > Width)
|
||||
{
|
||||
_headerPaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(_headerPaint.Typeface);
|
||||
c.DrawShapedText(shaper, DisplayName, Width / 2f, _headerHeight / 2f + _headerPaint.TextSize / 2 - 10, _headerPaint);
|
||||
}
|
||||
|
||||
private void DrawQuests(SKCanvas c)
|
||||
{
|
||||
var y = _headerHeight;
|
||||
foreach (var quest in _quests)
|
||||
{
|
||||
quest.DrawQuest(c, y);
|
||||
y += quest.Height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
using System.Linq;
|
||||
using CUE4Parse.GameTypes.FN.Enums;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.GameplayTags;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels.ApiEndpoints.Models;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseCommunity : BaseIcon
|
||||
{
|
||||
private readonly CommunityDesign _design;
|
||||
private string _rarityName;
|
||||
private string _source;
|
||||
private string _season;
|
||||
private bool _lowerDrawn;
|
||||
|
||||
public BaseCommunity(UObject uObject, EIconStyle style, string designName) : base(uObject, style)
|
||||
{
|
||||
Margin = 0;
|
||||
_lowerDrawn = false;
|
||||
_design = ApplicationService.ApiEndpointView.FModelApi.GetDesign(designName);
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
|
||||
|
||||
if (Object.TryGetValue(out FPackageIndex series, "Series"))
|
||||
{
|
||||
_rarityName = series.Name;
|
||||
}
|
||||
else if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList") &&
|
||||
dataList.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FPackageIndex _, "Series") == true) is { } dl)
|
||||
{
|
||||
_rarityName = dl.NonConstStruct?.Get<FPackageIndex>("Series").Name;
|
||||
}
|
||||
else if (Object.TryGetValue(out FStructFallback componentContainer, "ComponentContainer") &&
|
||||
componentContainer.TryGetValue(out FPackageIndex[] components, "Components") &&
|
||||
components.FirstOrDefault(c => c.Name.Contains("Series")) is { } seriesDef &&
|
||||
seriesDef.TryLoad(out var seriesDefObj) && seriesDefObj is not null &&
|
||||
seriesDefObj.TryGetValue(out series, "Series"))
|
||||
{
|
||||
_rarityName = series.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
_rarityName = Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription();
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
|
||||
CheckGameplayTags(gameplayTags);
|
||||
if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item"))
|
||||
CosmeticSource = cosmeticItem.Name.ToUpper();
|
||||
|
||||
DisplayName = DisplayName.ToUpper();
|
||||
Description = Description.ToUpper();
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
if (_design == null)
|
||||
{
|
||||
base.Draw(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
if (_design.DrawSeason && _design.Fonts.TryGetValue("Season", out var font))
|
||||
DrawToBottom(c, font, _season);
|
||||
if (_design.DrawSource && _design.Fonts.TryGetValue("Source", out font))
|
||||
DrawToBottom(c, font, _source);
|
||||
DrawUserFacingFlags(c, _design.GameplayTags.DrawCustomOnly);
|
||||
}
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void CheckGameplayTags(FGameplayTagContainer gameplayTags)
|
||||
{
|
||||
if (_design == null) return;
|
||||
if (_design.DrawSource)
|
||||
{
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source))
|
||||
_source = source.Text["Cosmetics.Source.".Length..].ToUpper();
|
||||
else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action))
|
||||
_source = action.Text["Athena.ItemAction.".Length..].ToUpper();
|
||||
}
|
||||
|
||||
if (_design.DrawSet && gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set))
|
||||
Description += GetCosmeticSet(set.Text, _design.DrawSetShort);
|
||||
if (_design.DrawSeason && gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
|
||||
_season = GetCosmeticSeason(season.Text, _design.DrawSeasonShort);
|
||||
|
||||
var triggers = _design.GameplayTags.DrawCustomOnly ? new[] { "Cosmetics.UserFacingFlags." } : new[] { "Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender." };
|
||||
GetUserFacingFlags(gameplayTags.GetAllGameplayTags(triggers));
|
||||
}
|
||||
|
||||
private string GetCosmeticSet(string setName, bool bShort)
|
||||
{
|
||||
return bShort ? setName["Cosmetics.Set.".Length..] : base.GetCosmeticSet(setName);
|
||||
}
|
||||
|
||||
private string GetCosmeticSeason(string seasonNumber, bool bShort)
|
||||
{
|
||||
if (!bShort) return base.GetCosmeticSeason(seasonNumber);
|
||||
var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
|
||||
(int chapterIdx, int seasonIdx) = GetInternalSID(int.Parse(s));
|
||||
return $"C{chapterIdx} S{seasonIdx}";
|
||||
}
|
||||
|
||||
private new void DrawBackground(SKCanvas c)
|
||||
{
|
||||
if (_design.Rarities.TryGetValue(_rarityName, out var rarity))
|
||||
{
|
||||
c.DrawBitmap(rarity.Background, 0, 0, ImagePaint);
|
||||
c.DrawBitmap(rarity.Upper, 0, 0, ImagePaint);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DrawBackground(c);
|
||||
}
|
||||
}
|
||||
|
||||
private new void DrawTextBackground(SKCanvas c)
|
||||
{
|
||||
if (!_lowerDrawn && string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description)) return;
|
||||
|
||||
_lowerDrawn = true;
|
||||
if (_design.Rarities.TryGetValue(_rarityName, out var rarity))
|
||||
{
|
||||
c.DrawBitmap(rarity.Lower, 0, 0, ImagePaint);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DrawTextBackground(c);
|
||||
}
|
||||
}
|
||||
|
||||
private new void DrawDisplayName(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DisplayName)) return;
|
||||
if (_design.Fonts.TryGetValue(nameof(DisplayName), out var font))
|
||||
{
|
||||
DisplayNamePaint.TextSize = font.FontSize;
|
||||
DisplayNamePaint.TextScaleX = font.FontScale;
|
||||
DisplayNamePaint.Color = font.FontColor;
|
||||
DisplayNamePaint.TextSkewX = font.SkewValue;
|
||||
DisplayNamePaint.TextAlign = font.Alignment;
|
||||
if (font.ShadowValue > 0)
|
||||
DisplayNamePaint.ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0, font.ShadowValue));
|
||||
if (font.Typeface.TryGetValue(UserSettings.Default.AssetLanguage, out var path) ||
|
||||
font.Typeface.TryGetValue(ELanguage.English, out path))
|
||||
DisplayNamePaint.Typeface = Utils.Typefaces.OnTheFly(path);
|
||||
|
||||
while (DisplayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
|
||||
{
|
||||
DisplayNamePaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
|
||||
var x = font.Alignment switch
|
||||
{
|
||||
SKTextAlign.Center => Width / 2f,
|
||||
_ => font.X
|
||||
};
|
||||
|
||||
c.DrawShapedText(shaper, DisplayName, x, font.Y, DisplayNamePaint);
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DrawDisplayName(c);
|
||||
}
|
||||
}
|
||||
|
||||
private new void DrawDescription(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Description)) return;
|
||||
if (_design.Fonts.TryGetValue(nameof(Description), out var font))
|
||||
{
|
||||
DescriptionPaint.TextSize = font.FontSize;
|
||||
DescriptionPaint.TextScaleX = font.FontScale;
|
||||
DescriptionPaint.Color = font.FontColor;
|
||||
DescriptionPaint.TextSkewX = font.SkewValue;
|
||||
DescriptionPaint.TextAlign = font.Alignment;
|
||||
if (font.ShadowValue > 0)
|
||||
DescriptionPaint.ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0, font.ShadowValue));
|
||||
if (font.Typeface.TryGetValue(UserSettings.Default.AssetLanguage, out var path) ||
|
||||
font.Typeface.TryGetValue(ELanguage.English, out path))
|
||||
DescriptionPaint.Typeface = Utils.Typefaces.OnTheFly(path);
|
||||
|
||||
while (DescriptionPaint.MeasureText(Description) > Width - Margin * 2)
|
||||
{
|
||||
DescriptionPaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(DescriptionPaint.Typeface);
|
||||
var x = font.Alignment switch
|
||||
{
|
||||
SKTextAlign.Center => Width / 2f,
|
||||
_ => font.X
|
||||
};
|
||||
|
||||
if (font.MaxLineCount < 2)
|
||||
{
|
||||
c.DrawShapedText(shaper, Description, x, font.Y, DescriptionPaint);
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.DrawMultilineText(c, Description, Width - Margin * 2, Margin, DescriptionPaint.TextAlign,
|
||||
new SKRect(Margin, font.Y, Width - Margin, Height), DescriptionPaint, out _);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.DrawDescription(c);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawToBottom(SKCanvas c, FontDesign font, string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return;
|
||||
if (!_lowerDrawn)
|
||||
{
|
||||
_lowerDrawn = true;
|
||||
DrawTextBackground(c);
|
||||
}
|
||||
|
||||
DisplayNamePaint.TextSize = font.FontSize;
|
||||
DisplayNamePaint.TextScaleX = font.FontScale;
|
||||
DisplayNamePaint.Color = font.FontColor;
|
||||
DisplayNamePaint.TextSkewX = font.SkewValue;
|
||||
DisplayNamePaint.TextAlign = font.Alignment;
|
||||
if (font.ShadowValue > 0)
|
||||
DisplayNamePaint.ImageFilter = SKImageFilter.CreateDropShadow(2, 2, 4, 4, new SKColor(0, 0, 0, font.ShadowValue));
|
||||
if (font.Typeface.TryGetValue(UserSettings.Default.AssetLanguage, out var path) ||
|
||||
font.Typeface.TryGetValue(ELanguage.English, out path))
|
||||
DisplayNamePaint.Typeface = Utils.Typefaces.OnTheFly(path);
|
||||
|
||||
var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
|
||||
var x = font.Alignment switch
|
||||
{
|
||||
SKTextAlign.Center => Width / 2f,
|
||||
_ => font.X
|
||||
};
|
||||
|
||||
c.DrawShapedText(shaper, text, x, font.Y, DisplayNamePaint);
|
||||
}
|
||||
|
||||
private void DrawUserFacingFlags(SKCanvas c, bool customOnly)
|
||||
{
|
||||
if (UserFacingFlags == null || UserFacingFlags.Count < 1) return;
|
||||
if (customOnly)
|
||||
{
|
||||
c.DrawBitmap(_design.GameplayTags.Custom, 0, 0, ImagePaint);
|
||||
}
|
||||
else
|
||||
{
|
||||
// add size to api
|
||||
// draw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,325 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using CUE4Parse.GameTypes.FN.Enums;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.GameplayTags;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using FModel.Settings;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseIcon : UCreator
|
||||
{
|
||||
public SKBitmap SeriesBackground { get; protected set; }
|
||||
protected string ShortDescription { get; set; }
|
||||
protected string CosmeticSource { get; set; }
|
||||
protected Dictionary<string, SKBitmap> UserFacingFlags { get; set; }
|
||||
|
||||
public BaseIcon(UObject uObject, EIconStyle style) : base(uObject, style) { }
|
||||
|
||||
public void ParseForReward(bool isUsingDisplayAsset)
|
||||
{
|
||||
// rarity
|
||||
if (Object.TryGetValue(out FPackageIndex series, "Series")) GetSeries(series);
|
||||
else if (Object.TryGetValue(out FStructFallback componentContainer, "ComponentContainer")) GetSeries(componentContainer);
|
||||
else GetRarity(Object.GetOrDefault("Rarity", EFortRarity.Uncommon)); // default is uncommon
|
||||
|
||||
if (Object.TryGetValue(out FInstancedStruct[] dataList, "DataList"))
|
||||
{
|
||||
GetSeries(dataList);
|
||||
Preview = Utils.GetBitmap(dataList);
|
||||
}
|
||||
|
||||
// preview
|
||||
if (Preview is null)
|
||||
{
|
||||
if (isUsingDisplayAsset && Utils.TryGetDisplayAsset(Object, out var preview))
|
||||
Preview = preview;
|
||||
else if (Object.TryGetValue(out FPackageIndex itemDefinition, "HeroDefinition", "WeaponDefinition"))
|
||||
Preview = Utils.GetBitmap(itemDefinition);
|
||||
else if (Object.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "EntryListIcon", "SmallPreviewImage", "BundleImage", "ItemDisplayAsset", "LargeIcon", "ToastIcon", "SmallIcon"))
|
||||
Preview = Utils.GetBitmap(largePreview);
|
||||
else if (Object.TryGetValue(out string s, "LargePreviewImage") && !string.IsNullOrEmpty(s))
|
||||
Preview = Utils.GetBitmap(s);
|
||||
else if (Object.TryGetValue(out FPackageIndex otherPreview, "SmallPreviewImage", "ToastIcon", "access_item"))
|
||||
Preview = Utils.GetBitmap(otherPreview);
|
||||
else if (Object.TryGetValue(out UMaterialInstanceConstant materialInstancePreview, "EventCalloutImage"))
|
||||
Preview = Utils.GetBitmap(materialInstancePreview);
|
||||
else if (Object.TryGetValue(out FStructFallback brush, "IconBrush") && brush.TryGetValue(out UTexture2D res, "ResourceObject"))
|
||||
Preview = Utils.GetBitmap(res);
|
||||
}
|
||||
|
||||
// text
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName", "BundleName", "DefaultHeaderText", "UIDisplayName", "EntryName", "EventCalloutTitle"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "Description", "ItemDescription", "BundleDescription", "GeneralDescription", "DefaultBodyText", "UIDescription", "UIDisplayDescription", "EntryDescription", "EventCalloutDescription"))
|
||||
Description = description.Text;
|
||||
else if (Object.TryGetValue(out FText[] descriptions, "Description"))
|
||||
Description = string.Join('\n', descriptions.Select(x => x.Text));
|
||||
if (Object.TryGetValue(out FText shortDescription, "ShortDescription", "UIDisplaySubName"))
|
||||
ShortDescription = shortDescription.Text;
|
||||
else if (Object.ExportType.Equals("AthenaItemWrapDefinition", StringComparison.OrdinalIgnoreCase))
|
||||
ShortDescription = Utils.GetLocalizedResource("Fort.Cosmetics", "ItemWrapShortDescription", "Wrap");
|
||||
|
||||
// Only works on non-cataba designs
|
||||
if (Object.TryGetValue(out FStructFallback eventArrowColor, "EventArrowColor") &&
|
||||
eventArrowColor.TryGetValue(out FLinearColor specifiedArrowColor, "SpecifiedColor") &&
|
||||
Object.TryGetValue(out FStructFallback eventArrowShadowColor, "EventArrowShadowColor") &&
|
||||
eventArrowShadowColor.TryGetValue(out FLinearColor specifiedShadowColor, "SpecifiedColor"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(specifiedArrowColor.Hex), SKColor.Parse(specifiedShadowColor.Hex) };
|
||||
Border = new[] { SKColor.Parse(specifiedShadowColor.Hex), SKColor.Parse(specifiedArrowColor.Hex) };
|
||||
}
|
||||
|
||||
Description = Utils.RemoveHtmlTags(Description);
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
ParseForReward(UserSettings.Default.CosmeticDisplayAsset);
|
||||
|
||||
if (Object.TryGetValue(out FGameplayTagContainer gameplayTags, "GameplayTags"))
|
||||
CheckGameplayTags(gameplayTags);
|
||||
if (Object.TryGetValue(out FPackageIndex cosmeticItem, "cosmetic_item"))
|
||||
CosmeticSource = cosmeticItem.Name;
|
||||
}
|
||||
|
||||
protected void Draw(SKCanvas c)
|
||||
{
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.NoBackground:
|
||||
DrawPreview(c);
|
||||
break;
|
||||
case EIconStyle.NoText:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawUserFacingFlags(c);
|
||||
break;
|
||||
default:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
DrawToBottom(c, SKTextAlign.Right, CosmeticSource);
|
||||
if (Description != ShortDescription)
|
||||
DrawToBottom(c, SKTextAlign.Left, ShortDescription);
|
||||
DrawUserFacingFlags(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
Draw(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void GetSeries(FPackageIndex s)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(s, out UObject export)) return;
|
||||
|
||||
GetSeries(export);
|
||||
}
|
||||
|
||||
private void GetSeries(FInstancedStruct[] s)
|
||||
{
|
||||
if (s.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FPackageIndex _, "Series") == true) is { } dl)
|
||||
GetSeries(dl.NonConstStruct?.Get<FPackageIndex>("Series"));
|
||||
}
|
||||
|
||||
private void GetSeries(FStructFallback s)
|
||||
{
|
||||
if (!s.TryGetValue(out FPackageIndex[] components, "Components")) return;
|
||||
if (components.FirstOrDefault(c => c.Name.Contains("Series")) is not { } seriesDef ||
|
||||
!seriesDef.TryLoad(out var seriesDefObj) || seriesDefObj is null ||
|
||||
!seriesDefObj.TryGetValue(out UObject series, "Series")) return;
|
||||
|
||||
GetSeries(series);
|
||||
}
|
||||
|
||||
protected void GetSeries(UObject uObject)
|
||||
{
|
||||
if (uObject is UTexture2D texture2D)
|
||||
{
|
||||
SeriesBackground = texture2D.Decode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (uObject.TryGetValue(out FSoftObjectPath backgroundTexture, "BackgroundTexture"))
|
||||
{
|
||||
SeriesBackground = Utils.GetBitmap(backgroundTexture);
|
||||
}
|
||||
|
||||
if (uObject.TryGetValue(out FStructFallback colors, "Colors") &&
|
||||
colors.TryGetValue(out FLinearColor color1, "Color1") &&
|
||||
colors.TryGetValue(out FLinearColor color2, "Color2") &&
|
||||
colors.TryGetValue(out FLinearColor color3, "Color3"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
|
||||
Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
|
||||
if (uObject.Name.Equals("PlatformSeries") &&
|
||||
uObject.TryGetValue(out FSoftObjectPath itemCardMaterial, "ItemCardMaterial") &&
|
||||
Utils.TryLoadObject(itemCardMaterial.AssetPathName.Text, out UMaterialInstanceConstant material))
|
||||
{
|
||||
foreach (var vectorParameter in material.VectorParameterValues)
|
||||
{
|
||||
if (vectorParameter.ParameterValue == null || !vectorParameter.ParameterInfo.Name.Text.Equals("ColorCircuitBackground"))
|
||||
continue;
|
||||
|
||||
Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetRarity(EFortRarity r)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Balance/RarityData.RarityData", out UObject export)) return;
|
||||
|
||||
if (export.GetByIndex<FStructFallback>((int) r) is { } data &&
|
||||
data.TryGetValue(out FLinearColor color1, "Color1") &&
|
||||
data.TryGetValue(out FLinearColor color2, "Color2") &&
|
||||
data.TryGetValue(out FLinearColor color3, "Color3"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
|
||||
Border = new[] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
}
|
||||
|
||||
protected string GetCosmeticSet(string setName)
|
||||
{
|
||||
if (!Utils.TryLoadObject("FortniteGame/Content/Athena/Items/Cosmetics/Metadata/CosmeticSets.CosmeticSets", out UDataTable cosmeticSets))
|
||||
return string.Empty;
|
||||
|
||||
if (!cosmeticSets.TryGetDataTableRow(setName, StringComparison.OrdinalIgnoreCase, out var uObject))
|
||||
return string.Empty;
|
||||
|
||||
var name = string.Empty;
|
||||
if (uObject.TryGetValue(out FText displayName, "DisplayName"))
|
||||
name = displayName.Text;
|
||||
|
||||
var format = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_SetMembership_NotRich", "\nPart of the {0} set.");
|
||||
return string.Format(format, name);
|
||||
}
|
||||
|
||||
protected (int, int) GetInternalSID(int number)
|
||||
{
|
||||
static int GetSeasonsInChapter(int chapter) => chapter switch
|
||||
{
|
||||
1 => 10,
|
||||
2 => 8,
|
||||
3 => 4,
|
||||
4 => 5,
|
||||
_ => 10
|
||||
};
|
||||
|
||||
var chapterIdx = 0;
|
||||
var seasonIdx = 0;
|
||||
while (number > 0)
|
||||
{
|
||||
var seasonsInChapter = GetSeasonsInChapter(++chapterIdx);
|
||||
if (number > seasonsInChapter)
|
||||
number -= seasonsInChapter;
|
||||
else
|
||||
{
|
||||
seasonIdx = number;
|
||||
number = 0;
|
||||
}
|
||||
}
|
||||
return (chapterIdx, seasonIdx);
|
||||
}
|
||||
|
||||
protected string GetCosmeticSeason(string seasonNumber)
|
||||
{
|
||||
var s = seasonNumber["Cosmetics.Filter.Season.".Length..];
|
||||
var initial = int.Parse(s);
|
||||
(int chapterIdx, int seasonIdx) = GetInternalSID(initial);
|
||||
|
||||
var season = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
|
||||
var introduced = Utils.GetLocalizedResource("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in <SeasonText>{0}</>.");
|
||||
if (initial <= 10) return Utils.RemoveHtmlTags(string.Format(introduced, string.Format(season, s)));
|
||||
|
||||
var chapter = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}");
|
||||
var chapterFormat = Utils.GetLocalizedResource("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}");
|
||||
var d = string.Format(chapterFormat, string.Format(chapter, chapterIdx), string.Format(season, seasonIdx));
|
||||
return Utils.RemoveHtmlTags(string.Format(introduced, d));
|
||||
}
|
||||
|
||||
private void CheckGameplayTags(FGameplayTagContainer gameplayTags)
|
||||
{
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source))
|
||||
CosmeticSource = source.Text["Cosmetics.Source.".Length..];
|
||||
else if (gameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action))
|
||||
CosmeticSource = action.Text["Athena.ItemAction.".Length..];
|
||||
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set))
|
||||
Description += GetCosmeticSet(set.Text);
|
||||
if (gameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
|
||||
Description += GetCosmeticSeason(season.Text);
|
||||
|
||||
GetUserFacingFlags(gameplayTags.GetAllGameplayTags(
|
||||
"Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender."));
|
||||
}
|
||||
|
||||
protected void GetUserFacingFlags(IList<string> userFacingFlags)
|
||||
{
|
||||
if (userFacingFlags.Count < 1 || !Utils.TryLoadObject("FortniteGame/Content/Items/ItemCategories.ItemCategories", out UObject itemCategories))
|
||||
return;
|
||||
|
||||
if (!itemCategories.TryGetValue(out FStructFallback[] tertiaryCategories, "TertiaryCategories"))
|
||||
return;
|
||||
|
||||
UserFacingFlags = new Dictionary<string, SKBitmap>(userFacingFlags.Count);
|
||||
foreach (var flag in userFacingFlags)
|
||||
{
|
||||
if (flag.Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Object.ExportType.Equals("AthenaPetCarrierItemDefinition", StringComparison.OrdinalIgnoreCase))
|
||||
UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png"))?.Stream);
|
||||
else UserFacingFlags[flag] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png"))?.Stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var category in tertiaryCategories)
|
||||
{
|
||||
if (category.TryGetValue(out FGameplayTagContainer tagContainer, "TagContainer") && tagContainer.TryGetGameplayTag(flag, out _) &&
|
||||
category.TryGetValue(out FStructFallback categoryBrush, "CategoryBrush") && categoryBrush.TryGetValue(out FStructFallback brushXxs, "Brush_XXS") &&
|
||||
brushXxs.TryGetValue(out FPackageIndex resourceObject, "ResourceObject") && Utils.TryGetPackageIndexExport(resourceObject, out UTexture2D texture))
|
||||
{
|
||||
UserFacingFlags[flag] = Utils.GetBitmap(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawUserFacingFlags(SKCanvas c)
|
||||
{
|
||||
if (UserFacingFlags == null) return;
|
||||
|
||||
const int size = 25;
|
||||
var x = Margin * (int) 2.5;
|
||||
foreach (var flag in UserFacingFlags.Values.Where(flag => flag != null))
|
||||
{
|
||||
c.DrawBitmap(flag.Resize(size), new SKPoint(x, Margin * (int) 2.5), ImagePaint);
|
||||
x += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,293 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CUE4Parse.GameTypes.FN.Enums;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Engine.Curves;
|
||||
using CUE4Parse.UE4.Objects.GameplayTags;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseIconStats : BaseIcon
|
||||
{
|
||||
private readonly IList<IconStat> _statistics;
|
||||
private const int _headerHeight = 128;
|
||||
private bool _screenLayer;
|
||||
|
||||
public BaseIconStats(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Width = 1024;
|
||||
Height = _headerHeight;
|
||||
Margin = 0;
|
||||
_statistics = new List<IconStat>();
|
||||
_screenLayer = uObject.ExportType.Equals("FortAccoladeItemDefinition", StringComparison.OrdinalIgnoreCase);
|
||||
DefaultPreview = Utils.GetBitmap("FortniteGame/Content/Athena/HUD/Quests/Art/T_NPC_Default.T_NPC_Default");
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
base.ParseForInfo();
|
||||
DisplayName = DisplayName.ToUpperInvariant();
|
||||
|
||||
if (Object.TryGetValue(out FName accoladeType, "AccoladeType") &&
|
||||
accoladeType.Text.Equals("EFortAccoladeType::Medal", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_screenLayer = false;
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FGameplayTagContainer poiLocations, "POILocations") &&
|
||||
Utils.TryLoadObject("FortniteGame/Content/Quests/QuestIndicatorData.QuestIndicatorData", out UObject uObject) &&
|
||||
uObject.TryGetValue(out FStructFallback[] challengeMapPoiData, "ChallengeMapPoiData"))
|
||||
{
|
||||
foreach (var location in poiLocations)
|
||||
{
|
||||
var locationName = "Unknown";
|
||||
foreach (var poi in challengeMapPoiData)
|
||||
{
|
||||
if (!poi.TryGetValue(out FStructFallback locationTag, "LocationTag") || !locationTag.TryGetValue(out FName tagName, "TagName") ||
|
||||
tagName != location.TagName || !poi.TryGetValue(out FText text, "Text")) continue;
|
||||
|
||||
locationName = text.Text;
|
||||
break;
|
||||
}
|
||||
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "B0C091D7409B1657423C5F97E9CF4C77", "LOCATION NAME"), locationName.ToUpper()));
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback maxStackSize, "MaxStackSize"))
|
||||
{
|
||||
if (maxStackSize.TryGetValue(out float v, "Value") && v > 0)
|
||||
{
|
||||
_statistics.Add(new IconStat("Max Stack", v, 15));
|
||||
}
|
||||
else if (TryGetCurveTableStat(maxStackSize, out var s))
|
||||
{
|
||||
_statistics.Add(new IconStat("Max Stack", s, 15));
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback xpRewardAmount, "XpRewardAmount") && TryGetCurveTableStat(xpRewardAmount, out var x))
|
||||
{
|
||||
_statistics.Add(new IconStat("XP Amount", x));
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback weaponStatHandle, "WeaponStatHandle") &&
|
||||
weaponStatHandle.TryGetValue(out FName weaponRowName, "RowName") &&
|
||||
weaponStatHandle.TryGetValue(out UDataTable dataTable, "DataTable") &&
|
||||
dataTable.TryGetDataTableRow(weaponRowName.Text, StringComparison.OrdinalIgnoreCase, out var weaponRowValue))
|
||||
{
|
||||
if (weaponRowValue.TryGetValue(out int bpc, "BulletsPerCartridge"))
|
||||
{
|
||||
var multiplier = bpc != 0f ? bpc : 1;
|
||||
if (weaponRowValue.TryGetValue(out float dmgPb, "DmgPB") && dmgPb != 0f)
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "35D04D1B45737BEA25B69686D9E085B9", "Damage"), dmgPb * multiplier, 200));
|
||||
}
|
||||
|
||||
if (weaponRowValue.TryGetValue(out float mdpc, "MaxDamagePerCartridge") && mdpc >= 0f)
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), mdpc, 200));
|
||||
}
|
||||
else if (weaponRowValue.TryGetValue(out float dmgCritical, "DamageZone_Critical"))
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "0DEF2455463B008C4499FEA03D149EDF", "Headshot Damage"), dmgPb * dmgCritical * multiplier, 200));
|
||||
}
|
||||
}
|
||||
|
||||
if (weaponRowValue.TryGetValue(out int clipSize, "ClipSize") && clipSize != 0)
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "068239DD4327B36124498C9C5F61C038", "Magazine Size"), clipSize, 50));
|
||||
}
|
||||
|
||||
if (weaponRowValue.TryGetValue(out float firingRate, "FiringRate") && firingRate != 0f)
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "27B80BA44805ABD5A2D2BAB2902B250C", "Fire Rate"), firingRate, 15));
|
||||
}
|
||||
|
||||
if (weaponRowValue.TryGetValue(out float armTime, "ArmTime") && armTime != 0f)
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "3BFEB8BD41A677CC5F45B9A90D6EAD6F", "Arming Delay"), armTime, 125));
|
||||
}
|
||||
|
||||
if (weaponRowValue.TryGetValue(out float reloadTime, "ReloadTime") && reloadTime != 0f)
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6EA26D1A4252034FBD869A90F9A6E49A", "Reload Time"), reloadTime, 15));
|
||||
}
|
||||
|
||||
if ((Object.ExportType.Equals("FortContextTrapItemDefinition", StringComparison.OrdinalIgnoreCase) ||
|
||||
Object.ExportType.Equals("FortTrapItemDefinition", StringComparison.OrdinalIgnoreCase)) &&
|
||||
weaponRowValue.TryGetValue(out UDataTable durabilityTable, "Durability") &&
|
||||
weaponRowValue.TryGetValue(out FName durabilityRowName, "DurabilityRowName") &&
|
||||
durabilityTable.TryGetDataTableRow(durabilityRowName.Text, StringComparison.OrdinalIgnoreCase, out var durability) &&
|
||||
durability.TryGetValue(out int duraByRarity, Object.GetOrDefault("Rarity", EFortRarity.Uncommon).GetDescription()))
|
||||
{
|
||||
_statistics.Add(new IconStat(Utils.GetLocalizedResource("", "6FA2882140CB69DE32FD73A392F0585B", "Durability"), duraByRarity, 20));
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
Height += 40 + (int) _informationPaint.TextSize * Utils.SplitLines(Description, _informationPaint, Width - 20).Count;
|
||||
Height += 50 * _statistics.Count;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawHeader(c);
|
||||
DrawDisplayName(c);
|
||||
DrawStatistics(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private bool TryGetCurveTableStat(FStructFallback property, out float statValue)
|
||||
{
|
||||
if (property.TryGetValue(out FStructFallback curve, "Curve") &&
|
||||
curve.TryGetValue(out FName rowName, "RowName") &&
|
||||
curve.TryGetValue(out UCurveTable curveTable, "CurveTable") &&
|
||||
curveTable.TryFindCurve(rowName, out var rowValue) &&
|
||||
rowValue is FSimpleCurve s && s.Keys.Length > 0)
|
||||
{
|
||||
statValue = s.Keys[0].Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
statValue = 0F;
|
||||
return false;
|
||||
}
|
||||
|
||||
private readonly SKPaint _informationPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Color = SKColor.Parse("#262630"), TextSize = 16,
|
||||
Typeface = Utils.Typefaces.Description
|
||||
};
|
||||
|
||||
private void DrawHeader(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
|
||||
new[] { Background[0].WithAlpha(180), Background[1].WithAlpha(220) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(_headerHeight, 0, Width, _headerHeight), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
|
||||
new[] { SKColor.Parse("#262630"), SKColor.Parse("#1f1f26") }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(0, _headerHeight, Width, Height), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
|
||||
new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4, Background, SKShaderTileMode.Clamp);
|
||||
using var rect = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
rect.MoveTo(0, 0);
|
||||
rect.LineTo(_headerHeight + _headerHeight / 3, 0);
|
||||
rect.LineTo(_headerHeight, _headerHeight);
|
||||
rect.LineTo(0, _headerHeight);
|
||||
rect.Close();
|
||||
c.DrawPath(rect, _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(_headerHeight / 2, _headerHeight / 2), new SKPoint(_headerHeight / 2 + 100, _headerHeight / 2),
|
||||
new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
|
||||
c.DrawPath(rect, _informationPaint);
|
||||
|
||||
_informationPaint.Shader = null;
|
||||
|
||||
ImagePaint.BlendMode = _screenLayer ? SKBlendMode.Screen : Preview == null ? SKBlendMode.ColorBurn : SKBlendMode.SrcOver;
|
||||
c.DrawBitmap((Preview ?? DefaultPreview).Resize(_headerHeight), 0, 0, ImagePaint);
|
||||
}
|
||||
|
||||
private new void DrawDisplayName(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DisplayName)) return;
|
||||
|
||||
_informationPaint.TextSize = 50;
|
||||
_informationPaint.Color = SKColors.White;
|
||||
_informationPaint.Typeface = Utils.Typefaces.Bundle;
|
||||
while (_informationPaint.MeasureText(DisplayName) > Width - _headerHeight * 2)
|
||||
{
|
||||
_informationPaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(_informationPaint.Typeface);
|
||||
c.DrawShapedText(shaper, DisplayName, _headerHeight + _headerHeight / 3 + 10, _headerHeight / 2f + _informationPaint.TextSize / 3, _informationPaint);
|
||||
}
|
||||
|
||||
private void DrawStatistics(SKCanvas c)
|
||||
{
|
||||
var outY = _headerHeight + 25f;
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
_informationPaint.TextSize = 16;
|
||||
_informationPaint.Color = SKColors.White.WithAlpha(175);
|
||||
_informationPaint.Typeface = Utils.Typefaces.Description;
|
||||
Utils.DrawMultilineText(c, Description, Width - 40, 0, SKTextAlign.Center,
|
||||
new SKRect(20, outY, Width - 20, Height), _informationPaint, out outY);
|
||||
outY += 25;
|
||||
}
|
||||
|
||||
foreach (var stat in _statistics)
|
||||
{
|
||||
stat.Draw(c, Border[0].WithAlpha(100), Width, _headerHeight, ref outY);
|
||||
outY += 50;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IconStat
|
||||
{
|
||||
private readonly string _statName;
|
||||
private readonly object _value;
|
||||
private readonly float _maxValue;
|
||||
|
||||
public IconStat(string statName, object value, float maxValue = 0)
|
||||
{
|
||||
_statName = statName.ToUpperInvariant();
|
||||
_value = value;
|
||||
_maxValue = maxValue;
|
||||
}
|
||||
|
||||
private readonly SKPaint _statPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
TextSize = 25, Typeface = Utils.Typefaces.DisplayName,
|
||||
Color = SKColors.White
|
||||
};
|
||||
|
||||
public void Draw(SKCanvas c, SKColor sliderColor, int width, int height, ref float y)
|
||||
{
|
||||
while (_statPaint.MeasureText(_statName) > height * 2 - 40)
|
||||
{
|
||||
_statPaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(_statPaint.Typeface);
|
||||
c.DrawShapedText(shaper, _statName, 50, y + 10, _statPaint);
|
||||
|
||||
_statPaint.TextAlign = SKTextAlign.Right;
|
||||
_statPaint.Typeface = Utils.Typefaces.BundleNumber;
|
||||
_statPaint.Color = sliderColor;
|
||||
var sliderRight = width - 100 - _statPaint.MeasureText(_value.ToString());
|
||||
c.DrawRect(new SKRect(height * 2, y, Math.Min(width - height, sliderRight), y + 5), _statPaint);
|
||||
|
||||
_statPaint.Color = SKColors.White;
|
||||
c.DrawText(_value.ToString(), new SKPoint(width - 50, y + 10), _statPaint);
|
||||
|
||||
if (_maxValue < 1 || !float.TryParse(_value.ToString(), out var floatValue)) return;
|
||||
if (floatValue < 0)
|
||||
floatValue = 0;
|
||||
var sliderWidth = (sliderRight - height * 2) * (floatValue / _maxValue);
|
||||
c.DrawRect(new SKRect(height * 2, y, Math.Min(height * 2 + sliderWidth, sliderRight), y + 5), _statPaint);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseItemAccessToken : UCreator
|
||||
{
|
||||
private readonly SKBitmap _locked, _unlocked;
|
||||
private string _unlockedDescription, _exportName;
|
||||
private BaseIcon _icon;
|
||||
|
||||
public BaseItemAccessToken(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
_unlocked = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Locks/T-Icon-Unlocked-128.T-Icon-Unlocked-128").Resize(24);
|
||||
_locked = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Locks/T-Icon-Lock-128.T-Icon-Lock-128").Resize(24);
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FPackageIndex accessItem, "access_item") &&
|
||||
Utils.TryGetPackageIndexExport(accessItem, out UObject uObject))
|
||||
{
|
||||
_exportName = uObject.Name;
|
||||
_icon = new BaseIcon(uObject, EIconStyle.Default);
|
||||
_icon.ParseForReward(false);
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName") && displayName.Text != "TBD")
|
||||
DisplayName = displayName.Text;
|
||||
else
|
||||
DisplayName = _icon?.DisplayName;
|
||||
|
||||
Description = Object.TryGetValue(out FText description, "Description", "ItemDescription") ? description.Text : _icon?.Description;
|
||||
if (Object.TryGetValue(out FText unlockDescription, "UnlockDescription")) _unlockedDescription = unlockDescription.Text;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.NoBackground:
|
||||
Preview = _icon.Preview;
|
||||
DrawPreview(c);
|
||||
break;
|
||||
case EIconStyle.NoText:
|
||||
Preview = _icon.Preview;
|
||||
_icon.DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
break;
|
||||
default:
|
||||
_icon.DrawBackground(c);
|
||||
DrawInformation(c);
|
||||
DrawToBottom(c, SKTextAlign.Right, _exportName);
|
||||
break;
|
||||
}
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void DrawInformation(SKCanvas c)
|
||||
{
|
||||
var size = 45;
|
||||
var left = Width / 2;
|
||||
|
||||
while (DisplayNamePaint.MeasureText(DisplayName) > Width - _icon.Margin * 2)
|
||||
{
|
||||
DisplayNamePaint.TextSize = size -= 2;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
|
||||
var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
|
||||
c.DrawShapedText(shaper, DisplayName, left - shapedText.Width / 2, _icon.Margin * 8 + size, DisplayNamePaint);
|
||||
|
||||
float topBase = _icon.Margin + size * 2;
|
||||
if (!string.IsNullOrEmpty(_unlockedDescription))
|
||||
{
|
||||
c.DrawBitmap(_locked, new SKRect(50, topBase, 50 + _locked.Width, topBase + _locked.Height), ImagePaint);
|
||||
Utils.DrawMultilineText(c, _unlockedDescription, Width, _icon.Margin, SKTextAlign.Left,
|
||||
new SKRect(70 + _locked.Width, topBase + 10, Width - 50, 256), DescriptionPaint, out topBase);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(Description))
|
||||
{
|
||||
c.DrawBitmap(_unlocked, new SKRect(50, topBase, 50 + _unlocked.Width, topBase + _unlocked.Height), ImagePaint);
|
||||
Utils.DrawMultilineText(c, Description, Width, _icon.Margin, SKTextAlign.Left,
|
||||
new SKRect(70 + _unlocked.Width, topBase + 10, Width - 50, 256), DescriptionPaint, out topBase);
|
||||
}
|
||||
|
||||
var h = Width - _icon.Margin - topBase;
|
||||
c.DrawBitmap(_icon.Preview ?? _icon.DefaultPreview, new SKRect(left - h / 2, topBase, left + h / 2, Width - _icon.Margin), ImagePaint);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseJuno : BaseIcon
|
||||
{
|
||||
private BaseIcon _character;
|
||||
|
||||
public BaseJuno(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FSoftObjectPath baseCid, "BaseAthenaCharacterItemDefinition") &&
|
||||
Utils.TryLoadObject(baseCid.AssetPathName.Text, out UObject cid))
|
||||
{
|
||||
_character = new BaseIcon(cid, Style);
|
||||
_character.ParseForInfo();
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath assembledMeshSchema, "AssembledMeshSchema", "LowDetailsAssembledMeshSchema") &&
|
||||
Utils.TryLoadObject(assembledMeshSchema.AssetPathName.Text, out UObject meshSchema) &&
|
||||
meshSchema.TryGetValue(out FInstancedStruct[] additionalData, "AdditionalData"))
|
||||
{
|
||||
foreach (var data in additionalData)
|
||||
{
|
||||
if (data.NonConstStruct?.TryGetValue(out FSoftObjectPath largePreview, "LargePreviewImage", "SmallPreviewImage") ?? false)
|
||||
{
|
||||
_character.Preview = Utils.GetBitmap(largePreview);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath baseEid, "BaseAthenaDanceItemDefinition") &&
|
||||
Utils.TryLoadObject(baseEid.AssetPathName.Text, out UObject eid))
|
||||
{
|
||||
_character = new BaseIcon(eid, Style);
|
||||
_character.ParseForInfo();
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw() => _character.Draw();
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseMaterialInstance : BaseIcon
|
||||
{
|
||||
public BaseMaterialInstance(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Background = new[] { SKColor.Parse("4F4F69"), SKColor.Parse("4F4F69") };
|
||||
Border = new[] { SKColor.Parse("9092AB") };
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object is not UMaterialInstanceConstant material) return;
|
||||
|
||||
texture_finding:
|
||||
foreach (var textureParameter in material.TextureParameterValues) // get texture from base material
|
||||
{
|
||||
if (!textureParameter.ParameterValue.TryLoad<UTexture2D>(out var texture) || Preview != null) continue;
|
||||
switch (textureParameter.ParameterInfo.Name.Text)
|
||||
{
|
||||
case "SeriesTexture":
|
||||
GetSeries(texture);
|
||||
break;
|
||||
case "TextureA":
|
||||
case "TextureB":
|
||||
case "OfferImage":
|
||||
case "CarTexture":
|
||||
Preview = Utils.GetBitmap(texture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
while (material.VectorParameterValues.Length == 0 || // try to get color from parent if not found here
|
||||
material.VectorParameterValues.All(x => x.ParameterInfo.Name.Text.Equals("FallOff_Color"))) // use parent if it only contains FallOff_Color
|
||||
{
|
||||
if (material.TryGetValue(out FPackageIndex parent, "Parent"))
|
||||
Utils.TryGetPackageIndexExport(parent, out material);
|
||||
else return;
|
||||
|
||||
if (material == null) return;
|
||||
}
|
||||
|
||||
if (Preview == null)
|
||||
{
|
||||
if (material.TryGetValue(out FPackageIndex parent, "Parent"))
|
||||
Utils.TryGetPackageIndexExport(parent, out material);
|
||||
|
||||
goto texture_finding;
|
||||
}
|
||||
|
||||
foreach (var vectorParameter in material.VectorParameterValues)
|
||||
{
|
||||
if (vectorParameter.ParameterValue == null) continue;
|
||||
switch (vectorParameter.ParameterInfo.Name.Text)
|
||||
{
|
||||
case "Background_Color_A":
|
||||
Background[0] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
|
||||
Border[0] = Background[0];
|
||||
break;
|
||||
case "Background_Color_B": // Border color can be defaulted here in some case where Background_Color_A should be taken from parent but Background_Color_B from base
|
||||
Background[1] = SKColor.Parse(vectorParameter.ParameterValue.Value.Hex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.NoBackground:
|
||||
DrawPreview(c);
|
||||
break;
|
||||
default:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
break;
|
||||
}
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseMtxOffer : UCreator
|
||||
{
|
||||
public BaseMtxOffer(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Background = new[] { SKColor.Parse("4F4F69"), SKColor.Parse("4F4F69") };
|
||||
Border = new[] { SKColor.Parse("9092AB") };
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FSoftObjectPath image, "SoftDetailsImage", "SoftTileImage"))
|
||||
{
|
||||
Preview = Utils.GetBitmap(image);
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback gradient, "Gradient") &&
|
||||
gradient.TryGetValue(out FLinearColor start, "Start") &&
|
||||
gradient.TryGetValue(out FLinearColor stop, "Stop"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(start.Hex), SKColor.Parse(stop.Hex) };
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FLinearColor background, "Background"))
|
||||
Border = new[] { SKColor.Parse(background.Hex) };
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText shortDescription, "ShortDescription"))
|
||||
Description = shortDescription.Text;
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback[] details, "DetailsAttributes"))
|
||||
{
|
||||
foreach (var detail in details)
|
||||
{
|
||||
if (detail.TryGetValue(out FText detailName, "Name"))
|
||||
{
|
||||
Description += $"\n- {detailName.Text.TrimEnd()}";
|
||||
}
|
||||
|
||||
if (detail.TryGetValue(out FText detailValue, "Value") && !string.IsNullOrEmpty(detailValue.Text))
|
||||
{
|
||||
Description += $" ({detailValue.Text})";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Description = Utils.RemoveHtmlTags(Description);
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.NoBackground:
|
||||
DrawPreview(c);
|
||||
break;
|
||||
case EIconStyle.NoText:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
break;
|
||||
default:
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
break;
|
||||
}
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseOfferDisplayData : UCreator
|
||||
{
|
||||
private readonly List<BaseMaterialInstance> _offerImages;
|
||||
|
||||
public BaseOfferDisplayData(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
_offerImages = new List<BaseMaterialInstance>();
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (!Object.TryGetValue(out FStructFallback[] contextualPresentations, "ContextualPresentations"))
|
||||
return;
|
||||
|
||||
for (var i = 0; i < contextualPresentations.Length; i++)
|
||||
{
|
||||
if (!contextualPresentations[i].TryGetValue(out FSoftObjectPath material, "Material") ||
|
||||
!material.TryLoad(out UMaterialInterface presentation)) continue;
|
||||
|
||||
var offerImage = new BaseMaterialInstance(presentation, Style);
|
||||
offerImage.ParseForInfo();
|
||||
_offerImages.Add(offerImage);
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap[_offerImages.Count];
|
||||
for (var i = 0; i < ret.Length; i++)
|
||||
{
|
||||
ret[i] = _offerImages[i]?.Draw()[0];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Services;
|
||||
using FModel.ViewModels;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BasePlaylist : UCreator
|
||||
{
|
||||
private ApiEndpointViewModel _apiEndpointView => ApplicationService.ApiEndpointView;
|
||||
private SKBitmap _missionIcon;
|
||||
|
||||
public BasePlaylist(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Margin = 0;
|
||||
Width = 1024;
|
||||
Height = 512;
|
||||
Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Tiles/T_Athena_Tile_Matchmaking_Default.T_Athena_Tile_Matchmaking_Default");
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FText displayName, "UIDisplayName", "DisplayName"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "UIDescription", "Description"))
|
||||
Description = description.Text;
|
||||
if (Object.TryGetValue(out UTexture2D missionIcon, "MissionIcon"))
|
||||
_missionIcon = Utils.GetBitmap(missionIcon).Resize(25);
|
||||
|
||||
if (!Object.TryGetValue(out FName playlistName, "PlaylistName") || string.IsNullOrWhiteSpace(playlistName.Text))
|
||||
return;
|
||||
|
||||
var playlist = _apiEndpointView.FortniteApi.GetPlaylist(playlistName.Text);
|
||||
if (!playlist.IsSuccess || playlist.Data.Images is not { HasShowcase: true } ||
|
||||
!_apiEndpointView.FortniteApi.TryGetBytes(playlist.Data.Images.Showcase, out var image))
|
||||
return;
|
||||
|
||||
Preview = Utils.GetBitmap(image).ResizeWithRatio(1024, 512);
|
||||
Width = Preview.Width;
|
||||
Height = Preview.Height;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.NoBackground:
|
||||
DrawPreview(c);
|
||||
break;
|
||||
case EIconStyle.NoText:
|
||||
DrawPreview(c);
|
||||
DrawMissionIcon(c);
|
||||
break;
|
||||
default:
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
DrawMissionIcon(c);
|
||||
break;
|
||||
}
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void DrawMissionIcon(SKCanvas c)
|
||||
{
|
||||
if (_missionIcon == null) return;
|
||||
c.DrawBitmap(_missionIcon, new SKPoint(5, 5), ImagePaint);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Extensions;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseQuest : BaseIcon
|
||||
{
|
||||
private int _count;
|
||||
private Reward _reward;
|
||||
private readonly bool _screenLayer;
|
||||
private readonly string[] _unauthorizedReward = { "Token", "ChallengeBundle", "GiftBox" };
|
||||
|
||||
public string NextQuestName { get; private set; }
|
||||
|
||||
public BaseQuest(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Margin = 0;
|
||||
Width = 1024;
|
||||
Height = 256;
|
||||
DefaultPreview = Utils.GetBitmap("FortniteGame/Content/Athena/HUD/Quests/Art/T_NPC_Default.T_NPC_Default");
|
||||
if (uObject != null)
|
||||
{
|
||||
_screenLayer = uObject.ExportType.Equals("FortFeatItemDefinition", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
private BaseQuest(int completionCount, EIconStyle style) : this(null, style) // completion
|
||||
{
|
||||
var description = completionCount < 0 ?
|
||||
Utils.GetLocalizedResource("AthenaChallengeDetailsEntry", "CompletionRewardFormat_All", "Complete <text color=\"FFF\" case=\"upper\" fontface=\"black\">all {0} challenges</> to earn the reward item") :
|
||||
Utils.GetLocalizedResource("AthenaChallengeDetailsEntry", "CompletionRewardFormat", "Complete <text color=\"FFF\" case=\"upper\" fontface=\"black\">any {0} challenges</> to earn the reward item");
|
||||
|
||||
DisplayName = ReformatString(description, completionCount.ToString(), completionCount < 0);
|
||||
}
|
||||
|
||||
public BaseQuest(int completionCount, FSoftObjectPath itemDefinition, EIconStyle style) : this(completionCount, style) // completion
|
||||
{
|
||||
_reward = Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject) ? new Reward(uObject) : new Reward();
|
||||
}
|
||||
|
||||
public BaseQuest(int completionCount, int quantity, string reward, EIconStyle style) : this(completionCount, style) // completion
|
||||
{
|
||||
_reward = new Reward(quantity, reward);
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
ParseForReward(false);
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback urgentQuestData, "UrgentQuestData"))
|
||||
{
|
||||
if (urgentQuestData.TryGetValue(out FText eventTitle, "EventTitle"))
|
||||
DisplayName = eventTitle.Text;
|
||||
if (urgentQuestData.TryGetValue(out FText eventDescription, "EventDescription"))
|
||||
Description = eventDescription.Text;
|
||||
if (urgentQuestData.TryGetValue(out FPackageIndex alertIcon, "AlertIcon", "BountyPriceImage"))
|
||||
Preview = Utils.GetBitmap(alertIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ShortDescription))
|
||||
Description = ShortDescription;
|
||||
if (string.IsNullOrEmpty(DisplayName) && !string.IsNullOrEmpty(Description))
|
||||
DisplayName = Description;
|
||||
if (DisplayName == Description)
|
||||
Description = string.Empty;
|
||||
|
||||
if ((Object.TryGetValue(out FSoftObjectPath icon, "QuestGiverWidgetIcon", "NotificationIconOverride") &&
|
||||
Utils.TryLoadObject(icon.AssetPathName.Text, out UObject iconObject)) ||
|
||||
(Object.TryGetValue(out FSoftObjectPath tandemCharacterData, "TandemCharacterData") &&
|
||||
Utils.TryLoadObject(tandemCharacterData.AssetPathName.Text, out UObject uObject) &&
|
||||
uObject.TryGetValue(out FSoftObjectPath tandemIcon, "EntryListIcon", "ToastIcon") &&
|
||||
Utils.TryLoadObject(tandemIcon.AssetPathName.Text, out iconObject)))
|
||||
{
|
||||
Preview = iconObject switch
|
||||
{
|
||||
UTexture2D text => Utils.GetBitmap(text),
|
||||
UMaterialInstanceConstant mat => Utils.GetBitmap(mat),
|
||||
_ => Preview
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out int objectiveCompletionCount, "ObjectiveCompletionCount"))
|
||||
_count = objectiveCompletionCount;
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback[] objectives, "Objectives") && objectives.Length > 0)
|
||||
{
|
||||
// actual description doesn't exist
|
||||
if (string.IsNullOrEmpty(Description) && objectives[0].TryGetValue(out FText description, "Description"))
|
||||
Description = description.Text;
|
||||
|
||||
// ObjectiveCompletionCount doesn't exist
|
||||
if (_count == 0)
|
||||
{
|
||||
if (objectives[0].TryGetValue(out int count, "Count") && count > 1)
|
||||
_count = count;
|
||||
else
|
||||
_count = objectives.Length;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback[] rewards, "Rewards"))
|
||||
{
|
||||
foreach (var reward in rewards)
|
||||
{
|
||||
if (!reward.TryGetValue(out FStructFallback itemPrimaryAssetId, "ItemPrimaryAssetId") ||
|
||||
!reward.TryGetValue(out int quantity, "Quantity")) continue;
|
||||
|
||||
if (!itemPrimaryAssetId.TryGetValue(out FStructFallback primaryAssetType, "PrimaryAssetType") ||
|
||||
!itemPrimaryAssetId.TryGetValue(out FName primaryAssetName, "PrimaryAssetName") ||
|
||||
!primaryAssetType.TryGetValue(out FName name, "Name")) continue;
|
||||
|
||||
if (name.Text.Equals("Quest", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
NextQuestName = primaryAssetName.Text;
|
||||
}
|
||||
else if (!_unauthorizedReward.Contains(name.Text))
|
||||
{
|
||||
_reward = new Reward(quantity, $"{name}:{primaryAssetName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_reward == null)
|
||||
{
|
||||
FName rowName = null;
|
||||
if (Object.TryGetValue(out UDataTable rewardsTable, "RewardsTable"))
|
||||
rowName = new FName("Default");
|
||||
else if (Object.TryGetValue(out FStructFallback[] rewardTableRows, "IndividualRewardTableRows") &&
|
||||
rewardTableRows.Length > 0 && rewardTableRows[0].TryGetValue(out rowName, "RowName") &&
|
||||
rewardTableRows[0].TryGetValue(out rewardsTable, "DataTable")) {}
|
||||
|
||||
if (rewardsTable != null && rowName != null && rewardsTable.TryGetDataTableRow(rowName.Text, StringComparison.InvariantCulture, out var row))
|
||||
{
|
||||
if (row.TryGetValue(out FName templateId, "TemplateId") &&
|
||||
row.TryGetValue(out int quantity, "Quantity"))
|
||||
{
|
||||
_reward = new Reward(quantity, templateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_reward == null && Object.TryGetValue(out FStructFallback[] hiddenRewards, "HiddenRewards"))
|
||||
{
|
||||
foreach (var hiddenReward in hiddenRewards)
|
||||
{
|
||||
if (!hiddenReward.TryGetValue(out FName templateId, "TemplateId") ||
|
||||
!hiddenReward.TryGetValue(out int quantity, "Quantity")) continue;
|
||||
|
||||
_reward = new Reward(quantity, templateId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_reward ??= new Reward();
|
||||
}
|
||||
|
||||
public void DrawQuest(SKCanvas c, int y)
|
||||
{
|
||||
DrawBackground(c, y);
|
||||
DrawPreview(c, y);
|
||||
DrawTexts(c, y);
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawQuest(c, 0);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private string ReformatString(string s, string completionCount, bool isAll)
|
||||
{
|
||||
s = s.Replace("({0})", "{0}").Replace("{QuestNumber}", "<text color=\"FFF\" case=\"upper\" fontface=\"black\">{0}</>");
|
||||
var index = s.IndexOf("{0}|plural(", StringComparison.OrdinalIgnoreCase);
|
||||
if (index > -1)
|
||||
{
|
||||
var p = s.Substring(index, s[index..].IndexOf(')') + 1);
|
||||
s = s.Replace(p, string.Empty);
|
||||
s = s.Insert(s.IndexOf("</>", StringComparison.OrdinalIgnoreCase), p.SubstringAfter("(").SubstringAfter("=").SubstringBefore(","));
|
||||
}
|
||||
|
||||
var upper = s.SubstringAfter(">").SubstringBefore("</>");
|
||||
return string.Format(Utils.RemoveHtmlTags(s.Replace(upper, upper.ToUpper())), isAll ? string.Empty : completionCount).Replace(" ", " ");
|
||||
}
|
||||
|
||||
private readonly SKPaint _informationPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Color = SKColor.Parse("#262630")
|
||||
};
|
||||
|
||||
private void DrawBackground(SKCanvas c, int y)
|
||||
{
|
||||
c.DrawRect(new SKRect(Margin, y, Width, y + Height), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, y + Height / 2), Width / 5 * 4,
|
||||
new[] { Background[0].WithAlpha(50), Background[1].WithAlpha(50) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(Height / 2, y, Width, y + Height), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, y + Height), new SKPoint(Width / 2, 75),
|
||||
new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(0, y + 75, Width, y + Height), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, y + Height / 2), Width / 5 * 4, Background, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(Margin, y, Height / 2, y + Height), _informationPaint);
|
||||
|
||||
_informationPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Height / 2, y + Height / 2), new SKPoint(Height / 2 + 100, y + Height / 2),
|
||||
new[] { SKColors.Black.WithAlpha(25), Background[1].WithAlpha(0) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(Height / 2, y, Height / 2 + 100, y + Height), _informationPaint);
|
||||
}
|
||||
|
||||
private void DrawPreview(SKCanvas c, int y)
|
||||
{
|
||||
ImagePaint.BlendMode = _screenLayer ? SKBlendMode.Screen : Preview == null ? SKBlendMode.ColorBurn : SKBlendMode.SrcOver;
|
||||
c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, y, Height - Margin, y + Height), ImagePaint);
|
||||
}
|
||||
|
||||
private void DrawTexts(SKCanvas c, int y)
|
||||
{
|
||||
_informationPaint.Shader = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(DisplayName))
|
||||
{
|
||||
_informationPaint.TextSize = 40;
|
||||
_informationPaint.Color = SKColors.White;
|
||||
_informationPaint.Typeface = Utils.Typefaces.Bundle;
|
||||
while (_informationPaint.MeasureText(DisplayName) > Width - Height - 10)
|
||||
{
|
||||
_informationPaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(_informationPaint.Typeface);
|
||||
c.DrawShapedText(shaper, DisplayName, Height, y + 50, _informationPaint);
|
||||
}
|
||||
|
||||
var outY = y + 75f;
|
||||
if (!string.IsNullOrWhiteSpace(Description))
|
||||
{
|
||||
_informationPaint.TextSize = 16;
|
||||
_informationPaint.Color = SKColors.White.WithAlpha(175);
|
||||
_informationPaint.Typeface = Utils.Typefaces.Description;
|
||||
Utils.DrawMultilineText(c, Description, Width - Height, y, SKTextAlign.Left,
|
||||
new SKRect(Height, outY, Width - 10, y + Height), _informationPaint, out outY);
|
||||
}
|
||||
|
||||
_informationPaint.Color = Border[0].WithAlpha(100);
|
||||
c.DrawRect(new SKRect(Height, outY, Width - 150, outY + 5), _informationPaint);
|
||||
|
||||
if (_count > 0)
|
||||
{
|
||||
_informationPaint.TextSize = 25;
|
||||
_informationPaint.Color = SKColors.White;
|
||||
_informationPaint.Typeface = Utils.Typefaces.BundleNumber;
|
||||
c.DrawText("0 / ", new SKPoint(Width - 130, outY + 10), _informationPaint);
|
||||
|
||||
_informationPaint.Color = Border[0];
|
||||
c.DrawText(_count.ToString(), new SKPoint(Width - 95, outY + 10), _informationPaint);
|
||||
}
|
||||
|
||||
_reward.DrawQuest(c, new SKRect(Height, outY + 25, Width - 20, y + Height - 25));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class Page
|
||||
{
|
||||
public int LevelsNeededForUnlock;
|
||||
public int RewardsNeededForUnlock;
|
||||
public Reward[] RewardEntryList;
|
||||
}
|
||||
|
||||
public class BaseSeason : UCreator
|
||||
{
|
||||
private Reward _firstWinReward;
|
||||
private Page[] _bookXpSchedule;
|
||||
private const int _headerHeight = 150;
|
||||
// keep the list because rewards are ordered by least to most important
|
||||
// we only care about the most but we also have filters so we can't just take the last reward
|
||||
|
||||
public BaseSeason(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Width = 1024;
|
||||
Height = _headerHeight + 50;
|
||||
Margin = 0;
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
_bookXpSchedule = Array.Empty<Page>();
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "ItemName"))
|
||||
DisplayName = displayName.Text.ToUpperInvariant();
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback seasonFirstWinRewards, "SeasonFirstWinRewards") &&
|
||||
seasonFirstWinRewards.TryGetValue(out FStructFallback[] rewards, "Rewards"))
|
||||
{
|
||||
foreach (var reward in rewards)
|
||||
{
|
||||
if (!reward.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
|
||||
!Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
|
||||
|
||||
_firstWinReward = new Reward(uObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FPackageIndex[] additionalSeasonData, "AdditionalSeasonData"))
|
||||
{
|
||||
foreach (var data in additionalSeasonData)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(data, out UObject packageIndex) ||
|
||||
!packageIndex.TryGetValue(out FStructFallback[] pageList, "PageList")) continue;
|
||||
|
||||
var i = 0;
|
||||
_bookXpSchedule = new Page[pageList.Length];
|
||||
foreach (var page in pageList)
|
||||
{
|
||||
if (!page.TryGetValue(out int levelsNeededForUnlock, "LevelsNeededForUnlock") ||
|
||||
!page.TryGetValue(out int rewardsNeededForUnlock, "RewardsNeededForUnlock") ||
|
||||
!page.TryGetValue(out FPackageIndex[] rewardEntryList, "RewardEntryList"))
|
||||
continue;
|
||||
|
||||
var p = new Page
|
||||
{
|
||||
LevelsNeededForUnlock = levelsNeededForUnlock,
|
||||
RewardsNeededForUnlock = rewardsNeededForUnlock,
|
||||
RewardEntryList = new Reward[rewardEntryList.Length]
|
||||
};
|
||||
|
||||
for (var j = 0; j < p.RewardEntryList.Length; j++)
|
||||
{
|
||||
if (!Utils.TryGetPackageIndexExport(rewardEntryList[j], out packageIndex) ||
|
||||
!packageIndex.TryGetValue(out FStructFallback battlePassOffer, "BattlePassOffer") ||
|
||||
!battlePassOffer.TryGetValue(out FStructFallback rewardItem, "RewardItem") ||
|
||||
!rewardItem.TryGetValue(out FSoftObjectPath itemDefinition, "ItemDefinition") ||
|
||||
!Utils.TryLoadObject(itemDefinition.AssetPathName.Text, out UObject uObject)) continue;
|
||||
|
||||
p.RewardEntryList[j] = new Reward(uObject);
|
||||
}
|
||||
|
||||
_bookXpSchedule[i++] = p;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Height += 100 * _bookXpSchedule.Sum(x => x.RewardEntryList.Length) / _bookXpSchedule.Length;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawHeader(c);
|
||||
_firstWinReward?.DrawSeasonWin(c, _headerHeight);
|
||||
DrawBookSchedule(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private const int _DEFAULT_AREA_SIZE = 80;
|
||||
private readonly SKPaint _headerPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.Bundle, TextSize = 50,
|
||||
TextAlign = SKTextAlign.Center, Color = SKColor.Parse("#262630")
|
||||
};
|
||||
|
||||
public void DrawHeader(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height), _headerPaint);
|
||||
|
||||
_headerPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, _headerHeight / 2), Width / 5 * 4,
|
||||
new[] { SKColors.SkyBlue.WithAlpha(50), SKColors.Blue.WithAlpha(50) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height), _headerPaint);
|
||||
|
||||
_headerPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, _headerHeight), new SKPoint(Width / 2, 75),
|
||||
new[] { SKColors.Black.WithAlpha(25), SKColors.Blue.WithAlpha(0) }, SKShaderTileMode.Clamp);
|
||||
c.DrawRect(new SKRect(0, 75, Width, _headerHeight), _headerPaint);
|
||||
|
||||
_headerPaint.Shader = null;
|
||||
_headerPaint.Color = SKColors.White;
|
||||
while (_headerPaint.MeasureText(DisplayName) > Width)
|
||||
{
|
||||
_headerPaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(_headerPaint.Typeface);
|
||||
c.DrawShapedText(shaper, DisplayName, Width / 2f, _headerHeight / 2f + _headerPaint.TextSize / 2 - 10, _headerPaint);
|
||||
}
|
||||
|
||||
private void DrawBookSchedule(SKCanvas c)
|
||||
{
|
||||
var x = 20;
|
||||
var y = _headerHeight + 50;
|
||||
foreach (var page in _bookXpSchedule)
|
||||
{
|
||||
foreach (var reward in page.RewardEntryList)
|
||||
{
|
||||
reward.DrawSeason(c, x, y, _DEFAULT_AREA_SIZE);
|
||||
x += _DEFAULT_AREA_SIZE + 20;
|
||||
}
|
||||
|
||||
y += _DEFAULT_AREA_SIZE + 20;
|
||||
x = 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseSeries : BaseIcon
|
||||
{
|
||||
public BaseSeries(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
GetSeries(Object);
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
|
||||
return new []{ret};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using FModel.Framework;
|
||||
using FModel.Settings;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseTandem : BaseIcon
|
||||
{
|
||||
private string _generalDescription, _additionalDescription;
|
||||
|
||||
public BaseTandem(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
DefaultPreview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/BattleRoyale/FeaturedItems/Outfit/T-AthenaSoldiers-CID-883-Athena-Commando-M-ChOneJonesy.T-AthenaSoldiers-CID-883-Athena-Commando-M-ChOneJonesy");
|
||||
Margin = 0;
|
||||
Width = 690;
|
||||
Height = 1080;
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
base.ParseForInfo();
|
||||
|
||||
string sidePanel = string.Empty, entryList = string.Empty;
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath sidePanelIcon, "SidePanelIcon"))
|
||||
sidePanel = sidePanelIcon.AssetPathName.Text;
|
||||
if (Object.TryGetValue(out FSoftObjectPath entryListIcon, "EntryListIcon"))
|
||||
entryList = entryListIcon.AssetPathName.Text;
|
||||
|
||||
// Overrides for generic "default" images Epic uses for Quest-only or unfinished NPCs
|
||||
if (sidePanel.Contains("Clown") && entryList.Contains("Clown"))
|
||||
Preview = null;
|
||||
else if (sidePanel.Contains("Bane") && !Object.Name.Contains("Sorana"))
|
||||
Preview = Utils.GetBitmap(entryList);
|
||||
else if (!string.IsNullOrWhiteSpace(sidePanel) && !sidePanel.Contains("Clown"))
|
||||
Preview = Utils.GetBitmap(sidePanel);
|
||||
else if ((string.IsNullOrWhiteSpace(sidePanel) || sidePanel.Contains("Clown")) && !string.IsNullOrWhiteSpace(entryList))
|
||||
Preview = Utils.GetBitmap(entryList);
|
||||
|
||||
if (Object.TryGetValue(out FText genDesc, "GeneralDescription"))
|
||||
_generalDescription = genDesc.Text;
|
||||
if (Object.TryGetValue(out FText addDesc, "AdditionalDescription"))
|
||||
_additionalDescription = addDesc.Text;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawHaze(c);
|
||||
|
||||
// Korean is slightly smaller than other languages, so the font size is increased slightly
|
||||
DrawName(c);
|
||||
DrawGeneralDescription(c);
|
||||
DrawAdditionalDescription(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private readonly SKPaint _panelPaint = new() { IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = SKColor.Parse("#0045C7") };
|
||||
|
||||
private new void DrawBackground(SKCanvas c)
|
||||
{
|
||||
c.DrawBitmap(SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/npcleftside.png"))?.Stream).Resize(Width, Height), 0, 0, new SKPaint { IsAntialias = false, FilterQuality = SKFilterQuality.None, ImageFilter = SKImageFilter.CreateBlur(0, 25) });
|
||||
|
||||
using var rect1 = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
_panelPaint.Color = SKColor.Parse("#002A8C");
|
||||
rect1.MoveTo(29, 0);
|
||||
rect1.LineTo(62, Height);
|
||||
rect1.LineTo(Width, Height);
|
||||
rect1.LineTo(Width, 0);
|
||||
rect1.LineTo(29, 0);
|
||||
rect1.Close();
|
||||
c.DrawPath(rect1, _panelPaint);
|
||||
|
||||
_panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(29, 0), new SKPoint(Width, Height),
|
||||
new[] { SKColor.Parse("#002A8C") }, SKShaderTileMode.Clamp);
|
||||
c.DrawPath(rect1, _panelPaint);
|
||||
|
||||
_panelPaint.Shader = SKShader.CreateRadialGradient(new SKPoint(348, 196), 300, new[] { SKColor.Parse("#0049CE"), SKColor.Parse("#002A8C") }, SKShaderTileMode.Clamp);
|
||||
c.DrawPath(rect1, _panelPaint);
|
||||
|
||||
using var rect2 = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
|
||||
rect2.MoveTo(10, 0);
|
||||
rect2.LineTo(30, 0);
|
||||
rect2.LineTo(63, Height);
|
||||
rect2.LineTo(56, Height);
|
||||
rect2.LineTo(10, 0);
|
||||
rect2.Close();
|
||||
c.DrawPath(rect2, _panelPaint);
|
||||
|
||||
_panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(10, 0), new SKPoint(62, Height),
|
||||
new[] { SKColor.Parse("#0045C7") }, SKShaderTileMode.Clamp);
|
||||
c.DrawPath(rect2, _panelPaint);
|
||||
}
|
||||
|
||||
private new void DrawPreview(SKCanvas c)
|
||||
{
|
||||
var previewToUse = Preview ?? DefaultPreview;
|
||||
|
||||
if (Preview == null)
|
||||
{
|
||||
previewToUse = DefaultPreview;
|
||||
ImagePaint.BlendMode = SKBlendMode.DstOut;
|
||||
ImagePaint.Color = SKColor.Parse("#00175F");
|
||||
}
|
||||
|
||||
var x = -125;
|
||||
|
||||
switch (previewToUse.Width)
|
||||
{
|
||||
case 512 when previewToUse.Height == 1024:
|
||||
previewToUse = previewToUse.ResizeWithRatio(500, 1000);
|
||||
x = 100;
|
||||
break;
|
||||
case 512 when previewToUse.Height == 512:
|
||||
case 128 when previewToUse.Height == 128:
|
||||
previewToUse = previewToUse.Resize(512);
|
||||
x = 125;
|
||||
break;
|
||||
default:
|
||||
previewToUse = previewToUse.Resize(1000, 1000);
|
||||
break;
|
||||
}
|
||||
|
||||
c.DrawBitmap(previewToUse, x, 30, ImagePaint);
|
||||
}
|
||||
|
||||
private void DrawHaze(SKCanvas c)
|
||||
{
|
||||
using var rect1 = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
rect1.MoveTo(29, 0);
|
||||
rect1.LineTo(62, Height);
|
||||
rect1.LineTo(Width, Height);
|
||||
rect1.LineTo(Width, 0);
|
||||
rect1.LineTo(29, 0);
|
||||
rect1.Close();
|
||||
|
||||
_panelPaint.Shader = SKShader.CreateLinearGradient(new SKPoint(343, 0), new SKPoint(343, Height),
|
||||
new[] { SKColors.Transparent, SKColor.Parse("#001E70FF"), SKColor.Parse("#001E70").WithAlpha(200), SKColor.Parse("#001E70").WithAlpha(245), SKColor.Parse("#001E70") }, new[] { 0, (float) .1, (float) .65, (float) .85, 1 }, SKShaderTileMode.Clamp);
|
||||
c.DrawPath(rect1, _panelPaint);
|
||||
}
|
||||
|
||||
private void DrawName(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(DisplayName)) return;
|
||||
|
||||
DisplayNamePaint.TextSize = UserSettings.Default.AssetLanguage switch
|
||||
{
|
||||
ELanguage.Korean => 56,
|
||||
_ => 42
|
||||
};
|
||||
|
||||
DisplayNamePaint.TextScaleX = (float) 1.1;
|
||||
DisplayNamePaint.Color = SKColors.White;
|
||||
DisplayNamePaint.TextSkewX = (float) -.25;
|
||||
DisplayNamePaint.TextAlign = SKTextAlign.Left;
|
||||
|
||||
var typeface = Utils.Typefaces.TandemDisplayName;
|
||||
if (typeface == Utils.Typefaces.Default)
|
||||
{
|
||||
DisplayNamePaint.TextSize = 30;
|
||||
}
|
||||
|
||||
DisplayNamePaint.Typeface = typeface;
|
||||
var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
|
||||
c.DrawShapedText(shaper, DisplayName.ToUpper(), 97, 900, DisplayNamePaint);
|
||||
}
|
||||
|
||||
private void DrawGeneralDescription(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_generalDescription)) return;
|
||||
|
||||
DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
|
||||
{
|
||||
ELanguage.Korean => 20,
|
||||
_ => 17
|
||||
};
|
||||
|
||||
DescriptionPaint.Color = SKColor.Parse("#00FFFB");
|
||||
DescriptionPaint.TextAlign = SKTextAlign.Left;
|
||||
|
||||
var typeface = Utils.Typefaces.TandemGenDescription;
|
||||
if (typeface == Utils.Typefaces.Default)
|
||||
{
|
||||
DescriptionPaint.TextSize = 21;
|
||||
}
|
||||
|
||||
DescriptionPaint.Typeface = typeface;
|
||||
var shaper = new CustomSKShaper(DescriptionPaint.Typeface);
|
||||
c.DrawShapedText(shaper, _generalDescription.ToUpper(), 97, 930, DescriptionPaint);
|
||||
}
|
||||
|
||||
private void DrawAdditionalDescription(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_additionalDescription)) return;
|
||||
|
||||
DescriptionPaint.TextSize = UserSettings.Default.AssetLanguage switch
|
||||
{
|
||||
ELanguage.Korean => 22,
|
||||
_ => 18
|
||||
};
|
||||
|
||||
DescriptionPaint.Color = SKColor.Parse("#89D8FF");
|
||||
DescriptionPaint.TextAlign = SKTextAlign.Left;
|
||||
|
||||
var typeface = Utils.Typefaces.TandemAddDescription;
|
||||
if (typeface == Utils.Typefaces.Default)
|
||||
{
|
||||
DescriptionPaint.TextSize = 20;
|
||||
}
|
||||
|
||||
DescriptionPaint.Typeface = typeface;
|
||||
Utils.DrawMultilineText(c, _additionalDescription, Width, 0, SKTextAlign.Left,
|
||||
new SKRect(97, 960, Width - 10, Height), DescriptionPaint, out _);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class BaseUserControl : UCreator
|
||||
{
|
||||
private List<Options> _optionValues = new();
|
||||
|
||||
private readonly SKPaint _displayNamePaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.DisplayName, TextSize = 45,
|
||||
Color = SKColors.White, TextAlign = SKTextAlign.Left
|
||||
};
|
||||
private readonly SKPaint _descriptionPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.DisplayName, TextSize = 25,
|
||||
Color = SKColor.Parse("88DBFF"), TextAlign = SKTextAlign.Left
|
||||
};
|
||||
|
||||
public BaseUserControl(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Width = 512;
|
||||
Height = 128;
|
||||
Margin = 32;
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FText optionDisplayName, "OptionDisplayName", "OptionText"))
|
||||
DisplayName = optionDisplayName.Text.ToUpperInvariant();
|
||||
if (Object.TryGetValue(out FText optionDescription, "OptionDescription", "OptionToolTip"))
|
||||
{
|
||||
Description = optionDescription.Text;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Description)) return;
|
||||
Height += (int) _descriptionPaint.TextSize * Utils.SplitLines(Description, _descriptionPaint, Width - Margin).Count;
|
||||
Height += (int) _descriptionPaint.TextSize;
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FStructFallback[] optionValues, "OptionValues", "Options"))
|
||||
{
|
||||
_optionValues = new List<Options>();
|
||||
foreach (var option in optionValues)
|
||||
{
|
||||
if (option.TryGetValue(out FText displayName, "DisplayName", "DisplayText"))
|
||||
{
|
||||
var opt = new Options { Option = displayName.Text.ToUpperInvariant() };
|
||||
if (option.TryGetValue(out FLinearColor color, "Value"))
|
||||
opt.Color = SKColor.Parse(color.Hex).WithAlpha(150);
|
||||
|
||||
_optionValues.Add(opt);
|
||||
}
|
||||
else if (option.TryGetValue(out FName primaryAssetName, "PrimaryAssetName"))
|
||||
{
|
||||
_optionValues.Add(new Options { Option = primaryAssetName.Text });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FText optionOnText, "OptionOnText"))
|
||||
_optionValues.Add(new Options { Option = optionOnText.Text });
|
||||
if (Object.TryGetValue(out FText optionOffText, "OptionOffText"))
|
||||
_optionValues.Add(new Options { Option = optionOffText.Text });
|
||||
|
||||
if (Object.TryGetValue(out int iMin, "Min", "DefaultValue") &&
|
||||
Object.TryGetValue(out int iMax, "Max"))
|
||||
{
|
||||
var increment = iMin;
|
||||
if (Object.TryGetValue(out int incrementValue, "IncrementValue"))
|
||||
increment = incrementValue;
|
||||
|
||||
var format = "{0}";
|
||||
if (Object.TryGetValue(out FText unitName, "UnitName"))
|
||||
format = unitName.Text;
|
||||
|
||||
for (var i = iMin; i <= iMax; i += increment)
|
||||
{
|
||||
_optionValues.Add(new Options { Option = string.Format(format, i) });
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out float fMin, "Min", "DefaultValue") &&
|
||||
Object.TryGetValue(out float fMax, "Max"))
|
||||
{
|
||||
var increment = fMin;
|
||||
if (Object.TryGetValue(out float incrementValue, "IncrementValue"))
|
||||
increment = incrementValue;
|
||||
|
||||
var format = "{0}";
|
||||
if (Object.TryGetValue(out FText unitName, "UnitName"))
|
||||
format = unitName.Text;
|
||||
|
||||
for (var i = fMin; i <= fMax; i += increment)
|
||||
{
|
||||
_optionValues.Add(new Options { Option = string.Format(format, i) });
|
||||
}
|
||||
}
|
||||
|
||||
Height += Margin;
|
||||
Height += 35 * _optionValues.Count;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Opaque);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawInformation(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private new void DrawBackground(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateLinearGradient(
|
||||
new SKPoint(Width / 2, Height),
|
||||
new SKPoint(Width, Height / 4),
|
||||
new[] { SKColor.Parse("01369C"), SKColor.Parse("1273C8") },
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
}
|
||||
|
||||
private void DrawInformation(SKCanvas c)
|
||||
{
|
||||
// display name
|
||||
while (_displayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
|
||||
{
|
||||
_displayNamePaint.TextSize -= 2;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(_displayNamePaint.Typeface);
|
||||
c.DrawShapedText(shaper, DisplayName, Margin, Margin + _displayNamePaint.TextSize, _displayNamePaint);
|
||||
#if DEBUG
|
||||
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Margin + _displayNamePaint.TextSize), new SKPaint { Color = SKColors.Blue, IsStroke = true });
|
||||
#endif
|
||||
|
||||
// description
|
||||
float y = Margin;
|
||||
if (!string.IsNullOrEmpty(DisplayName)) y += _displayNamePaint.TextSize;
|
||||
if (!string.IsNullOrEmpty(Description)) y += _descriptionPaint.TextSize + Margin / 2F;
|
||||
|
||||
Utils.DrawMultilineText(c, Description, Width, Margin, SKTextAlign.Left,
|
||||
new SKRect(Margin, y, Width - Margin, 256), _descriptionPaint, out var top);
|
||||
|
||||
// options
|
||||
foreach (var option in _optionValues)
|
||||
{
|
||||
option.Draw(c, Margin, Width, ref top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Options
|
||||
{
|
||||
private const int _SPACE = 5;
|
||||
private const int _HEIGHT = 30;
|
||||
|
||||
private readonly SKPaint _optionPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.DisplayName, TextSize = 20,
|
||||
Color = SKColor.Parse("EEFFFF"), TextAlign = SKTextAlign.Left
|
||||
};
|
||||
|
||||
public string Option;
|
||||
public SKColor Color = SKColor.Parse("55C5FC").WithAlpha(150);
|
||||
|
||||
public void Draw(SKCanvas c, int margin, int width, ref float top)
|
||||
{
|
||||
c.DrawRect(new SKRect(margin, top, width - margin, top + _HEIGHT), new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = Color });
|
||||
c.DrawText(Option, margin + _SPACE * 2, top + 20 * 1.1f, _optionPaint);
|
||||
top += _HEIGHT + _SPACE;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
using System;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases.FN;
|
||||
|
||||
public class Reward
|
||||
{
|
||||
private string _rewardQuantity;
|
||||
private BaseIcon _theReward;
|
||||
|
||||
public bool HasReward() => _theReward != null;
|
||||
|
||||
public Reward()
|
||||
{
|
||||
_rewardQuantity = "x0";
|
||||
}
|
||||
|
||||
public Reward(int quantity, FName primaryAssetName) : this(quantity, primaryAssetName.Text)
|
||||
{
|
||||
}
|
||||
|
||||
public Reward(int quantity, string assetName) : this()
|
||||
{
|
||||
_rewardQuantity = $"x{quantity:###,###,###}".Trim();
|
||||
|
||||
if (assetName.Contains(':'))
|
||||
{
|
||||
var parts = assetName.Split(':');
|
||||
|
||||
if (parts[0].Equals("HomebaseBannerIcon", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
if (!Utils.TryLoadObject($"FortniteGame/Content/Items/BannerIcons/{parts[1]}.{parts[1]}", out UObject p))
|
||||
return;
|
||||
|
||||
_theReward = new BaseIcon(p, EIconStyle.Default);
|
||||
_theReward.ParseForReward(false);
|
||||
_theReward.Border[0] = SKColors.White;
|
||||
_rewardQuantity = _theReward.DisplayName;
|
||||
}
|
||||
else GetReward(parts[1]);
|
||||
}
|
||||
else GetReward(assetName);
|
||||
}
|
||||
|
||||
public Reward(UObject uObject)
|
||||
{
|
||||
_theReward = new BaseIcon(uObject, EIconStyle.Default);
|
||||
_theReward.ParseForReward(false);
|
||||
_theReward.Border[0] = SKColors.White;
|
||||
_rewardQuantity = _theReward.DisplayName;
|
||||
}
|
||||
|
||||
private readonly SKPaint _rewardPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High
|
||||
};
|
||||
|
||||
public void DrawQuest(SKCanvas c, SKRect rect)
|
||||
{
|
||||
_rewardPaint.TextSize = 50;
|
||||
if (HasReward())
|
||||
{
|
||||
c.DrawBitmap((_theReward.Preview ?? _theReward.DefaultPreview).Resize((int) rect.Height), new SKPoint(rect.Left, rect.Top), _rewardPaint);
|
||||
|
||||
_rewardPaint.Color = _theReward.Border[0];
|
||||
_rewardPaint.Typeface = _rewardQuantity.StartsWith("x") ? Utils.Typefaces.BundleNumber : Utils.Typefaces.Bundle;
|
||||
_rewardPaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 5, 5, _theReward.Background[0].WithAlpha(150));
|
||||
while (_rewardPaint.MeasureText(_rewardQuantity) > rect.Width)
|
||||
{
|
||||
_rewardPaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(_rewardPaint.Typeface);
|
||||
c.DrawShapedText(shaper, _rewardQuantity, rect.Left + rect.Height + 25, rect.MidY + 20, _rewardPaint);
|
||||
}
|
||||
else
|
||||
{
|
||||
_rewardPaint.Color = SKColors.White;
|
||||
_rewardPaint.Typeface = Utils.Typefaces.BundleNumber;
|
||||
c.DrawText("No Reward", new SKPoint(rect.Left, rect.MidY + 20), _rewardPaint);
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawSeasonWin(SKCanvas c, int size)
|
||||
{
|
||||
if (!HasReward()) return;
|
||||
c.DrawBitmap((_theReward.Preview ?? _theReward.DefaultPreview).Resize(size), new SKPoint(0, 0), _rewardPaint);
|
||||
}
|
||||
|
||||
public void DrawSeason(SKCanvas c, int x, int y, int areaSize)
|
||||
{
|
||||
if (!HasReward()) return;
|
||||
|
||||
// area + icon
|
||||
_rewardPaint.Color = SKColor.Parse("#0F5CAF");
|
||||
c.DrawRect(new SKRect(x, y, x + areaSize, y + areaSize), _rewardPaint);
|
||||
c.DrawBitmap(_theReward.Preview.Resize(areaSize), new SKPoint(x, y), _rewardPaint);
|
||||
|
||||
// rarity color
|
||||
_rewardPaint.Color = _theReward.Background[0];
|
||||
var pathBottom = new SKPath {FillType = SKPathFillType.EvenOdd};
|
||||
pathBottom.MoveTo(x, y + areaSize);
|
||||
pathBottom.LineTo(x, y + areaSize - areaSize / 25 * 2.5f);
|
||||
pathBottom.LineTo(x + areaSize, y + areaSize - areaSize / 25 * 4.5f);
|
||||
pathBottom.LineTo(x + areaSize, y + areaSize);
|
||||
pathBottom.Close();
|
||||
c.DrawPath(pathBottom, _rewardPaint);
|
||||
}
|
||||
|
||||
private void GetReward(string trigger)
|
||||
{
|
||||
switch (trigger.ToLower())
|
||||
{
|
||||
// case "athenabattlestar":
|
||||
// _theReward = new BaseIcon(null, EIconStyle.Default);
|
||||
// _theReward.Border[0] = SKColor.Parse("FFDB67");
|
||||
// _theReward.Background[0] = SKColor.Parse("8F4A20");
|
||||
// _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/Athena/UI/Frontend/Art/T_UI_BP_BattleStar_L.T_UI_BP_BattleStar_L");
|
||||
// break;
|
||||
// case "athenaseasonalxp":
|
||||
// _theReward = new BaseIcon(null, EIconStyle.Default);
|
||||
// _theReward.Border[0] = SKColor.Parse("E6FDB1");
|
||||
// _theReward.Background[0] = SKColor.Parse("51830F");
|
||||
// _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-FNBR-XPUncommon-L.T-FNBR-XPUncommon-L");
|
||||
// break;
|
||||
// case "mtxgiveaway":
|
||||
// _theReward = new BaseIcon(null, EIconStyle.Default);
|
||||
// _theReward.Border[0] = SKColor.Parse("DCE6FF");
|
||||
// _theReward.Background[0] = SKColor.Parse("64A0AF");
|
||||
// _theReward.Preview = Utils.GetBitmap("FortniteGame/Content/UI/Foundation/Textures/Icons/Items/T-Items-MTX.T-Items-MTX");
|
||||
// break;
|
||||
default:
|
||||
{
|
||||
var path = Utils.GetFullPath($"FortniteGame/(?:Content/Athena|Content/Items|Plugins/GameFeatures)/.*?/{trigger}.uasset"); // path has no objectname and its needed so we push the trigger again as the objectname
|
||||
if (!string.IsNullOrWhiteSpace(path) && Utils.TryLoadObject(path.Replace("uasset", trigger), out UObject d))
|
||||
{
|
||||
_theReward = new BaseIcon(d, EIconStyle.Default);
|
||||
_theReward.ParseForReward(false);
|
||||
_theReward.Border[0] = SKColors.White;
|
||||
_rewardQuantity = $"{_theReward.DisplayName} ({_rewardQuantity})";
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,287 +0,0 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Extensions;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.MV;
|
||||
|
||||
public class BaseFighter : UCreator
|
||||
{
|
||||
private float _xOffset = 1f;
|
||||
private float _yOffset = 1f;
|
||||
private float _zoom = 1f;
|
||||
|
||||
private readonly SKBitmap _pattern;
|
||||
private readonly SKBitmap _perk;
|
||||
private readonly SKBitmap _emote;
|
||||
private readonly SKBitmap _skin;
|
||||
|
||||
private (SKBitmap, List<string>) _fighterType;
|
||||
private readonly List<SKBitmap> _recommendedPerks;
|
||||
private readonly List<SKBitmap> _availableTaunts;
|
||||
private readonly List<SKBitmap> _skins;
|
||||
|
||||
public BaseFighter(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Width = 1024;
|
||||
DisplayNamePaint.TextSize = 100;
|
||||
DisplayNamePaint.TextAlign = SKTextAlign.Left;
|
||||
DisplayNamePaint.Typeface = Utils.Typefaces.TandemDisplayName;
|
||||
DescriptionPaint.TextSize = 25;
|
||||
DescriptionPaint.Typeface = Utils.Typefaces.TandemGenDescription;
|
||||
DefaultPreview = Utils.GetBitmap("/Game/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random");
|
||||
|
||||
_pattern = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/UI_Textures/halftone_jagged.halftone_jagged");
|
||||
_perk = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_perks.ui_icons_perks");
|
||||
_emote = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_emote.ui_icons_emote");
|
||||
_skin = Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_skins.ui_icons_skins");
|
||||
_fighterType.Item2 = new List<string>();
|
||||
_recommendedPerks = new List<SKBitmap>();
|
||||
_availableTaunts = new List<SKBitmap>();
|
||||
_skins = new List<SKBitmap>();
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FLinearColor backgroundColor, "BackgroundColor"))
|
||||
Background = new[] { SKColor.Parse(backgroundColor.Hex) };
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath portraitMaterial, "CollectionsPortraitMaterial") &&
|
||||
portraitMaterial.TryLoad(out UMaterialInstanceConstant portrait))
|
||||
{
|
||||
_xOffset = Math.Abs(portrait.ScalarParameterValues.FirstOrDefault(x => x.ParameterInfo.Name.Text == "XOffset")?.ParameterValue ?? 1f);
|
||||
_yOffset = Math.Abs(portrait.ScalarParameterValues.FirstOrDefault(x => x.ParameterInfo.Name.Text == "YOffset")?.ParameterValue / 10 ?? 1f);
|
||||
_zoom = Math.Clamp(portrait.ScalarParameterValues.FirstOrDefault(x => x.ParameterInfo.Name.Text == "Zoom")?.ParameterValue ?? 1f, 0, 1);
|
||||
Preview = Utils.GetBitmap(portrait);
|
||||
}
|
||||
else if (Object.TryGetValue(out FSoftObjectPath portraitTexture, "NewCharacterSelectPortraitTexture", "HUDPortraitTexture"))
|
||||
Preview = Utils.GetBitmap(portraitTexture);
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName"))
|
||||
DisplayName = displayName.Text;
|
||||
|
||||
GetFighterClassInfo(Object.GetOrDefault("Class", EFighterClass.Support));
|
||||
_fighterType.Item2.Add(Utils.GetLocalizedResource(Object.GetOrDefault("Type", EFighterType.Horizontal)));
|
||||
if (Object.TryGetValue(out FText property, "Property"))
|
||||
_fighterType.Item2.Add(property.Text);
|
||||
|
||||
if (Object.TryGetValue(out UScriptSet recommendedPerks, "RecommendedPerkDatas")) // PORCO DIO WB USE ARRAYS!!!!!!
|
||||
{
|
||||
foreach (var recommendedPerk in recommendedPerks.Properties)
|
||||
{
|
||||
if (recommendedPerk.GenericValue is not FPackageIndex packageIndex ||
|
||||
!Utils.TryGetPackageIndexExport(packageIndex, out UObject export) ||
|
||||
!export.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail"))
|
||||
continue;
|
||||
|
||||
_recommendedPerks.Add(Utils.GetBitmap(rewardThumbnail));
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath[] availableTaunts, "AvailableTauntData"))
|
||||
{
|
||||
foreach (var taunt in availableTaunts)
|
||||
{
|
||||
if (!Utils.TryLoadObject(taunt.AssetPathName.Text, out UObject export) ||
|
||||
!export.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail"))
|
||||
continue;
|
||||
|
||||
_availableTaunts.Add(Utils.GetBitmap(rewardThumbnail));
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath[] skins, "Skins"))
|
||||
{
|
||||
foreach (var skin in skins)
|
||||
{
|
||||
if (!Utils.TryLoadObject(skin.AssetPathName.Text, out UObject export) ||
|
||||
!export.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail"))
|
||||
continue;
|
||||
|
||||
_skins.Add(Utils.GetBitmap(rewardThumbnail));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawDisplayName(c);
|
||||
DrawFighterInfo(c);
|
||||
DrawRecommendedPerks(c);
|
||||
DrawAvailableTaunts(c);
|
||||
DrawSkins(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void GetFighterClassInfo(EFighterClass clas)
|
||||
{
|
||||
if (!Utils.TryLoadObject("/Game/Panda_Main/UI/In-Game/Data/UICharacterClassInfo_Datatable.UICharacterClassInfo_Datatable", out UDataTable dataTable))
|
||||
return;
|
||||
|
||||
var row = dataTable.RowMap.ElementAt((int) clas).Value;
|
||||
if (!row.TryGetValue(out FText displayName, "DisplayName_5_9DB5DDFF490E1F4AD72329866F96B81D") ||
|
||||
!row.TryGetValue(out FPackageIndex icon, "Icon_8_711534AD4F240D4B001AA6A471EA1895"))
|
||||
return;
|
||||
|
||||
_fighterType.Item1 = Utils.GetBitmap(icon);
|
||||
_fighterType.Item2.Add(displayName.Text);
|
||||
}
|
||||
|
||||
private new void DrawBackground(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = Background[0]
|
||||
});
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(DisplayName))
|
||||
{
|
||||
c.DrawText(DisplayName, -50, 125, new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.DisplayName, TextSize = 200,
|
||||
TextScaleX = .95f, TextSkewX = -0.25f, Color = SKColors.Black.WithAlpha(25)
|
||||
});
|
||||
}
|
||||
|
||||
c.DrawBitmap(_pattern, new SKRect(0, Height / 2, Width, Height), new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High, BlendMode = SKBlendMode.SoftLight
|
||||
});
|
||||
|
||||
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
path.MoveTo(0, Height);
|
||||
path.LineTo(0, Height - 20);
|
||||
path.LineTo(Width, Height - 60);
|
||||
path.LineTo(Width, Height);
|
||||
path.Close();
|
||||
c.DrawPath(path, new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = SKColor.Parse("#141629")
|
||||
});
|
||||
}
|
||||
|
||||
private new void DrawPreview(SKCanvas c)
|
||||
{
|
||||
var img = (Preview ?? DefaultPreview).ResizeWithRatio(_zoom);
|
||||
var x_offset = img.Width * _xOffset;
|
||||
var y_offset = img.Height * -_yOffset;
|
||||
c.DrawBitmap(img, new SKRect(Width + x_offset - img.Width, y_offset, Width + x_offset, img.Height + y_offset), ImagePaint);
|
||||
}
|
||||
|
||||
private new void DrawDisplayName(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(DisplayName)) return;
|
||||
c.DrawText(DisplayName.ToUpper(), 50, 100, DisplayNamePaint);
|
||||
}
|
||||
|
||||
private void DrawFighterInfo(SKCanvas c)
|
||||
{
|
||||
if (_fighterType.Item1 != null)
|
||||
c.DrawBitmap(_fighterType.Item1, new SKRect(50, 112.5f, 98, 160.5f), ImagePaint);
|
||||
|
||||
c.DrawText(string.Join(" | ", _fighterType.Item2), 98, 145, DescriptionPaint);
|
||||
}
|
||||
|
||||
private void DrawRecommendedPerks(SKCanvas c)
|
||||
{
|
||||
const int x = 50;
|
||||
const int y = 200;
|
||||
const int size = 64;
|
||||
|
||||
ImagePaint.ImageFilter = null;
|
||||
ImagePaint.BlendMode = SKBlendMode.SoftLight;
|
||||
c.DrawBitmap(_perk, new SKRect(x, y, x + size / 2, y + size / 2), ImagePaint);
|
||||
if (_recommendedPerks.Count < 1) return;
|
||||
|
||||
ImagePaint.BlendMode = SKBlendMode.SrcOver;
|
||||
ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 2.5f, 2.5f, SKColors.Black);
|
||||
c.DrawBitmap(_recommendedPerks[1], new SKRect(161, y, 225, y + size), ImagePaint);
|
||||
c.DrawBitmap(_recommendedPerks[2], new SKRect(193, y + size / 2, 257, y + size * 1.5f), ImagePaint);
|
||||
c.DrawBitmap(_recommendedPerks[3], new SKRect(161, y + size, 225, y + size * 2), ImagePaint);
|
||||
|
||||
ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 5, 5, SKColors.Black.WithAlpha(150));
|
||||
c.DrawBitmap(_recommendedPerks[0], new SKRect(x, y, x + size * 2, y + size * 2), ImagePaint);
|
||||
}
|
||||
|
||||
private void DrawAvailableTaunts(SKCanvas c)
|
||||
{
|
||||
var x = 300;
|
||||
const int y = 232;
|
||||
const int size = 64;
|
||||
|
||||
ImagePaint.ImageFilter = null;
|
||||
ImagePaint.BlendMode = SKBlendMode.SoftLight;
|
||||
c.DrawBitmap(_emote, new SKRect(x, y - size / 2, x + size / 2, y), ImagePaint);
|
||||
if (_availableTaunts.Count < 1) return;
|
||||
|
||||
ImagePaint.BlendMode = SKBlendMode.SrcOver;
|
||||
ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 1.5f, 1.5f, SKColors.Black);
|
||||
|
||||
foreach (var taunt in _availableTaunts)
|
||||
{
|
||||
c.DrawBitmap(taunt, new SKRect(x, y, x + size, y + size), ImagePaint);
|
||||
x += size;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSkins(SKCanvas c)
|
||||
{
|
||||
var x = 50;
|
||||
const int y = 333;
|
||||
const int size = 128;
|
||||
|
||||
ImagePaint.ImageFilter = null;
|
||||
ImagePaint.BlendMode = SKBlendMode.SoftLight;
|
||||
c.DrawBitmap(_skin, new SKRect(x, y, x + size / 4, y + size / 4), ImagePaint);
|
||||
if (_skins.Count < 1) return;
|
||||
|
||||
ImagePaint.BlendMode = SKBlendMode.SrcOver;
|
||||
ImagePaint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 1.5f, 1.5f, SKColors.Black);
|
||||
foreach (var skin in _skins)
|
||||
{
|
||||
c.DrawBitmap(skin, new SKRect(x, y, x + size, y + size), ImagePaint);
|
||||
x += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum EFighterClass : byte
|
||||
{
|
||||
Mage = 4,
|
||||
Tank = 3,
|
||||
Fighter = 2,
|
||||
Bruiser = 2,
|
||||
Assassin = 1,
|
||||
Support = 0 // Default
|
||||
}
|
||||
|
||||
public enum EFighterType : byte
|
||||
{
|
||||
[Description("B980C82D40FF37FD359C74A339CE1B3A")]
|
||||
Hybrid = 2,
|
||||
|
||||
[Description("2C55443D47164019BE73A5ABDC670F36")]
|
||||
Vertical = 1,
|
||||
|
||||
[Description("97A60DD54AA23D4B93D5B891F729BF5C")]
|
||||
Horizontal = 0 // Default
|
||||
}
|
||||
|
|
@ -1,344 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.MV;
|
||||
|
||||
public class BasePandaIcon : UCreator
|
||||
{
|
||||
private float _y_offset;
|
||||
private ERewardRarity _rarity;
|
||||
private string _type;
|
||||
|
||||
protected readonly List<(SKBitmap, string)> Pictos;
|
||||
|
||||
public BasePandaIcon(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Width = 1024;
|
||||
Margin = 30;
|
||||
DisplayNamePaint.TextSize = 50;
|
||||
DisplayNamePaint.TextAlign = SKTextAlign.Left;
|
||||
DisplayNamePaint.Color = SKColor.Parse("#191C33");
|
||||
DescriptionPaint.TextSize = 25;
|
||||
DefaultPreview = Utils.GetBitmap("/Game/Panda_Main/UI/PreMatch/Images/DiamondPortraits/0010_Random.0010_Random");
|
||||
|
||||
_y_offset = Height / 2 + DescriptionPaint.TextSize;
|
||||
Pictos = new List<(SKBitmap, string)>();
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
var category = Object.GetOrDefault("Category", EPerkCategory.Offense);
|
||||
_rarity = Object.GetOrDefault("Rarity", ERewardRarity.None);
|
||||
|
||||
_type = Object.ExportType;
|
||||
var t = _type switch // ERewardType like
|
||||
{
|
||||
"StatTrackingBundleData" => EItemType.Badge,
|
||||
"AnnouncerPackData" => EItemType.Announcer,
|
||||
"CharacterGiftData" => EItemType.ExperiencePoints,
|
||||
"ProfileIconData" => EItemType.ProfileIcon,
|
||||
"RingOutVfxData" => EItemType.Ringout,
|
||||
"BannerData" => EItemType.Banner,
|
||||
"EmoteData" => EItemType.Sticker,
|
||||
"QuestData" => EItemType.Mission,
|
||||
"TauntData" => EItemType.Emote,
|
||||
"SkinData" => EItemType.Variant,
|
||||
"PerkData" when category == EPerkCategory.CharacterSpecific => EItemType.SignaturePerk,
|
||||
"PerkData" => EItemType.Perk,
|
||||
_ => EItemType.Unknown
|
||||
};
|
||||
|
||||
if (t == EItemType.SignaturePerk) _rarity = ERewardRarity.Legendary;
|
||||
Background = GetRarityBackground(_rarity);
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath rewardThumbnail, "RewardThumbnail", "DisplayTextureRef", "Texture"))
|
||||
Preview = Utils.GetBitmap(rewardThumbnail);
|
||||
else if (Object.TryGetValue(out FPackageIndex icon, "Icon"))
|
||||
Preview = Utils.GetBitmap(icon);
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "QuestName"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "Description", "QuestDescription"))
|
||||
Description = Utils.RemoveHtmlTags(description.Text);
|
||||
|
||||
var unlockLocation = Object.GetOrDefault("UnlockLocation", EUnlockLocation.None);
|
||||
if (t == EItemType.Unknown && unlockLocation == EUnlockLocation.CharacterMastery) t = EItemType.MasteryLevel;
|
||||
|
||||
Pictos.Add((Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_unlocked.ui_icons_unlocked"), Utils.GetLocalizedResource(unlockLocation)));
|
||||
if (Object.TryGetValue(out string slug, "Slug"))
|
||||
{
|
||||
t = _type switch
|
||||
{
|
||||
"HydraSyncedDataAsset" when slug == "gold" => EItemType.Gold,
|
||||
"HydraSyncedDataAsset" when slug == "gleamium" => EItemType.Gleamium,
|
||||
"HydraSyncedDataAsset" when slug == "match_toasts" => EItemType.Toast,
|
||||
_ => t
|
||||
};
|
||||
Pictos.Add((Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_link.ui_icons_link"), slug));
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out int xpValue, "XPValue"))
|
||||
DisplayName += $" (+{xpValue})";
|
||||
|
||||
if (Utils.TryLoadObject("/Game/Panda_Main/UI/Prototype/Foundation/Types/DT_EconomyGlossary.DT_EconomyGlossary", out UDataTable dataTable))
|
||||
{
|
||||
if (t != EItemType.Unknown &&
|
||||
dataTable.RowMap.ElementAt((int) t).Value.TryGetValue(out FText name, "Name_14_7F75AD6047CBDEA7B252B1BD76EF84B9"))
|
||||
_type = name.Text;
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
DrawPictos(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private SKColor[] GetRarityBackground(ERewardRarity rarity)
|
||||
{
|
||||
return rarity switch // the colors here are the base color and brighter color that the game uses for rarities from the "Rarity to Color" blueprint function
|
||||
{
|
||||
ERewardRarity.Common => new[]
|
||||
{
|
||||
SKColor.Parse(new FLinearColor(0.068478f, 0.651406f, 0.016807f, 1.000000f).Hex),
|
||||
SKColor.Parse(new FLinearColor(0.081422f, 1.000000f, 0.000000f, 1.000000f).Hex)
|
||||
},
|
||||
ERewardRarity.Rare => new[]
|
||||
{
|
||||
SKColor.Parse(new FLinearColor(0.035911f, 0.394246f, 0.900000f, 1.000000f).Hex),
|
||||
SKColor.Parse(new FLinearColor(0.033333f, 0.434207f, 1.000000f, 1.000000f).Hex)
|
||||
},
|
||||
ERewardRarity.Epic => new[]
|
||||
{
|
||||
SKColor.Parse(new FLinearColor(0.530391f, 0.060502f, 0.900000f, 1.000000f).Hex),
|
||||
SKColor.Parse(new FLinearColor(0.579907f, 0.045833f, 1.000000f, 1.000000f).Hex)
|
||||
},
|
||||
ERewardRarity.Legendary => new[]
|
||||
{
|
||||
SKColor.Parse(new FLinearColor(1.000000f, 0.223228f, 0.002428f, 1.000000f).Hex),
|
||||
SKColor.Parse(new FLinearColor(1.000000f, 0.479320f, 0.030713f, 1.000000f).Hex)
|
||||
},
|
||||
_ => new[]
|
||||
{
|
||||
SKColor.Parse(new FLinearColor(0.194618f, 0.651406f, 0.630757f, 1.000000f).Hex),
|
||||
SKColor.Parse(new FLinearColor(0.273627f, 0.955208f, 0.914839f, 1.000000f).Hex)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private new void DrawBackground(SKCanvas c)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = SKColor.Parse("#F3FCF0")
|
||||
});
|
||||
|
||||
var has_tr = _rarity != ERewardRarity.None;
|
||||
var tr = Utils.GetLocalizedResource(_rarity);
|
||||
var tr_paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
TextAlign = SKTextAlign.Right,
|
||||
TextSize = 35,
|
||||
Color = SKColors.White,
|
||||
Typeface = Utils.Typefaces.DisplayName
|
||||
};
|
||||
|
||||
var path = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
path.MoveTo(0, Height);
|
||||
path.LineTo(14, Height);
|
||||
path.LineTo(20, 20);
|
||||
if (has_tr)
|
||||
{
|
||||
const int margin = 15;
|
||||
var width = tr_paint.MeasureText(tr);
|
||||
path.LineTo(Width - width - margin * 2, 15);
|
||||
path.LineTo(Width - width - margin * 2.5f, 60);
|
||||
path.LineTo(Width, 55);
|
||||
}
|
||||
else
|
||||
{
|
||||
path.LineTo(Width, 14);
|
||||
}
|
||||
path.LineTo(Width, 0);
|
||||
path.LineTo(0, 0);
|
||||
path.LineTo(0, Height);
|
||||
path.Close();
|
||||
c.DrawPath(path, new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateLinearGradient(
|
||||
new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4), Background, SKShaderTileMode.Clamp)
|
||||
});
|
||||
|
||||
if (has_tr)
|
||||
{
|
||||
var x = Width - 20f;
|
||||
foreach (var a in tr.Select(character => character.ToString()).Reverse())
|
||||
{
|
||||
c.DrawText(a, x, 40, tr_paint);
|
||||
x -= tr_paint.MeasureText(a) - 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private new void DrawPreview(SKCanvas c)
|
||||
{
|
||||
const int size = 384;
|
||||
var y = Height - size - Margin * 2;
|
||||
c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, y, size + Margin, y + size), ImagePaint);
|
||||
}
|
||||
|
||||
private new void DrawDisplayName(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(DisplayName))
|
||||
return;
|
||||
|
||||
var x = 450f;
|
||||
while (DisplayNamePaint.MeasureText(DisplayName) > Width - x / 1.25)
|
||||
{
|
||||
DisplayNamePaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var y = Height / 2 - DisplayNamePaint.TextSize / 4;
|
||||
foreach (var a in DisplayName.Select(character => character.ToString()))
|
||||
{
|
||||
c.DrawText(a, x, y, DisplayNamePaint);
|
||||
x += DisplayNamePaint.MeasureText(a) - 4;
|
||||
}
|
||||
}
|
||||
|
||||
private new void DrawDescription(SKCanvas c)
|
||||
{
|
||||
const int x = 450;
|
||||
DescriptionPaint.Color = Background[0];
|
||||
c.DrawText(_type.ToUpper(), x, 170, DescriptionPaint);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Description)) return;
|
||||
|
||||
DescriptionPaint.Color = SKColor.Parse("#191C33");
|
||||
Utils.DrawMultilineText(c, Description, Width - x, Margin, SKTextAlign.Left,
|
||||
new SKRect(x, _y_offset, Width - Margin, Height - Margin), DescriptionPaint, out _y_offset);
|
||||
}
|
||||
|
||||
private void DrawPictos(SKCanvas c)
|
||||
{
|
||||
if (Pictos.Count < 1) return;
|
||||
|
||||
const float x = 450f;
|
||||
const int size = 24;
|
||||
var color = SKColor.Parse("#495B6E");
|
||||
var paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
TextSize = 27,
|
||||
Color = color,
|
||||
Typeface = Utils.Typefaces.Default
|
||||
};
|
||||
|
||||
ImagePaint.ColorFilter = SKColorFilter.CreateBlendMode(color, SKBlendMode.SrcIn);
|
||||
|
||||
foreach (var picto in Pictos)
|
||||
{
|
||||
c.DrawBitmap(picto.Item1, new SKRect(x, _y_offset + 10, x + size, _y_offset + 10 + size), ImagePaint);
|
||||
c.DrawText(picto.Item2, x + size + 10, _y_offset + size + 6, paint);
|
||||
_y_offset += size + 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum ERewardRarity : byte
|
||||
{
|
||||
[Description("0D4B15CE4FB6F2BC5E5F5FAA9E8B376C")]
|
||||
None = 0, // Default
|
||||
|
||||
[Description("0FCDEF47485E2C3D0D477988C481D8E3")]
|
||||
Common = 1,
|
||||
|
||||
[Description("18241CA7441AE16AAFB6EFAB499FF981")]
|
||||
Rare = 2,
|
||||
|
||||
[Description("D999D9CB4754D1078BF9A1B34A231005")]
|
||||
Epic = 3,
|
||||
|
||||
[Description("705AE967407D6EF8870E988A08C6900E")]
|
||||
Legendary = 4
|
||||
}
|
||||
|
||||
public enum EUnlockLocation : byte
|
||||
{
|
||||
[Description("0D4B15CE4FB6F2BC5E5F5FAA9E8B376C")]
|
||||
None = 0, // Default
|
||||
|
||||
[Description("0AFBCE5F41D930D6E9B5138C8EBCFE87")]
|
||||
Shop = 1,
|
||||
|
||||
[Description("062F178B4EE74502C9AD9D878F3D7CEA")]
|
||||
AccountLevel = 2,
|
||||
|
||||
[Description("1AE7A5DF477B2B5F4B3CCC8DCD732884")]
|
||||
CharacterMastery = 3,
|
||||
|
||||
[Description("0B37731C49DC9AE1EAC566950C1A329D")]
|
||||
Battlepass = 4,
|
||||
|
||||
[Description("16F160084187479E5D471786190AE5B7")]
|
||||
CharacterAffinity = 5,
|
||||
|
||||
[Description("E5C1E35C406C585E83B5D18A817FA0B4")]
|
||||
GuildBoss = 6,
|
||||
|
||||
[Description("4A89F5DD432113750EF52D8B58977DCE")]
|
||||
Tutorial = 7
|
||||
}
|
||||
|
||||
public enum EPerkCategory : byte
|
||||
{
|
||||
Offense = 0, // Default
|
||||
Defense = 1,
|
||||
Utility = 2,
|
||||
CharacterSpecific = 3
|
||||
}
|
||||
|
||||
public enum EItemType
|
||||
{
|
||||
Unknown = -1,
|
||||
Announcer,
|
||||
Badge,
|
||||
Banner,
|
||||
BattlePassPoints,
|
||||
Emote,
|
||||
ExperiencePoints,
|
||||
Gleamium,
|
||||
Gold,
|
||||
MasteryLevel,
|
||||
Mission,
|
||||
Perk,
|
||||
PlayerLevel,
|
||||
ProfileIcon,
|
||||
Rested,
|
||||
Ringout,
|
||||
SignaturePerk,
|
||||
Sticker,
|
||||
Toast,
|
||||
Variant
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.MV;
|
||||
|
||||
public class BasePerkGroup : UCreator
|
||||
{
|
||||
private readonly List<BasePandaIcon> _perks;
|
||||
|
||||
public BasePerkGroup(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
_perks = new List<BasePandaIcon>();
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out UScriptSet perks, "Perks")) // PORCO DIO WB USE ARRAYS!!!!!!
|
||||
{
|
||||
foreach (var perk in perks.Properties)
|
||||
{
|
||||
if (perk.GenericValue is not FPackageIndex packageIndex ||
|
||||
!Utils.TryGetPackageIndexExport(packageIndex, out UObject export))
|
||||
continue;
|
||||
|
||||
var icon = new BasePandaIcon(export, Style);
|
||||
icon.ParseForInfo();
|
||||
_perks.Add(icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap[_perks.Count];
|
||||
for (var i = 0; i < ret.Length; i++)
|
||||
{
|
||||
ret[i] = _perks[i].Draw()[0];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
using System.ComponentModel;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Extensions;
|
||||
|
||||
namespace FModel.Creator.Bases.MV;
|
||||
|
||||
public class BaseQuest : BasePandaIcon
|
||||
{
|
||||
public BaseQuest(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FStructFallback[] questCompletionRewards, "QuestCompletionRewards") &&
|
||||
questCompletionRewards.Length > 0 && questCompletionRewards[0] is { } actualReward)
|
||||
{
|
||||
var rewardType = actualReward.GetOrDefault("RewardType", EQuestRewardType.Inventory);
|
||||
var count = actualReward.GetOrDefault("Count", 0);
|
||||
Pictos.Add((Utils.GetBitmap("/Game/Panda_Main/UI/Assets/Icons/ui_icons_plus.ui_icons_plus"), count.ToString()));
|
||||
|
||||
base.ParseForInfo();
|
||||
|
||||
if (actualReward.TryGetValue(out FPackageIndex assetReward, "AssetReward") &&
|
||||
Utils.TryGetPackageIndexExport(assetReward, out UObject export))
|
||||
{
|
||||
var item = new BasePandaIcon(export, Style);
|
||||
item.ParseForInfo();
|
||||
Preview = item.Preview;
|
||||
}
|
||||
else if (rewardType != EQuestRewardType.Inventory)
|
||||
{
|
||||
Preview = Utils.GetBitmap(rewardType.GetDescription());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
base.ParseForInfo();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum EQuestRewardType : byte
|
||||
{
|
||||
Inventory = 0, // Default
|
||||
|
||||
[Description("/Game/Panda_Main/UI/Assets/Icons/UI_CharacterTicket.UI_CharacterTicket")]
|
||||
AccountXP = 1,
|
||||
|
||||
[Description("/Game/Panda_Main/UI/Assets/Icons/UI_BattlepassToken.UI_BattlepassToken")]
|
||||
BattlepassXP = 2
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.SB;
|
||||
|
||||
public class BaseDivision : UCreator
|
||||
{
|
||||
public BaseDivision(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FPackageIndex icon, "Icon", "IconNoTier"))
|
||||
{
|
||||
Preview = Utils.GetBitmap(icon);
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FLinearColor lightColor, "UILightColor") &&
|
||||
Object.TryGetValue(out FLinearColor mediumColor, "UIMediumColor") &&
|
||||
Object.TryGetValue(out FLinearColor darkColor, "UIDarkColor") &&
|
||||
Object.TryGetValue(out FLinearColor cardColor, "UICardColor"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(lightColor.Hex), SKColor.Parse(cardColor.Hex) };
|
||||
Border = new[] { SKColor.Parse(mediumColor.Hex), SKColor.Parse(darkColor.Hex) };
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName"))
|
||||
DisplayName = displayName.Text;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.SB;
|
||||
|
||||
public class BaseGameModeInfo : UCreator
|
||||
{
|
||||
private SKBitmap _icon;
|
||||
|
||||
public BaseGameModeInfo(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Width = 738;
|
||||
Height = 1024;
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "Description"))
|
||||
Description = description.Text;
|
||||
if (Object.TryGetValue(out UMaterialInstanceConstant portrait, "Portrait"))
|
||||
Preview = Utils.GetBitmap(portrait);
|
||||
if (Object.TryGetValue(out UTexture2D icon, "Icon"))
|
||||
_icon = Utils.GetBitmap(icon).Resize(25);
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawIcon(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void DrawIcon(SKCanvas c)
|
||||
{
|
||||
if (_icon == null) return;
|
||||
c.DrawBitmap(_icon, new SKPoint(5, 5), ImagePaint);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.SB;
|
||||
|
||||
public class BaseLeague : UCreator
|
||||
{
|
||||
private int _promotionXp, _xpLostPerMatch;
|
||||
|
||||
public BaseLeague(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
_promotionXp = 0;
|
||||
_xpLostPerMatch = 0;
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out int promotionXp, "PromotionXP"))
|
||||
_promotionXp = promotionXp;
|
||||
if (Object.TryGetValue(out int xpLostPerMatch, "XPLostPerMatch"))
|
||||
_xpLostPerMatch = xpLostPerMatch;
|
||||
|
||||
if (Object.TryGetValue(out FPackageIndex division, "Division") &&
|
||||
Utils.TryGetPackageIndexExport(division, out UObject div))
|
||||
{
|
||||
var d = new BaseDivision(div, Style);
|
||||
d.ParseForInfo();
|
||||
Preview = d.Preview;
|
||||
Background = d.Background;
|
||||
Border = d.Border;
|
||||
}
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName"))
|
||||
DisplayName = displayName.Text;
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
|
||||
DrawToBottom(c, SKTextAlign.Left, $"PromotionXP: {_promotionXp}");
|
||||
DrawToBottom(c, SKTextAlign.Right, $"XPLostPerMatch: {_xpLostPerMatch}");
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
using System;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Engine;
|
||||
using CUE4Parse.UE4.Objects.Core.i18N;
|
||||
using CUE4Parse.UE4.Objects.Core.Math;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bases.SB;
|
||||
|
||||
public class BaseSpellIcon : BaseIcon
|
||||
{
|
||||
private SKBitmap _seriesBackground2;
|
||||
|
||||
private readonly SKPaint _overlayPaint = new()
|
||||
{
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
IsAntialias = true,
|
||||
Color = SKColors.Transparent.WithAlpha(75)
|
||||
};
|
||||
|
||||
public BaseSpellIcon(UObject uObject, EIconStyle style) : base(uObject, style)
|
||||
{
|
||||
Background = new[] { SKColor.Parse("FFFFFF"), SKColor.Parse("636363") };
|
||||
Border = new[] { SKColor.Parse("D0D0D0"), SKColor.Parse("FFFFFF") };
|
||||
Width = Object.ExportType.StartsWith("GCosmeticCard") ? 1536 : 512;
|
||||
Height = Object.ExportType.StartsWith("GCosmeticCard") ? 450 : 512;
|
||||
}
|
||||
|
||||
public override void ParseForInfo()
|
||||
{
|
||||
if (Object.TryGetValue(out FName rarity, "Rarity"))
|
||||
GetRarity(rarity);
|
||||
|
||||
if (Object.TryGetValue(out FSoftObjectPath preview, "IconTexture", "OfferTexture", "PortraitTexture"))
|
||||
Preview = Utils.GetBitmap(preview);
|
||||
else if (Object.TryGetValue(out FPackageIndex icon, "IconTexture", "OfferTexture", "PortraitTexture"))
|
||||
Preview = Utils.GetBitmap(icon);
|
||||
|
||||
if (Object.TryGetValue(out FText displayName, "DisplayName", "Title", "Name"))
|
||||
DisplayName = displayName.Text;
|
||||
if (Object.TryGetValue(out FText description, "Description"))
|
||||
Description = description.Text;
|
||||
|
||||
SeriesBackground = Utils.GetBitmap("g3/Content/UI/Textures/assets/HUDAccentFillBox.HUDAccentFillBox");
|
||||
_seriesBackground2 = Utils.GetBitmap("g3/Content/UI/Textures/assets/store/ItemBGStatic_UIT.ItemBGStatic_UIT");
|
||||
}
|
||||
|
||||
public override SKBitmap[] Draw()
|
||||
{
|
||||
var ret = new SKBitmap(Width, Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
using var c = new SKCanvas(ret);
|
||||
|
||||
DrawBackgrounds(c);
|
||||
DrawBackground(c);
|
||||
DrawPreview(c);
|
||||
DrawTextBackground(c);
|
||||
DrawDisplayName(c);
|
||||
DrawDescription(c);
|
||||
|
||||
return new[] { ret };
|
||||
}
|
||||
|
||||
private void DrawBackgrounds(SKCanvas c)
|
||||
{
|
||||
if (SeriesBackground != null)
|
||||
c.DrawBitmap(SeriesBackground, new SKRect(0, 0, Width, Height), ImagePaint);
|
||||
if (_seriesBackground2 != null)
|
||||
c.DrawBitmap(_seriesBackground2, new SKRect(0, 0, Width, Height), _overlayPaint);
|
||||
|
||||
var x = Margin * (int) 2.5;
|
||||
const int radi = 15;
|
||||
c.DrawCircle(x + radi, x + radi, radi, new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateRadialGradient(
|
||||
new SKPoint(radi, radi), radi * 2 / 5 * 4,
|
||||
Background, SKShaderTileMode.Clamp)
|
||||
});
|
||||
}
|
||||
|
||||
private void GetRarity(FName n)
|
||||
{
|
||||
if (!Utils.TryLoadObject("g3/Content/UI/UIKit/DT_RarityColors.DT_RarityColors", out UDataTable rarity)) return;
|
||||
|
||||
if (rarity.TryGetDataTableRow(n.Text["EXRarity::".Length..], StringComparison.Ordinal, out var row))
|
||||
{
|
||||
if (row.TryGetValue(out FLinearColor[] colors, "Colors"))
|
||||
{
|
||||
Background = new[] { SKColor.Parse(colors[0].Hex), SKColor.Parse(colors[2].Hex) };
|
||||
Border = new[] { SKColor.Parse(colors[1].Hex), SKColor.Parse(colors[0].Hex) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
using System;
|
||||
using System.Windows;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using FModel.Framework;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
|
||||
namespace FModel.Creator.Bases;
|
||||
|
||||
public abstract class UCreator
|
||||
{
|
||||
protected UObject Object { get; }
|
||||
protected EIconStyle Style { get; }
|
||||
public SKBitmap DefaultPreview { get; set; }
|
||||
public SKBitmap Preview { get; set; }
|
||||
public SKColor[] Background { get; protected set; }
|
||||
public SKColor[] Border { get; protected set; }
|
||||
public string DisplayName { get; protected set; }
|
||||
public string Description { get; protected set; }
|
||||
public int Margin { get; protected set; }
|
||||
public int Width { get; protected set; }
|
||||
public int Height { get; protected set; }
|
||||
|
||||
public abstract void ParseForInfo();
|
||||
public abstract SKBitmap[] Draw();
|
||||
|
||||
protected UCreator(UObject uObject, EIconStyle style)
|
||||
{
|
||||
DefaultPreview = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_Placeholder_Item_Image.png"))?.Stream);
|
||||
Background = new[] { SKColor.Parse("5BFD00"), SKColor.Parse("003700") };
|
||||
Border = new[] { SKColor.Parse("1E8500"), SKColor.Parse("5BFD00") };
|
||||
DisplayName = string.Empty;
|
||||
Description = string.Empty;
|
||||
Width = 512;
|
||||
Height = 512;
|
||||
Margin = 2;
|
||||
Object = uObject;
|
||||
Style = style;
|
||||
}
|
||||
|
||||
private const int _STARTER_TEXT_POSITION = 380, _NAME_TEXT_SIZE = 45, _BOTTOM_TEXT_SIZE = 15;
|
||||
protected readonly SKPaint DisplayNamePaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.DisplayName, TextSize = _NAME_TEXT_SIZE,
|
||||
Color = SKColors.White, TextAlign = SKTextAlign.Center
|
||||
};
|
||||
protected readonly SKPaint DescriptionPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Utils.Typefaces.Description, TextSize = 13,
|
||||
Color = SKColors.White
|
||||
};
|
||||
protected readonly SKPaint ImagePaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High
|
||||
};
|
||||
private readonly SKPaint _textBackgroundPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = new SKColor(0, 0, 50, 75)
|
||||
};
|
||||
private readonly SKPaint _shortDescriptionPaint = new()
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Color = SKColors.White
|
||||
};
|
||||
|
||||
public void DrawBackground(SKCanvas c)
|
||||
{
|
||||
// reverse doesn't affect basic rarities
|
||||
if (Background[0] == Background[1]) Background[0] = Border[0];
|
||||
Background[0].ToHsl(out _, out _, out var l1);
|
||||
Background[1].ToHsl(out _, out _, out var l2);
|
||||
var reverse = l1 > l2;
|
||||
|
||||
// border
|
||||
c.DrawRect(new SKRect(0, 0, Width, Height),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateLinearGradient(
|
||||
new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4), Border, SKShaderTileMode.Clamp)
|
||||
});
|
||||
|
||||
if (this is BaseIcon { SeriesBackground: { } } baseIcon)
|
||||
c.DrawBitmap(baseIcon.SeriesBackground, new SKRect(baseIcon.Margin, baseIcon.Margin, baseIcon.Width - baseIcon.Margin,
|
||||
baseIcon.Height - baseIcon.Margin), ImagePaint);
|
||||
else
|
||||
{
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.Flat:
|
||||
{
|
||||
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Height - Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateLinearGradient(new SKPoint(Width / 2, Height), new SKPoint(Width, Height / 4),
|
||||
new[] { Background[reverse ? 0 : 1].WithAlpha(150), Border[0] }, SKShaderTileMode.Clamp)
|
||||
});
|
||||
if (string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description)) return;
|
||||
|
||||
var pathTop = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
pathTop.MoveTo(Margin, Margin);
|
||||
pathTop.LineTo(Margin + Width / 17 * 10, Margin);
|
||||
pathTop.LineTo(Margin, Margin + Height / 17);
|
||||
pathTop.Close();
|
||||
c.DrawPath(pathTop, new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = Background[1].WithAlpha(75)
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
c.DrawRect(new SKRect(Margin, Margin, Width - Margin, Height - Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateRadialGradient(new SKPoint(Width / 2, Height / 2), Width / 5 * 4,
|
||||
new[] { Background[reverse ? 0 : 1], Background[reverse ? 1 : 0] },
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawPreview(SKCanvas c)
|
||||
=> c.DrawBitmap(Preview ?? DefaultPreview, new SKRect(Margin, Margin, Width - Margin, Height - Margin), ImagePaint);
|
||||
|
||||
protected void DrawTextBackground(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrEmpty(DisplayName) && string.IsNullOrEmpty(Description)) return;
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.Flat:
|
||||
{
|
||||
var pathBottom = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
pathBottom.MoveTo(Margin, Height - Margin);
|
||||
pathBottom.LineTo(Margin, Height - Margin - Height / 17 * 2.5f);
|
||||
pathBottom.LineTo(Width - Margin, Height - Margin - Height / 17 * 4.5f);
|
||||
pathBottom.LineTo(Width - Margin, Height - Margin);
|
||||
pathBottom.Close();
|
||||
c.DrawPath(pathBottom, _textBackgroundPaint);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
c.DrawRect(new SKRect(Margin, _STARTER_TEXT_POSITION, Width - Margin, Height - Margin), _textBackgroundPaint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected void DrawDisplayName(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(DisplayName)) return;
|
||||
|
||||
while (DisplayNamePaint.MeasureText(DisplayName) > Width - Margin * 2)
|
||||
{
|
||||
DisplayNamePaint.TextSize -= 1;
|
||||
}
|
||||
|
||||
var shaper = new CustomSKShaper(DisplayNamePaint.Typeface);
|
||||
var shapedText = shaper.Shape(DisplayName, DisplayNamePaint);
|
||||
var x = Width / 2f;
|
||||
var y = _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE;
|
||||
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.Flat:
|
||||
{
|
||||
DisplayNamePaint.TextAlign = SKTextAlign.Right;
|
||||
x = Width - Margin * 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
var halfWidth = shapedText.Width / 2f;
|
||||
c.DrawLine(x - halfWidth, 0, x - halfWidth, Width, new SKPaint { Color = SKColors.Blue, IsStroke = true });
|
||||
c.DrawLine(x + halfWidth, 0, x + halfWidth, Width, new SKPaint { Color = SKColors.Blue, IsStroke = true });
|
||||
c.DrawRect(new SKRect(Margin, _STARTER_TEXT_POSITION, Width - Margin, y), new SKPaint { Color = SKColors.Blue, IsStroke = true });
|
||||
#endif
|
||||
|
||||
c.DrawShapedText(shaper, DisplayName, x, y, DisplayNamePaint);
|
||||
}
|
||||
|
||||
protected void DrawDescription(SKCanvas c)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Description)) return;
|
||||
|
||||
var maxLine = string.IsNullOrEmpty(DisplayName) ? 8 : 4;
|
||||
var side = SKTextAlign.Center;
|
||||
switch (Style)
|
||||
{
|
||||
case EIconStyle.Flat:
|
||||
side = SKTextAlign.Right;
|
||||
break;
|
||||
}
|
||||
|
||||
Utils.DrawCenteredMultilineText(c, Description, maxLine, Width, Margin, side,
|
||||
new SKRect(Margin, string.IsNullOrEmpty(DisplayName) ? _STARTER_TEXT_POSITION : _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE, Width - Margin, Height - _BOTTOM_TEXT_SIZE), DescriptionPaint);
|
||||
}
|
||||
|
||||
protected void DrawToBottom(SKCanvas c, SKTextAlign side, string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return;
|
||||
|
||||
_shortDescriptionPaint.TextAlign = side;
|
||||
_shortDescriptionPaint.TextSize = Utils.Typefaces.Bottom == null ? 15 : 13;
|
||||
switch (side)
|
||||
{
|
||||
case SKTextAlign.Left:
|
||||
_shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.DisplayName;
|
||||
var shaper = new CustomSKShaper(_shortDescriptionPaint.Typeface);
|
||||
c.DrawShapedText(shaper, text, Margin * 2.5f, Width - Margin * 2.5f, _shortDescriptionPaint);
|
||||
break;
|
||||
case SKTextAlign.Right:
|
||||
_shortDescriptionPaint.Typeface = Utils.Typefaces.Bottom ?? Utils.Typefaces.Default;
|
||||
c.DrawText(text, Width - Margin * 2.5f, Width - Margin * 2.5f, _shortDescriptionPaint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
FModel/Creator/Bundles/CompletionReward.cs
Normal file
60
FModel/Creator/Bundles/CompletionReward.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using FModel.Utils;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using System;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
public class CompletionReward
|
||||
{
|
||||
private const string _TRIGGER1 = "<text color=\"FFF\" case=\"upper\" fontface=\"black\">";
|
||||
private const string _TRIGGER2 = "</>";
|
||||
public string CompletionText;
|
||||
public Reward Reward;
|
||||
|
||||
public CompletionReward(IntProperty completionCount)
|
||||
{
|
||||
string all = Localizations.GetLocalization("AthenaChallengeDetailsEntry", "CompletionRewardFormat_All", "Complete <text color=\"FFF\" case=\"upper\" fontface=\"black\">all {0} challenges</> to earn the reward item");
|
||||
string allFormated = ReformatString(all, completionCount.Value.ToString(), true);
|
||||
string any = Localizations.GetLocalization("AthenaChallengeDetailsEntry", "CompletionRewardFormat", "Complete <text color=\"FFF\" case=\"upper\" fontface=\"black\">any {0} challenges</> to earn the reward item");
|
||||
string anyFormated = ReformatString(any, completionCount.Value.ToString(), false);
|
||||
CompletionText = completionCount.Value >= 0 ? anyFormated : allFormated;
|
||||
|
||||
Reward = null;
|
||||
}
|
||||
|
||||
public CompletionReward(IntProperty completionCount, IntProperty quantity, SoftObjectProperty itemDefinition) : this(completionCount)
|
||||
{
|
||||
Reward = new Reward(quantity, itemDefinition);
|
||||
}
|
||||
|
||||
public CompletionReward(IntProperty completionCount, IntProperty quantity, string reward) : this(completionCount)
|
||||
{
|
||||
Reward = new Reward(quantity, reward);
|
||||
}
|
||||
|
||||
private string ReformatString(string s, string completionCount, bool isAll)
|
||||
{
|
||||
s = s.Replace("({0})", "{0}").Replace("{QuestNumber}", "<text color=\"FFF\" case=\"upper\" fontface=\"black\">{0}</>");
|
||||
|
||||
int index = s.IndexOf("|plural(", StringComparison.CurrentCultureIgnoreCase);
|
||||
if (index > -1)
|
||||
{
|
||||
int i = s.Substring(index).IndexOf(')', StringComparison.CurrentCultureIgnoreCase);
|
||||
s = s.Replace(s.Substring(index, i + 1), string.Empty).Replace("{0} {0}", "{0}");
|
||||
}
|
||||
|
||||
int index1 = s.IndexOf(_TRIGGER1, StringComparison.CurrentCultureIgnoreCase);
|
||||
if (index1 < 0) index1 = 0;
|
||||
string partOne = s.Substring(0, index1);
|
||||
|
||||
string partTemp = s.Substring(index1 + _TRIGGER1.Length);
|
||||
int index2 = partTemp.IndexOf(_TRIGGER2, StringComparison.CurrentCultureIgnoreCase);
|
||||
if (index2 < 0) index2 = 0;
|
||||
string partUpper = partTemp.Substring(0, index2).ToUpper().Replace("{0}", isAll ? string.Empty : completionCount);
|
||||
|
||||
string partTwo = partTemp.Substring(index2 + _TRIGGER2.Length);
|
||||
|
||||
return string.Format("{0}{1}{2}", partOne, partUpper, partTwo).Replace(" ", " ").Replace(" ,", ",");
|
||||
}
|
||||
}
|
||||
}
|
||||
106
FModel/Creator/Bundles/Header.cs
Normal file
106
FModel/Creator/Bundles/Header.cs
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
public class Header
|
||||
{
|
||||
public SKColor PrimaryColor;
|
||||
public SKColor SecondaryColor;
|
||||
public SKColor AccentColor;
|
||||
public SKBitmap DisplayImage; // 256x256
|
||||
public SKBitmap CustomBackground; // 1024x256
|
||||
|
||||
private readonly Random _random = new Random(Environment.TickCount);
|
||||
private readonly string[] _randomColors = new string[255]
|
||||
{
|
||||
"F44336", "FFEBEE", "FFCDD2", "EF9A9A", "E57373", "EF5350", "E53935", "D32F2F", "C62828", "B71C1C",
|
||||
"FF8A80", "FF5252", "FF1744", "D50000", "FCE4EC", "F8BBD0", "F48FB1", "F06292", "EC407A", "E91E63",
|
||||
"D81B60", "C2185B", "AD1457", "880E4F", "FF80AB", "FF4081", "F50057", "C51162", "F3E5F5", "E1BEE7",
|
||||
"CE93D8", "BA68C8", "AB47BC", "9C27B0", "8E24AA", "7B1FA2", "6A1B9A", "4A148C", "EA80FC", "E040FB",
|
||||
"D500F9", "AA00FF", "EDE7F6", "D1C4E9", "B39DDB", "9575CD", "7E57C2", "673AB7", "5E35B1", "512DA8",
|
||||
"4527A0", "311B92", "B388FF", "7C4DFF", "651FFF", "6200EA", "E8EAF6", "C5CAE9", "9FA8DA", "7986CB",
|
||||
"5C6BC0", "3F51B5", "3949AB", "303F9F", "283593", "1A237E", "8C9EFF", "536DFE", "3D5AFE", "304FFE",
|
||||
"E3F2FD", "BBDEFB", "90CAF9", "64B5F6", "42A5F5", "2196F3", "1E88E5", "1976D2", "1565C0", "0D47A1",
|
||||
"82B1FF", "448AFF", "2979FF", "2962FF", "E1F5FE", "B3E5FC", "81D4FA", "4FC3F7", "29B6F6", "03A9F4",
|
||||
"039BE5", "0288D1", "0277BD", "01579B", "80D8FF", "40C4FF", "00B0FF", "0091EA", "E0F7FA", "B2EBF2",
|
||||
"80DEEA", "4DD0E1", "26C6DA", "00BCD4", "00ACC1", "0097A7", "00838F", "006064", "84FFFF", "18FFFF",
|
||||
"00E5FF", "00B8D4", "E0F2F1", "B2DFDB", "80CBC4", "4DB6AC", "26A69A", "009688", "00897B", "00796B",
|
||||
"00695C", "004D40", "A7FFEB", "64FFDA", "1DE9B6", "00BFA5", "E8F5E9", "C8E6C9", "A5D6A7", "81C784",
|
||||
"66BB6A", "4CAF50", "43A047", "388E3C", "2E7D32", "1B5E20", "B9F6CA", "69F0AE", "00E676", "00C853",
|
||||
"F1F8E9", "DCEDC8", "C5E1A5", "AED581", "9CCC65", "8BC34A", "7CB342", "689F38", "558B2F", "33691E",
|
||||
"CCFF90", "B2FF59", "76FF03", "64DD17", "F9FBE7", "F0F4C3", "E6EE9C", "DCE775", "D4E157", "CDDC39",
|
||||
"C0CA33", "AFB42B", "9E9D24", "827717", "F4FF81", "EEFF41", "C6FF00", "AEEA00", "FFFDE7", "FFF9C4",
|
||||
"FFF59D", "FFF176", "FFEE58", "FFEB3B", "FDD835", "FBC02D", "F9A825", "F57F17", "FFFF8D", "FFFF00",
|
||||
"FFEA00", "FFD600", "FFF8E1", "FFECB3", "FFE082", "FFD54F", "FFCA28", "FFC107", "FFB300", "FFA000",
|
||||
"FF8F00", "FF6F00", "FFE57F", "FFD740", "FFC400", "FFAB00", "FFF3E0", "FFE0B2", "FFCC80", "FFB74D",
|
||||
"FFA726", "FF9800", "FB8C00", "F57C00", "EF6C00", "E65100", "FFD180", "FFAB40", "FF9100", "FF6D00",
|
||||
"FBE9E7", "FFCCBC", "FFAB91", "FF8A65", "FF7043", "FF5722", "F4511E", "E64A19", "D84315", "BF360C",
|
||||
"FF9E80", "FF6E40", "FF3D00", "DD2C00", "EFEBE9", "D7CCC8", "BCAAA4", "A1887F", "8D6E63", "795548",
|
||||
"6D4C41", "5D4037", "4E342E", "3E2723", "FAFAFA", "F5F5F5", "EEEEEE", "E0E0E0", "BDBDBD", "9E9E9E",
|
||||
"757575", "616161", "424242", "212121", "ECEFF1", "CFD8DC", "B0BEC5", "90A4AE", "78909C", "607D8B",
|
||||
"546E7A", "455A64", "37474F", "263238", "000000",
|
||||
};
|
||||
|
||||
public Header()
|
||||
{
|
||||
if (Properties.Settings.Default.UseChallengeBanner)
|
||||
{
|
||||
SKColor mainColor = SKColor.Parse(Properties.Settings.Default.ChallengeBannerPrimaryColor);
|
||||
mainColor.ToHsl(out float h, out float s, out float l);
|
||||
float i = l + 20.0F > 100.0F ? 100.0F - l : 20.0F;
|
||||
|
||||
PrimaryColor = mainColor;
|
||||
SecondaryColor = SKColor.Parse(Properties.Settings.Default.ChallengeBannerSecondaryColor);
|
||||
AccentColor = SKColor.FromHsl(h += i, s, l);
|
||||
DisplayImage = null;
|
||||
if (!string.IsNullOrEmpty(Properties.Settings.Default.ChallengeBannerPath))
|
||||
CustomBackground = SKBitmap.Decode(new FileInfo(Properties.Settings.Default.ChallengeBannerPath).Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
|
||||
else CustomBackground = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SKColor mainColor = SKColor.Parse(_randomColors[_random.Next(0, 255)]);
|
||||
mainColor.ToHsl(out float h, out float s, out float l);
|
||||
while (l > 75 || l < 10)
|
||||
{
|
||||
mainColor = SKColor.Parse(_randomColors[_random.Next(0, 255)]);
|
||||
mainColor.ToHsl(out float _, out float _, out l);
|
||||
}
|
||||
float i = l + 20.0F > 100.0F ? 100.0F - l : 20.0F;
|
||||
|
||||
PrimaryColor = mainColor;
|
||||
SecondaryColor = SKColor.FromHsl(h, s, l += i);
|
||||
AccentColor = SKColor.FromHsl(h += i, s, l);
|
||||
DisplayImage = null;
|
||||
CustomBackground = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Header(StructProperty displayStyle, string assetFolder) : this()
|
||||
{
|
||||
if (displayStyle.Value is UObject o)
|
||||
{
|
||||
if (!Properties.Settings.Default.UseChallengeBanner && o.TryGetValue(out var c1, "PrimaryColor") && c1 is StructProperty s1 && s1.Value is FLinearColor primaryColor)
|
||||
PrimaryColor = SKColor.Parse(primaryColor.Hex);
|
||||
if (!Properties.Settings.Default.UseChallengeBanner && o.TryGetValue(out var c2, "SecondaryColor") && c2 is StructProperty s2 && s2.Value is FLinearColor secondaryColor)
|
||||
SecondaryColor = SKColor.Parse(secondaryColor.Hex);
|
||||
if (!Properties.Settings.Default.UseChallengeBanner && o.TryGetValue("AccentColor", out var c3) && c3 is StructProperty s3 && s3.Value is FLinearColor accentColor)
|
||||
{
|
||||
AccentColor = SKColor.Parse(accentColor.Hex);
|
||||
if (SecondaryColor.Red + SecondaryColor.Green + SecondaryColor.Blue <= 75 || assetFolder.Equals("LTM", StringComparison.CurrentCultureIgnoreCase)) // if secondary is too dark
|
||||
SecondaryColor = AccentColor; // use accent and pray for accent to be ligher
|
||||
}
|
||||
|
||||
if (o.TryGetValue("DisplayImage", out var i) && i is SoftObjectProperty displayImage)
|
||||
DisplayImage = Utils.GetSoftObjectTexture(displayImage);
|
||||
if (CustomBackground == null && o.TryGetValue("CustomBackground", out var b) && b is SoftObjectProperty customBackground)
|
||||
CustomBackground = Utils.GetSoftObjectTexture(customBackground);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
FModel/Creator/Bundles/HeaderStyle.cs
Normal file
99
FModel/Creator/Bundles/HeaderStyle.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Texts;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
static class HeaderStyle
|
||||
{
|
||||
public static void DrawHeaderPaint(SKCanvas c, BaseBundle icon)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, 0, icon.Width, icon.HeaderHeight), new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = icon.DisplayStyle.PrimaryColor
|
||||
});
|
||||
|
||||
if (icon.DisplayStyle.CustomBackground != null && icon.DisplayStyle.CustomBackground.Height != icon.DisplayStyle.CustomBackground.Width)
|
||||
{
|
||||
icon.IsDisplayNameShifted = false;
|
||||
var bgPaint = new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High, BlendMode = SKBlendMode.Screen };
|
||||
if (Properties.Settings.Default.UseChallengeBanner) bgPaint.Color = SKColors.Transparent.WithAlpha((byte)Properties.Settings.Default.ChallengeBannerOpacity);
|
||||
c.DrawBitmap(icon.DisplayStyle.CustomBackground, new SKRect(0, 0, 1024, 256), bgPaint);
|
||||
}
|
||||
else if (icon.DisplayStyle.DisplayImage != null)
|
||||
{
|
||||
icon.IsDisplayNameShifted = true;
|
||||
if (icon.DisplayStyle.CustomBackground != null && icon.DisplayStyle.CustomBackground.Height == icon.DisplayStyle.CustomBackground.Width)
|
||||
c.DrawBitmap(icon.DisplayStyle.CustomBackground, new SKRect(0, 0, icon.HeaderHeight, icon.HeaderHeight),
|
||||
new SKPaint {
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High, BlendMode = SKBlendMode.Screen,
|
||||
ImageFilter = SKImageFilter.CreateDropShadow(2.5F, 0, 20, 0, icon.DisplayStyle.SecondaryColor.WithAlpha(25))
|
||||
});
|
||||
|
||||
c.DrawBitmap(icon.DisplayStyle.DisplayImage, new SKRect(0, 0, icon.HeaderHeight, icon.HeaderHeight),
|
||||
new SKPaint {
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High,
|
||||
ImageFilter = SKImageFilter.CreateDropShadow(-2.5F, 0, 20, 0, icon.DisplayStyle.SecondaryColor.WithAlpha(50))
|
||||
});
|
||||
}
|
||||
|
||||
SKPath pathTop = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
pathTop.MoveTo(0, icon.HeaderHeight);
|
||||
pathTop.LineTo(icon.Width, icon.HeaderHeight);
|
||||
pathTop.LineTo(icon.Width, icon.HeaderHeight - 19);
|
||||
pathTop.LineTo(icon.Width / 2 + 7, icon.HeaderHeight - 23);
|
||||
pathTop.LineTo(icon.Width / 2 + 13, icon.HeaderHeight - 7);
|
||||
pathTop.LineTo(0, icon.HeaderHeight - 19);
|
||||
pathTop.Close();
|
||||
c.DrawPath(pathTop, new SKPaint {
|
||||
IsAntialias = true, FilterQuality = SKFilterQuality.High, Color = icon.DisplayStyle.SecondaryColor,
|
||||
ImageFilter = SKImageFilter.CreateDropShadow(-5, -5, 0, 0, icon.DisplayStyle.AccentColor.WithAlpha(75))
|
||||
});
|
||||
|
||||
c.DrawRect(new SKRect(0, icon.HeaderHeight, icon.Width, icon.HeaderHeight + icon.AdditionalSize), new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = icon.DisplayStyle.PrimaryColor.WithAlpha(200) // default background is black, so i'm kinda lowering the brightness here and that's what i want
|
||||
});
|
||||
}
|
||||
|
||||
public static void DrawHeaderText(SKCanvas c, BaseBundle icon)
|
||||
{
|
||||
using SKPaint paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.BundleDisplayNameTypeface,
|
||||
TextSize = 50,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Left,
|
||||
};
|
||||
|
||||
string text = icon.DisplayName.ToUpper();
|
||||
int x = icon.IsDisplayNameShifted ? 300 : 50;
|
||||
while (paint.MeasureText(text) > (icon.Width - x))
|
||||
{
|
||||
paint.TextSize -= 2;
|
||||
}
|
||||
c.DrawText(text, x, 155, paint);
|
||||
|
||||
paint.Color = SKColors.White.WithAlpha(150);
|
||||
paint.TextAlign = SKTextAlign.Right;
|
||||
paint.TextSize = 23;
|
||||
c.DrawText(icon.Watermark
|
||||
.Replace("{BundleName}", text)
|
||||
.Replace("{Date}", DateTime.Now.ToString("dd/MM/yyyy")),
|
||||
icon.Width - 25, icon.HeaderHeight - 40, paint);
|
||||
|
||||
paint.Typeface = Text.TypeFaces.BundleDefaultTypeface;
|
||||
paint.Color = icon.DisplayStyle.SecondaryColor;
|
||||
paint.TextAlign = SKTextAlign.Left;
|
||||
paint.TextSize = 30;
|
||||
c.DrawText(icon.FolderName.ToUpper(), x, 95, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
FModel/Creator/Bundles/Quest.cs
Normal file
94
FModel/Creator/Bundles/Quest.cs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
using FModel.Creator.Texts;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
public class Quest
|
||||
{
|
||||
public string Description;
|
||||
public int Count;
|
||||
public Reward Reward;
|
||||
|
||||
public Quest()
|
||||
{
|
||||
Description = "";
|
||||
Count = 0;
|
||||
Reward = null;
|
||||
}
|
||||
|
||||
public Quest(UObject obj) : this()
|
||||
{
|
||||
if (obj.TryGetValue("Description", out var d) && d is TextProperty description)
|
||||
Description = Text.GetTextPropertyBase(description);
|
||||
if (obj.TryGetValue("ObjectiveCompletionCount", out var o) && o is IntProperty objectiveCompletionCount)
|
||||
Count = objectiveCompletionCount.Value;
|
||||
|
||||
if (obj.TryGetValue("Objectives", out var v1) && v1 is ArrayProperty a1 &&
|
||||
a1.Value.Length > 0 && a1.Value[0] is StructProperty s && s.Value is UObject objectives)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Description) && objectives.TryGetValue("Description", out var od) && od is TextProperty objectivesDescription)
|
||||
Description = Text.GetTextPropertyBase(objectivesDescription);
|
||||
|
||||
if (Count == 0 && objectives.TryGetValue("Count", out var c) && c is IntProperty count)
|
||||
Count = count.Value;
|
||||
}
|
||||
|
||||
if (obj.TryGetValue("RewardsTable", out var v4) && v4 is ObjectProperty rewardsTable)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(rewardsTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var u = p.GetExport<UDataTable>();
|
||||
if (u != null && u.TryGetValue("Default", out var i) && i is UObject r &&
|
||||
r.TryGetValue("TemplateId", out var i1) && i1 is NameProperty templateId &&
|
||||
r.TryGetValue("Quantity", out var i2) && i2 is IntProperty quantity)
|
||||
{
|
||||
Reward = new Reward(quantity, templateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Reward == null && obj.TryGetValue("Rewards", out var v2) && v2 is ArrayProperty rewards)
|
||||
{
|
||||
foreach (StructProperty reward in rewards.Value)
|
||||
{
|
||||
if (reward.Value is UObject r1 &&
|
||||
r1.TryGetValue("ItemPrimaryAssetId", out var i1) && i1 is StructProperty itemPrimaryAssetId &&
|
||||
r1.TryGetValue("Quantity", out var i2) && i2 is IntProperty quantity)
|
||||
{
|
||||
if (itemPrimaryAssetId.Value is UObject r2 &&
|
||||
r2.TryGetValue("PrimaryAssetType", out var t1) && t1 is StructProperty primaryAssetType &&
|
||||
r2.TryGetValue("PrimaryAssetName", out var t2) && t2 is NameProperty primaryAssetName)
|
||||
{
|
||||
if (primaryAssetType.Value is UObject r3 && r3.TryGetValue("Name", out var k) && k is NameProperty name)
|
||||
{
|
||||
if (!name.Value.String.Equals("Quest") && !name.Value.String.Equals("Token") &&
|
||||
!name.Value.String.Equals("ChallengeBundle") && !name.Value.String.Equals("GiftBox"))
|
||||
{
|
||||
Reward = new Reward(quantity, primaryAssetName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Reward == null && obj.TryGetValue("HiddenRewards", out var v3) && v3 is ArrayProperty hiddenRewards)
|
||||
{
|
||||
foreach (StructProperty reward in hiddenRewards.Value)
|
||||
{
|
||||
if (reward.Value is UObject r1 &&
|
||||
r1.TryGetValue("TemplateId", out var i1) && i1 is NameProperty templateId &&
|
||||
r1.TryGetValue("Quantity", out var i2) && i2 is IntProperty quantity)
|
||||
{
|
||||
Reward = new Reward(quantity, templateId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
175
FModel/Creator/Bundles/QuestStyle.cs
Normal file
175
FModel/Creator/Bundles/QuestStyle.cs
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Texts;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
static class QuestStyle
|
||||
{
|
||||
public static void DrawQuests(SKCanvas c, BaseBundle icon)
|
||||
{
|
||||
using SKPaint paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
TextSize = 27,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Left,
|
||||
Typeface = Text.TypeFaces.BundleDisplayNameTypeface
|
||||
};
|
||||
|
||||
int y = icon.HeaderHeight + 50;
|
||||
foreach (Quest q in icon.Quests)
|
||||
{
|
||||
DrawQuestBackground(c, icon, y, true);
|
||||
|
||||
paint.TextSize = 27;
|
||||
paint.ImageFilter = null;
|
||||
paint.Color = SKColors.White;
|
||||
paint.TextAlign = SKTextAlign.Left;
|
||||
paint.Typeface = Text.TypeFaces.BundleDisplayNameTypeface;
|
||||
while (paint.MeasureText(q.Description) > icon.Width - 65 - 165)
|
||||
{
|
||||
paint.TextSize -= 1;
|
||||
}
|
||||
c.DrawText(q.Description, new SKPoint(65, y + paint.TextSize + 11), paint);
|
||||
|
||||
paint.TextSize = 16;
|
||||
paint.Color = SKColors.White.WithAlpha(200);
|
||||
paint.Typeface = Text.TypeFaces.BundleDefaultTypeface;
|
||||
c.DrawText(q.Count.ToString(), new SKPoint(93 + icon.Width - (175 * 3), y + 60), paint);
|
||||
|
||||
if (q.Reward?.RewardIcon != null)
|
||||
{
|
||||
if (q.Reward.IsCountShifted)
|
||||
{
|
||||
int l = q.Reward.RewardQuantity.ToString().Length;
|
||||
paint.TextSize = l >= 5 ? 30 : 35;
|
||||
paint.TextAlign = SKTextAlign.Right;
|
||||
paint.Color = SKColor.Parse(q.Reward.RewardFillColor);
|
||||
paint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 5, 5, SKColor.Parse(q.Reward.RewardBorderColor).WithAlpha(200));
|
||||
c.DrawText(q.Reward.RewardQuantity.ToString(), new SKPoint(icon.Width - 85, y + 47.5F), paint);
|
||||
c.DrawBitmap(q.Reward.RewardIcon, new SKPoint(icon.Width - 30 - q.Reward.RewardIcon.Width, y + 12.5F),
|
||||
new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High });
|
||||
}
|
||||
else
|
||||
c.DrawBitmap(q.Reward.RewardIcon, new SKPoint(icon.Width - 125, y + 5),
|
||||
new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High });
|
||||
}
|
||||
|
||||
y += 95;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawCompletionRewards(SKCanvas c, BaseBundle icon)
|
||||
{
|
||||
using SKPaint paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
TextSize = 35,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Left,
|
||||
Typeface = Text.TypeFaces.BundleDisplayNameTypeface
|
||||
};
|
||||
|
||||
int y = icon.HeaderHeight + (50 * 2) + (95 * icon.Quests.Count);
|
||||
foreach (CompletionReward r in icon.CompletionRewards)
|
||||
{
|
||||
DrawQuestBackground(c, icon, y, false);
|
||||
|
||||
paint.TextSize = 35;
|
||||
paint.ImageFilter = null;
|
||||
paint.Color = SKColors.White;
|
||||
paint.TextAlign = SKTextAlign.Left;
|
||||
paint.Typeface = Text.TypeFaces.BundleDisplayNameTypeface;
|
||||
while (paint.MeasureText(r.CompletionText) > icon.Width - 65 - 165)
|
||||
{
|
||||
paint.TextSize -= 1;
|
||||
}
|
||||
c.DrawText(r.CompletionText, new SKPoint(65, y + paint.TextSize + 15), paint);
|
||||
|
||||
if (r.Reward?.RewardIcon != null)
|
||||
{
|
||||
if (r.Reward.IsCountShifted)
|
||||
{
|
||||
int l = r.Reward.RewardQuantity.ToString().Length;
|
||||
paint.TextSize = l >= 5 ? 30 : 35;
|
||||
paint.TextAlign = SKTextAlign.Right;
|
||||
paint.Color = SKColor.Parse(r.Reward.RewardFillColor);
|
||||
paint.Typeface = Text.TypeFaces.BundleDefaultTypeface;
|
||||
paint.ImageFilter = SKImageFilter.CreateDropShadow(0, 0, 5, 5, SKColor.Parse(r.Reward.RewardBorderColor).WithAlpha(200));
|
||||
c.DrawText(r.Reward.RewardQuantity.ToString(), new SKPoint(icon.Width - 85, y + 47.5F), paint);
|
||||
c.DrawBitmap(r.Reward.RewardIcon, new SKPoint(icon.Width - 30 - r.Reward.RewardIcon.Width, y + 12.5F),
|
||||
new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High });
|
||||
}
|
||||
else
|
||||
c.DrawBitmap(r.Reward.RewardIcon, new SKPoint(icon.Width - 125, y + 5),
|
||||
new SKPaint { IsAntialias = true, FilterQuality = SKFilterQuality.High });
|
||||
}
|
||||
|
||||
y += 95;
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawQuestBackground(SKCanvas c, BaseBundle icon, int y, bool hasSlider)
|
||||
{
|
||||
SKColor baseColor = icon.DisplayStyle.PrimaryColor;
|
||||
using SKPaint paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = baseColor
|
||||
};
|
||||
using SKPath secondaryRect = new SKPath
|
||||
{
|
||||
FillType = SKPathFillType.EvenOdd
|
||||
};
|
||||
using SKPath selector = new SKPath
|
||||
{
|
||||
FillType = SKPathFillType.EvenOdd
|
||||
};
|
||||
using SKPath slider = new SKPath
|
||||
{
|
||||
FillType = SKPathFillType.EvenOdd
|
||||
};
|
||||
|
||||
c.DrawRect(new SKRect(25, y, icon.Width - 25, y + 75), paint);
|
||||
|
||||
baseColor.ToHsl(out float h, out float s, out float l);
|
||||
baseColor = SKColor.FromHsl(h, s, l + 5);
|
||||
paint.Color = baseColor;
|
||||
|
||||
secondaryRect.MoveTo(32, y + 5);
|
||||
secondaryRect.LineTo(icon.Width - 155, y + 4);
|
||||
secondaryRect.LineTo(icon.Width - 175, y + 68);
|
||||
secondaryRect.LineTo(29, y + 71);
|
||||
secondaryRect.Close();
|
||||
c.DrawPath(secondaryRect, paint);
|
||||
|
||||
paint.Color = icon.DisplayStyle.SecondaryColor;
|
||||
selector.MoveTo(41, y + 38);
|
||||
selector.LineTo(48, y + 34);
|
||||
selector.LineTo(52, y + 39);
|
||||
selector.LineTo(46, y + 44);
|
||||
selector.Close();
|
||||
c.DrawPath(selector, paint);
|
||||
|
||||
if (hasSlider)
|
||||
{
|
||||
slider.MoveTo(65, y + 53);
|
||||
slider.LineTo(65 + icon.Width - (175 * 3), y + 53);
|
||||
slider.LineTo(65 + icon.Width - (175 * 3), y + 58);
|
||||
slider.LineTo(65, y + 58);
|
||||
slider.Close();
|
||||
c.DrawPath(slider, paint);
|
||||
|
||||
paint.TextSize = 14;
|
||||
paint.Color = SKColors.White;
|
||||
paint.TextAlign = SKTextAlign.Left;
|
||||
paint.Typeface = Text.TypeFaces.BundleDefaultTypeface;
|
||||
c.DrawText("0 / ", new SKPoint(75 + icon.Width - (175 * 3), y + 59), paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
FModel/Creator/Bundles/Reward.cs
Normal file
135
FModel/Creator/Bundles/Reward.cs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
|
||||
namespace FModel.Creator.Bundles
|
||||
{
|
||||
public class Reward
|
||||
{
|
||||
public int RewardQuantity;
|
||||
public SKBitmap RewardIcon;
|
||||
public string RewardFillColor;
|
||||
public string RewardBorderColor;
|
||||
public bool IsCountShifted;
|
||||
|
||||
public Reward()
|
||||
{
|
||||
RewardQuantity = 0;
|
||||
RewardIcon = null;
|
||||
RewardFillColor = "";
|
||||
RewardBorderColor = "";
|
||||
IsCountShifted = false;
|
||||
}
|
||||
|
||||
public Reward(IntProperty quantity, NameProperty primaryAssetName) : this(quantity, primaryAssetName.Value.String) { }
|
||||
public Reward(IntProperty quantity, string assetName) : this()
|
||||
{
|
||||
RewardQuantity = quantity.Value;
|
||||
|
||||
if (assetName.Contains(':'))
|
||||
{
|
||||
string[] parts = assetName.Split(':');
|
||||
if (parts[0].Equals("HomebaseBannerIcon", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/Banners/BannerIcons");
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var c = p.GetExport<UDataTable>();
|
||||
if (c != null && c.TryGetCaseInsensitiveValue(parts[1], out var s) && s is UObject banner)
|
||||
{
|
||||
RewardIcon = new BaseIcon(banner, "BannerIcons.uasset", false).IconImage.Resize(64, 64);
|
||||
}
|
||||
}
|
||||
}
|
||||
else GetReward(parts[1]);
|
||||
}
|
||||
else GetReward(assetName);
|
||||
}
|
||||
|
||||
public Reward(IntProperty quantity, SoftObjectProperty itemDefinition) : this()
|
||||
{
|
||||
RewardQuantity = quantity.Value;
|
||||
|
||||
PakPackage p = Utils.GetPropertyPakPackage(itemDefinition.Value.AssetPathName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
if (d != null)
|
||||
{
|
||||
int s1 = itemDefinition.Value.AssetPathName.String.LastIndexOf('/');
|
||||
if (s1 < 0) s1 = 0;
|
||||
int s2 = itemDefinition.Value.AssetPathName.String.LastIndexOf('.') - s1;
|
||||
switch (itemDefinition.Value.AssetPathName.String)
|
||||
{
|
||||
case "/Game/Items/PersistentResources/AthenaBattleStar.AthenaBattleStar":
|
||||
IsCountShifted = true;
|
||||
RewardFillColor = "FFDB67";
|
||||
RewardBorderColor = "8F4A20";
|
||||
RewardIcon = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Items/T-FNBR-BattlePoints").Resize(48, 48);
|
||||
break;
|
||||
case "/Game/Items/PersistentResources/AthenaSeasonalXP.AthenaSeasonalXP":
|
||||
IsCountShifted = true;
|
||||
RewardFillColor = "E6FDB1";
|
||||
RewardBorderColor = "51830F";
|
||||
RewardIcon = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Items/T-FNBR-XPMedium").Resize(48, 48);
|
||||
break;
|
||||
case "/Game/Items/Currency/MtxGiveaway.MtxGiveaway":
|
||||
IsCountShifted = true;
|
||||
RewardFillColor = "DCE6FF";
|
||||
RewardBorderColor = "64A0AF";
|
||||
RewardIcon = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Items/T-Items-MTX").Resize(48, 48);
|
||||
break;
|
||||
default:
|
||||
IsCountShifted = false;
|
||||
RewardIcon = new BaseIcon(d, itemDefinition.Value.AssetPathName.String.Substring(s1, s2) + ".uasset", false).IconImage.Resize(64, 64);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetReward(string trigger)
|
||||
{
|
||||
switch (trigger.ToLower())
|
||||
{
|
||||
case "athenabattlestar":
|
||||
IsCountShifted = true;
|
||||
RewardFillColor = "FFDB67";
|
||||
RewardBorderColor = "8F4A20";
|
||||
RewardIcon = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Items/T-FNBR-BattlePoints").Resize(48, 48);
|
||||
break;
|
||||
case "athenaseasonalxp":
|
||||
IsCountShifted = true;
|
||||
RewardFillColor = "E6FDB1";
|
||||
RewardBorderColor = "51830F";
|
||||
RewardIcon = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Items/T-FNBR-XPMedium").Resize(48, 48);
|
||||
break;
|
||||
case "mtxgiveaway":
|
||||
IsCountShifted = true;
|
||||
RewardFillColor = "DCE6FF";
|
||||
RewardBorderColor = "64A0AF";
|
||||
RewardIcon = Utils.GetTexture("/Game/UI/Foundation/Textures/Icons/Items/T-Items-MTX").Resize(48, 48);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
string path = Utils.GetFullPath($"/FortniteGame/Content/Athena/.*?/{trigger}.*").Replace("FortniteGame/Content", "Game");
|
||||
PakPackage p = Utils.GetPropertyPakPackage(path);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
if (d != null)
|
||||
{
|
||||
int i = path.LastIndexOf('/');
|
||||
IsCountShifted = false;
|
||||
RewardIcon = new BaseIcon(d, path.Substring(i > 0 ? i : 0) + ".uasset", false).IconImage.Resize(64, 64);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
276
FModel/Creator/Creator.cs
Normal file
276
FModel/Creator/Creator.cs
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Bundles;
|
||||
using FModel.Creator.Icons;
|
||||
using FModel.Creator.Rarities;
|
||||
using FModel.Creator.Stats;
|
||||
using FModel.Creator.Texts;
|
||||
using FModel.ViewModels.ImageBox;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using SkiaSharp;
|
||||
using System.IO;
|
||||
|
||||
namespace FModel.Creator
|
||||
{
|
||||
static class Creator
|
||||
{
|
||||
/// <summary>
|
||||
/// i don't cache images because i don't wanna store a lot of SKCanvas in the memory
|
||||
/// </summary>
|
||||
/// <returns>true if an icon has been drawn</returns>
|
||||
public static bool TryDrawIcon(string assetPath, FName[] exportTypes, IUExport[] exports)
|
||||
{
|
||||
var d = new DirectoryInfo(assetPath);
|
||||
string assetName = d.Name;
|
||||
string assetFolder = d.Parent.Name;
|
||||
if (Text.TypeFaces.NeedReload(false)) Text.TypeFaces = new Typefaces(); // when opening bundle creator settings without loading paks first
|
||||
|
||||
int index = Globals.Game.ActualGame == EGame.Valorant ? 1 : 0;
|
||||
string exportType = exportTypes.Length > index ? exportTypes[index].String : string.Empty;
|
||||
switch (exportType)
|
||||
{
|
||||
case "AthenaConsumableEmoteItemDefinition":
|
||||
case "AthenaSkyDiveContrailItemDefinition":
|
||||
case "AthenaLoadingScreenItemDefinition":
|
||||
case "AthenaVictoryPoseItemDefinition":
|
||||
case "AthenaPetCarrierItemDefinition":
|
||||
case "AthenaMusicPackItemDefinition":
|
||||
case "AthenaBattleBusItemDefinition":
|
||||
case "AthenaCharacterItemDefinition":
|
||||
case "AthenaBackpackItemDefinition":
|
||||
case "AthenaPickaxeItemDefinition":
|
||||
case "AthenaGadgetItemDefinition":
|
||||
case "AthenaGliderItemDefinition":
|
||||
case "AthenaDailyQuestDefinition":
|
||||
case "AthenaSprayItemDefinition":
|
||||
case "AthenaDanceItemDefinition":
|
||||
case "AthenaEmojiItemDefinition":
|
||||
case "AthenaItemWrapDefinition":
|
||||
case "AthenaToyItemDefinition":
|
||||
case "FortHeroType":
|
||||
case "FortTokenType":
|
||||
case "FortAbilityKit":
|
||||
case "FortWorkerType":
|
||||
case "RewardGraphToken":
|
||||
case "FortBannerTokenType":
|
||||
case "FortVariantTokenType":
|
||||
case "FortFeatItemDefinition":
|
||||
case "FortStatItemDefinition":
|
||||
case "FortTrapItemDefinition":
|
||||
case "FortAmmoItemDefinition":
|
||||
case "FortQuestItemDefinition":
|
||||
case "FortBadgeItemDefinition":
|
||||
case "FortAwardItemDefinition":
|
||||
case "FortGadgetItemDefinition":
|
||||
case "FortPlaysetItemDefinition":
|
||||
case "FortGiftBoxItemDefinition":
|
||||
case "FortSpyTechItemDefinition":
|
||||
case "FortAccoladeItemDefinition":
|
||||
case "FortCardPackItemDefinition":
|
||||
case "FortDefenderItemDefinition":
|
||||
case "FortCurrencyItemDefinition":
|
||||
case "FortResourceItemDefinition":
|
||||
case "FortSchematicItemDefinition":
|
||||
case "FortIngredientItemDefinition":
|
||||
case "FortAccountBuffItemDefinition":
|
||||
case "FortWeaponMeleeItemDefinition":
|
||||
case "FortContextTrapItemDefinition":
|
||||
case "FortPlayerPerksItemDefinition":
|
||||
case "FortPlaysetPropItemDefinition":
|
||||
case "FortHomebaseNodeItemDefinition":
|
||||
case "FortWeaponRangedItemDefinition":
|
||||
case "FortNeverPersistItemDefinition":
|
||||
case "RadioContentSourceItemDefinition":
|
||||
case "FortPlaysetGrenadeItemDefinition":
|
||||
case "FortPersonalVehicleItemDefinition":
|
||||
case "FortHardcoreModifierItemDefinition":
|
||||
case "FortConsumableAccountItemDefinition":
|
||||
case "FortConversionControlItemDefinition":
|
||||
case "FortAccountBuffCreditItemDefinition":
|
||||
case "FortPersistentResourceItemDefinition":
|
||||
case "FortCampaignHeroLoadoutItemDefinition":
|
||||
case "FortConditionalResourceItemDefinition":
|
||||
case "FortChallengeBundleScheduleDefinition":
|
||||
case "FortWeaponMeleeDualWieldItemDefinition":
|
||||
case "FortDailyRewardScheduleTokenDefinition":
|
||||
{
|
||||
BaseIcon icon = new BaseIcon(exports[index], exportType, ref assetName);
|
||||
int height = icon.Size + icon.AdditionalSize;
|
||||
using (var ret = new SKBitmap(icon.Size, height, SKColorType.Rgba8888, SKAlphaType.Premul))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign != EIconDesign.NoBackground)
|
||||
{
|
||||
Rarity.DrawRarity(c, icon);
|
||||
}
|
||||
|
||||
LargeSmallImage.DrawPreviewImage(c, icon);
|
||||
|
||||
if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign != EIconDesign.NoBackground)
|
||||
{
|
||||
if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign != EIconDesign.NoText)
|
||||
{
|
||||
Text.DrawBackground(c, icon);
|
||||
Text.DrawDisplayName(c, icon);
|
||||
Text.DrawDescription(c, icon);
|
||||
if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign != EIconDesign.Mini)
|
||||
{
|
||||
if (!icon.ShortDescription.Equals(icon.DisplayName) && !icon.ShortDescription.Equals(icon.Description))
|
||||
Text.DrawToBottom(c, icon, ETextSide.Left, icon.ShortDescription);
|
||||
Text.DrawToBottom(c, icon, ETextSide.Right, icon.CosmeticSource);
|
||||
}
|
||||
}
|
||||
UserFacingFlag.DrawUserFacingFlags(c, icon);
|
||||
|
||||
// has more things to show
|
||||
if (height > icon.Size)
|
||||
{
|
||||
Statistics.DrawStats(c, icon);
|
||||
}
|
||||
}
|
||||
|
||||
Watermark.DrawWatermark(c); // watermark should only be applied on icons with width = 512
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "FortMtxOfferData":
|
||||
{
|
||||
BaseOffer icon = new BaseOffer(exports[index]);
|
||||
using (var ret = new SKBitmap(icon.Size, icon.Size, SKColorType.Rgba8888, SKAlphaType.Premul))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign != EIconDesign.NoBackground)
|
||||
{
|
||||
icon.DrawBackground(c);
|
||||
}
|
||||
icon.DrawImage(c);
|
||||
|
||||
Watermark.DrawWatermark(c); // watermark should only be applied on icons with width = 512
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "FortItemSeriesDefinition":
|
||||
{
|
||||
BaseIcon icon = new BaseIcon();
|
||||
using (var ret = new SKBitmap(icon.Size, icon.Size, SKColorType.Rgba8888, SKAlphaType.Opaque))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
Serie.GetRarity(icon, exports[index]);
|
||||
Rarity.DrawRarity(c, icon);
|
||||
|
||||
Watermark.DrawWatermark(c); // watermark should only be applied on icons with width = 512
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "PlaylistUserOptionEnum":
|
||||
case "PlaylistUserOptionBool":
|
||||
case "PlaylistUserOptionString":
|
||||
case "PlaylistUserOptionIntEnum":
|
||||
case "PlaylistUserOptionIntRange":
|
||||
case "PlaylistUserOptionColorEnum":
|
||||
case "PlaylistUserOptionFloatEnum":
|
||||
case "PlaylistUserOptionFloatRange":
|
||||
case "PlaylistUserOptionPrimaryAsset":
|
||||
case "PlaylistUserOptionCollisionProfileEnum":
|
||||
{
|
||||
BaseUserOption icon = new BaseUserOption(exports[index]);
|
||||
using (var ret = new SKBitmap(icon.Width, icon.Height, SKColorType.Rgba8888, SKAlphaType.Opaque))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
icon.Draw(c);
|
||||
|
||||
Watermark.DrawWatermark(c); // watermark should only be applied on icons with width = 512
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "FortChallengeBundleItemDefinition":
|
||||
{
|
||||
BaseBundle icon = new BaseBundle(exports[index], assetFolder);
|
||||
using (var ret = new SKBitmap(icon.Width, icon.HeaderHeight + icon.AdditionalSize, SKColorType.Rgba8888, SKAlphaType.Opaque))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
HeaderStyle.DrawHeaderPaint(c, icon);
|
||||
HeaderStyle.DrawHeaderText(c, icon);
|
||||
QuestStyle.DrawQuests(c, icon);
|
||||
QuestStyle.DrawCompletionRewards(c, icon);
|
||||
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "FortItemAccessTokenType":
|
||||
{
|
||||
BaseItemAccess icon = new BaseItemAccess(exports[index]);
|
||||
using (var ret = new SKBitmap(icon.Size, icon.Size, SKColorType.Rgba8888, SKAlphaType.Opaque))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
icon.Draw(c);
|
||||
|
||||
Watermark.DrawWatermark(c); // watermark should only be applied on icons with width = 512
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "MapUIData":
|
||||
{
|
||||
BaseMapUIData icon = new BaseMapUIData(exports[index]);
|
||||
using (var ret = new SKBitmap(icon.Width, icon.Height, SKColorType.Rgba8888, SKAlphaType.Premul))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
icon.Draw(c);
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case "ArmorUIData":
|
||||
case "SprayUIData":
|
||||
case "ThemeUIData":
|
||||
case "ContractUIData":
|
||||
case "CurrencyUIData":
|
||||
case "GameModeUIData":
|
||||
case "CharacterUIData":
|
||||
case "SprayLevelUIData":
|
||||
case "EquippableUIData":
|
||||
case "PlayerCardUIData":
|
||||
case "Gun_UIData_Base_C":
|
||||
case "CharacterRoleUIData":
|
||||
case "EquippableSkinUIData":
|
||||
case "EquippableCharmUIData":
|
||||
case "EquippableSkinLevelUIData":
|
||||
case "EquippableSkinChromaUIData":
|
||||
case "EquippableCharmLevelUIData":
|
||||
{
|
||||
BaseUIData icon = new BaseUIData(exports, index);
|
||||
using (var ret = new SKBitmap(icon.Width + icon.AdditionalWidth, icon.Height, SKColorType.Rgba8888, SKAlphaType.Premul))
|
||||
using (var c = new SKCanvas(ret))
|
||||
{
|
||||
icon.Draw(c);
|
||||
|
||||
Watermark.DrawWatermark(c); // watermark should only be applied on icons with width = 512
|
||||
ImageBoxVm.imageBoxViewModel.Set(ret, assetName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//case "StreamedVideoDataAsset": // must find a way to automatically gets the right version in the url
|
||||
// {
|
||||
// if (Globals.Game.ActualGame == EGame.Valorant && exports[index].GetExport<StructProperty>("Uuid") is StructProperty s && s.Value is FGuid uuid)
|
||||
// {
|
||||
// Process.Start(new ProcessStartInfo
|
||||
// {
|
||||
// FileName = string.Format(
|
||||
// "http://valorant.dyn.riotcdn.net/x/videos/release-01.05/{0}_default_universal.mp4",
|
||||
// $"{uuid.A:x8}-{uuid.B >> 16:x4}-{uuid.B & 0xFFFF:x4}-{uuid.C >> 16:x4}-{uuid.C & 0xFFFF:x4}{uuid.D:x8}"),
|
||||
// UseShellExecute = true
|
||||
// });
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Bases.BB;
|
||||
using FModel.Creator.Bases.FN;
|
||||
using FModel.Creator.Bases.MV;
|
||||
using FModel.Creator.Bases.SB;
|
||||
|
||||
namespace FModel.Creator;
|
||||
|
||||
public class CreatorPackage : IDisposable
|
||||
{
|
||||
private UObject _object;
|
||||
private EIconStyle _style;
|
||||
|
||||
public CreatorPackage(UObject uObject, EIconStyle style)
|
||||
{
|
||||
_object = uObject;
|
||||
_style = style;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UCreator ConstructCreator()
|
||||
{
|
||||
TryConstructCreator(out var creator);
|
||||
return creator;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryConstructCreator(out UCreator creator)
|
||||
{
|
||||
switch (_object.ExportType)
|
||||
{
|
||||
// Fortnite
|
||||
case "FortCreativeWeaponMeleeItemDefinition":
|
||||
case "AthenaConsumableEmoteItemDefinition":
|
||||
case "AthenaSkyDiveContrailItemDefinition":
|
||||
case "AthenaLoadingScreenItemDefinition":
|
||||
case "AthenaVictoryPoseItemDefinition":
|
||||
case "AthenaPetCarrierItemDefinition":
|
||||
case "AthenaMusicPackItemDefinition":
|
||||
case "AthenaBattleBusItemDefinition":
|
||||
case "AthenaCharacterItemDefinition":
|
||||
case "AthenaMapMarkerItemDefinition":
|
||||
case "AthenaBackpackItemDefinition":
|
||||
case "AthenaPickaxeItemDefinition":
|
||||
case "AthenaGadgetItemDefinition":
|
||||
case "AthenaGliderItemDefinition":
|
||||
case "AthenaSprayItemDefinition":
|
||||
case "AthenaDanceItemDefinition":
|
||||
case "AthenaEmojiItemDefinition":
|
||||
case "AthenaItemWrapDefinition":
|
||||
case "AthenaToyItemDefinition":
|
||||
case "FortHeroType":
|
||||
case "FortTokenType":
|
||||
case "FortAbilityKit":
|
||||
case "FortWorkerType":
|
||||
case "RewardGraphToken":
|
||||
case "JunoKnowledgeBundle":
|
||||
case "FortBannerTokenType":
|
||||
case "FortVariantTokenType":
|
||||
case "FortDecoItemDefinition":
|
||||
case "FortStatItemDefinition":
|
||||
case "FortAmmoItemDefinition":
|
||||
case "FortEmoteItemDefinition":
|
||||
case "FortBadgeItemDefinition":
|
||||
case "SparksMicItemDefinition":
|
||||
case "FortAwardItemDefinition":
|
||||
case "FortStackItemDefinition":
|
||||
case "FortWorldItemDefinition":
|
||||
case "SparksAuraItemDefinition":
|
||||
case "SparksDrumItemDefinition":
|
||||
case "SparksBassItemDefinition":
|
||||
case "FortGadgetItemDefinition":
|
||||
case "AthenaCharmItemDefinition":
|
||||
case "FortPlaysetItemDefinition":
|
||||
case "FortGiftBoxItemDefinition":
|
||||
case "FortOutpostItemDefinition":
|
||||
case "FortVehicleItemDefinition":
|
||||
case "FortMissionItemDefinition":
|
||||
case "FortAccountItemDefinition":
|
||||
case "SparksGuitarItemDefinition":
|
||||
case "FortCardPackItemDefinition":
|
||||
case "FortDefenderItemDefinition":
|
||||
case "FortCurrencyItemDefinition":
|
||||
case "FortResourceItemDefinition":
|
||||
case "FortBackpackItemDefinition":
|
||||
case "FortEventQuestMapDataAsset":
|
||||
case "FortBuildingItemDefinition":
|
||||
case "FortWeaponModItemDefinition":
|
||||
case "FortCodeTokenItemDefinition":
|
||||
case "FortSchematicItemDefinition":
|
||||
case "FortAlterableItemDefinition":
|
||||
case "SparksKeyboardItemDefinition":
|
||||
case "FortWorldMultiItemDefinition":
|
||||
case "FortAlterationItemDefinition":
|
||||
case "FortExpeditionItemDefinition":
|
||||
case "FortIngredientItemDefinition":
|
||||
case "FortConsumableItemDefinition":
|
||||
case "StWFortAccoladeItemDefinition":
|
||||
case "FortAccountBuffItemDefinition":
|
||||
case "FortWeaponMeleeItemDefinition":
|
||||
case "FortPlayerPerksItemDefinition":
|
||||
case "FortPlaysetPropItemDefinition":
|
||||
case "FortPrerollDataItemDefinition":
|
||||
case "JunoRecipeBundleItemDefinition":
|
||||
case "FortHomebaseNodeItemDefinition":
|
||||
case "FortNeverPersistItemDefinition":
|
||||
case "FortPlayerAugmentItemDefinition":
|
||||
case "FortSmartBuildingItemDefinition":
|
||||
case "FortGiftBoxUnlockItemDefinition":
|
||||
case "FortWeaponModItemDefinitionOptic":
|
||||
case "RadioContentSourceItemDefinition":
|
||||
case "FortPlaysetGrenadeItemDefinition":
|
||||
case "JunoWeaponCreatureItemDefinition":
|
||||
case "FortEventDependentItemDefinition":
|
||||
case "FortPersonalVehicleItemDefinition":
|
||||
case "FortGameplayModifierItemDefinition":
|
||||
case "FortHardcoreModifierItemDefinition":
|
||||
case "FortWeaponModItemDefinitionMagazine":
|
||||
case "FortConsumableAccountItemDefinition":
|
||||
case "FortConversionControlItemDefinition":
|
||||
case "FortAccountBuffCreditItemDefinition":
|
||||
case "JunoBuildInstructionsItemDefinition":
|
||||
case "FortCharacterCosmeticItemDefinition":
|
||||
case "JunoBuildingSetAccountItemDefinition":
|
||||
case "FortEventCurrencyItemDefinitionRedir":
|
||||
case "FortPersistentResourceItemDefinition":
|
||||
case "FortWeaponMeleeOffhandItemDefinition":
|
||||
case "FortHomebaseBannerIconItemDefinition":
|
||||
case "FortVehicleCosmeticsVariantTokenType":
|
||||
case "JunoBuildingPropAccountItemDefinition":
|
||||
case "FortCampaignHeroLoadoutItemDefinition":
|
||||
case "FortConditionalResourceItemDefinition":
|
||||
case "FortChallengeBundleScheduleDefinition":
|
||||
case "FortWeaponMeleeDualWieldItemDefinition":
|
||||
case "FortDailyRewardScheduleTokenDefinition":
|
||||
case "FortCreativeWeaponRangedItemDefinition":
|
||||
case "FortVehicleCosmeticsItemDefinition_Body":
|
||||
case "FortVehicleCosmeticsItemDefinition_Skin":
|
||||
case "FortVehicleCosmeticsItemDefinition_Wheel":
|
||||
case "FortCreativeRealEstatePlotItemDefinition":
|
||||
case "FortDeployableBaseCloudSaveItemDefinition":
|
||||
case "FortVehicleCosmeticsItemDefinition_Booster":
|
||||
case "AthenaDanceItemDefinition_AdHocSquadsJoin_C":
|
||||
case "FortVehicleCosmeticsItemDefinition_DriftSmoke":
|
||||
case "FortVehicleCosmeticsItemDefinition_EngineAudio":
|
||||
creator = _style switch
|
||||
{
|
||||
EIconStyle.Cataba => new BaseCommunity(_object, _style, "Cataba"),
|
||||
_ => new BaseIcon(_object, _style)
|
||||
};
|
||||
return true;
|
||||
case "JunoAthenaCharacterItemOverrideDefinition":
|
||||
case "JunoAthenaDanceItemOverrideDefinition":
|
||||
creator = new BaseJuno(_object, _style);
|
||||
return true;
|
||||
case "FortTandemCharacterData":
|
||||
creator = new BaseTandem(_object, _style);
|
||||
return true;
|
||||
case "FortTrapItemDefinition":
|
||||
case "FortSpyTechItemDefinition":
|
||||
case "FortAccoladeItemDefinition":
|
||||
case "FortContextTrapItemDefinition":
|
||||
case "FortWeaponRangedItemDefinition":
|
||||
case "Daybreak_LevelExitVehicle_PartItemDefinition_C":
|
||||
creator = new BaseIconStats(_object, _style);
|
||||
return true;
|
||||
case "FortItemSeriesDefinition":
|
||||
creator = new BaseSeries(_object, _style);
|
||||
return true;
|
||||
case "MaterialInstanceConstant"
|
||||
when _object.Owner != null &&
|
||||
(_object.Owner.Name.Contains("/MI_OfferImages/", StringComparison.OrdinalIgnoreCase) ||
|
||||
_object.Owner.Name.EndsWith($"/RenderSwitch_Materials/{_object.Name}", StringComparison.OrdinalIgnoreCase) ||
|
||||
_object.Owner.Name.EndsWith($"/MI_BPTile/{_object.Name}", StringComparison.OrdinalIgnoreCase)):
|
||||
creator = new BaseMaterialInstance(_object, _style);
|
||||
return true;
|
||||
case "AthenaItemShopOfferDisplayData":
|
||||
creator = new BaseOfferDisplayData(_object, _style);
|
||||
return true;
|
||||
case "FortMtxOfferData":
|
||||
creator = new BaseMtxOffer(_object, _style);
|
||||
return true;
|
||||
case "FortPlaylistAthena":
|
||||
creator = new BasePlaylist(_object, _style);
|
||||
return true;
|
||||
case "FortFeatItemDefinition":
|
||||
case "FortQuestItemDefinition":
|
||||
case "FortQuestItemDefinition_Athena":
|
||||
case "FortQuestItemDefinition_Campaign":
|
||||
case "AthenaDailyQuestDefinition":
|
||||
case "FortUrgentQuestItemDefinition":
|
||||
creator = new Bases.FN.BaseQuest(_object, _style);
|
||||
return true;
|
||||
case "FortCompendiumItemDefinition":
|
||||
case "FortChallengeBundleItemDefinition":
|
||||
creator = new BaseBundle(_object, _style);
|
||||
return true;
|
||||
// case "AthenaSeasonItemDefinition":
|
||||
// creator = new BaseSeason(_object, _style);
|
||||
// return true;
|
||||
case "FortItemAccessTokenType":
|
||||
creator = new BaseItemAccessToken(_object, _style);
|
||||
return true;
|
||||
case "FortCreativeOption":
|
||||
case "PlaylistUserOptionEnum":
|
||||
case "PlaylistUserOptionBool":
|
||||
case "PlaylistUserOptionString":
|
||||
case "PlaylistUserOptionIntEnum":
|
||||
case "PlaylistUserOptionIntRange":
|
||||
case "PlaylistUserOptionColorEnum":
|
||||
case "PlaylistUserOptionFloatEnum":
|
||||
case "PlaylistUserOptionFloatRange":
|
||||
case "PlaylistUserTintedIconIntEnum":
|
||||
case "PlaylistUserOptionPrimaryAsset":
|
||||
case "PlaylistUserOptionCollisionProfileEnum":
|
||||
creator = new BaseUserControl(_object, _style);
|
||||
return true;
|
||||
// PandaGame
|
||||
case "CharacterData":
|
||||
creator = new BaseFighter(_object, _style);
|
||||
return true;
|
||||
case "PerkGroup":
|
||||
creator = new BasePerkGroup(_object, _style);
|
||||
return true;
|
||||
case "StatTrackingBundleData":
|
||||
case "HydraSyncedDataAsset":
|
||||
case "AnnouncerPackData":
|
||||
case "CharacterGiftData":
|
||||
case "ProfileIconData":
|
||||
case "RingOutVfxData":
|
||||
case "BannerData":
|
||||
case "EmoteData":
|
||||
case "TauntData":
|
||||
case "SkinData":
|
||||
case "PerkData":
|
||||
creator = new BasePandaIcon(_object, _style);
|
||||
return true;
|
||||
case "QuestData":
|
||||
creator = new Bases.MV.BaseQuest(_object, _style);
|
||||
return true;
|
||||
default:
|
||||
creator = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => $"{_object.ExportType} | {_style}";
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_object = null;
|
||||
}
|
||||
}
|
||||
57
FModel/Creator/Icons/DisplayAssetImage.cs
Normal file
57
FModel/Creator/Icons/DisplayAssetImage.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Icons
|
||||
{
|
||||
static class DisplayAssetImage
|
||||
{
|
||||
public static bool GetDisplayAssetImage(BaseIcon icon, SoftObjectProperty o, ref string assetName)
|
||||
{
|
||||
string imageType = "DetailsImage";
|
||||
switch ("DA_Featured_" + assetName)
|
||||
{
|
||||
case "DA_Featured_Glider_ID_141_AshtonBoardwalk.uasset":
|
||||
case "DA_Featured_Glider_ID_150_TechOpsBlue.uasset":
|
||||
case "DA_Featured_Glider_ID_131_SpeedyMidnight.uasset":
|
||||
case "DA_Featured_Pickaxe_ID_178_SpeedyMidnight.uasset":
|
||||
case "DA_Featured_Glider_ID_015_Brite.uasset":
|
||||
case "DA_Featured_Glider_ID_016_Tactical.uasset":
|
||||
case "DA_Featured_Glider_ID_017_Assassin.uasset":
|
||||
case "DA_Featured_Pickaxe_ID_027_Scavenger.uasset":
|
||||
case "DA_Featured_Pickaxe_ID_028_Space.uasset":
|
||||
case "DA_Featured_Pickaxe_ID_029_Assassin.uasset":
|
||||
return false;
|
||||
case "DA_Featured_Glider_ID_070_DarkViking.uasset":
|
||||
case "DA_Featured_CID_319_Athena_Commando_F_Nautilus.uasset":
|
||||
imageType = "TileImage";
|
||||
break;
|
||||
}
|
||||
|
||||
string path = o?.Value.AssetPathName.String;
|
||||
if (string.IsNullOrEmpty(path))
|
||||
path = "/Game/Catalog/DisplayAssets/DA_Featured_" + assetName.Substring(0, assetName.LastIndexOf("."));
|
||||
|
||||
PakPackage p = Utils.GetPropertyPakPackage(path);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
if (obj != null)
|
||||
{
|
||||
if (obj.TryGetValue(imageType, out var v1) && v1 is StructProperty s && s.Value is UObject type &&
|
||||
type.TryGetValue("ResourceObject", out var v2) && v2 is ObjectProperty resourceObject)
|
||||
{
|
||||
if (!resourceObject.Value.Resource.OuterIndex.Resource.ObjectName.String.Contains("/Game/Athena/Prototype/Textures/"))
|
||||
{
|
||||
icon.IconImage = Utils.GetObjectTexture(resourceObject);
|
||||
assetName = "DA_Featured_" + assetName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
FModel/Creator/Icons/LargeSmallImage.cs
Normal file
61
FModel/Creator/Icons/LargeSmallImage.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Icons
|
||||
{
|
||||
static class LargeSmallImage
|
||||
{
|
||||
public static void GetPreviewImage(BaseIcon icon, StructProperty u)
|
||||
{
|
||||
if (u.Value is UObject o && o.TryGetValue("ResourceObject", out var v) && v is ObjectProperty resourceObject)
|
||||
icon.IconImage = Utils.GetObjectTexture(resourceObject);
|
||||
}
|
||||
public static void GetPreviewImage(BaseIcon icon, ObjectProperty o, string assetName) => GetPreviewImage(icon, o, assetName, true);
|
||||
public static void GetPreviewImage(BaseIcon icon, ObjectProperty o, string assetName, bool hightRes)
|
||||
{
|
||||
string path = o.Value.Resource.OuterIndex.Resource.ObjectName.String;
|
||||
if (path.Equals("/Game/Athena/Items/Weapons/WID_Harvest_Pickaxe_STWCosmetic_Tier"))
|
||||
path += "_" + assetName.Substring(assetName.LastIndexOf(".") - 1, 1);
|
||||
|
||||
PakPackage p = Utils.GetPropertyPakPackage(path);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
if (GetPreviewImage(icon, p.GetIndexedExport<UObject>(0), hightRes))
|
||||
return;
|
||||
else if (GetPreviewImage(icon, p.GetIndexedExport<UObject>(1), hightRes)) // FortniteGame/Content/Athena/Items/Cosmetics/Pickaxes/Pickaxe_ID_402_BlackKnightFemale1H.uasset
|
||||
return;
|
||||
}
|
||||
}
|
||||
public static void GetPreviewImage(BaseIcon icon, SoftObjectProperty s) => icon.IconImage = Utils.GetSoftObjectTexture(s);
|
||||
|
||||
private static bool GetPreviewImage(BaseIcon icon, UObject obj, bool hightRes)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
if (hightRes && obj.TryGetValue("LargePreviewImage", out var sLarge) && sLarge is SoftObjectProperty largePreviewImage && !string.IsNullOrEmpty(largePreviewImage.Value.AssetPathName.String))
|
||||
{
|
||||
GetPreviewImage(icon, largePreviewImage);
|
||||
return true;
|
||||
}
|
||||
else if (obj.TryGetValue("SmallPreviewImage", out var sSmall1) && sSmall1 is SoftObjectProperty smallPreviewImage1 && !string.IsNullOrEmpty(smallPreviewImage1.Value.AssetPathName.String))
|
||||
{
|
||||
GetPreviewImage(icon, smallPreviewImage1);
|
||||
return true;
|
||||
}
|
||||
else if (obj.TryGetValue("SmallPreviewImage", out var sSmall2) && sSmall2 is ObjectProperty smallPreviewImage2 && !string.IsNullOrEmpty(smallPreviewImage2.Value.Resource.OuterIndex.Resource.ObjectName.String))
|
||||
{
|
||||
icon.IconImage = Utils.GetObjectTexture(smallPreviewImage2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void DrawPreviewImage(SKCanvas c, BaseIcon icon) =>
|
||||
c.DrawBitmap(icon.IconImage ?? icon.FallbackImage, new SKRect(icon.Margin, icon.Margin, icon.Size - icon.Margin, icon.Size - icon.Margin),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
}
|
||||
}
|
||||
73
FModel/Creator/Icons/UserFacingFlag.cs
Normal file
73
FModel/Creator/Icons/UserFacingFlag.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
|
||||
namespace FModel.Creator.Icons
|
||||
{
|
||||
static class UserFacingFlag
|
||||
{
|
||||
public static void GetUserFacingFlags(List<string> uffs, BaseIcon icon, string exportType)
|
||||
{
|
||||
if (uffs.Count > 0)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/Items/ItemCategories"); //PrimaryCategories - SecondaryCategories - TertiaryCategories
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var o = p.GetExport<UObject>();
|
||||
if (o != null && o.TryGetValue("TertiaryCategories", out var tertiaryCategories) && tertiaryCategories is ArrayProperty tertiaryArray)
|
||||
{
|
||||
icon.UserFacingFlags = new SKBitmap[uffs.Count];
|
||||
for (int i = 0; i < uffs.Count; i++)
|
||||
{
|
||||
if (uffs[i].Equals("Cosmetics.UserFacingFlags.HasUpgradeQuests"))
|
||||
{
|
||||
if (exportType.Equals("AthenaPetCarrierItemDefinition"))
|
||||
icon.UserFacingFlags[i] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Pets-64.png")).Stream);
|
||||
else
|
||||
icon.UserFacingFlags[i] = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T-Icon-Quests-64.png")).Stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (StructProperty structProp in tertiaryArray.Value)
|
||||
{
|
||||
if (structProp.Value is UObject mainUObject &&
|
||||
mainUObject.TryGetValue("TagContainer", out var struc1) && struc1 is StructProperty tagContainer && tagContainer.Value is FGameplayTagContainer f && f.GameplayTags.TryGetGameplayTag(uffs[i], out var _) &&
|
||||
mainUObject.TryGetValue("CategoryBrush", out var struc2) && struc2 is StructProperty categoryBrush && categoryBrush.Value is UObject categoryUObject &&
|
||||
categoryUObject.TryGetValue("Brush_XXS", out var struc3) && struc3 is StructProperty brushXXS && brushXXS.Value is UObject brushUObject &&
|
||||
brushUObject.TryGetValue("ResourceObject", out var object1) && object1 is ObjectProperty resourceObject)
|
||||
{
|
||||
icon.UserFacingFlags[i] = Utils.GetObjectTexture(resourceObject);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawUserFacingFlags(SKCanvas c, BaseIcon icon)
|
||||
{
|
||||
if (icon.UserFacingFlags != null)
|
||||
{
|
||||
int size = 25;
|
||||
int x = icon.Margin * (int)2.5;
|
||||
foreach (SKBitmap b in icon.UserFacingFlags)
|
||||
{
|
||||
if (b == null)
|
||||
continue;
|
||||
|
||||
c.DrawBitmap(b.Resize(size, size), new SKPoint(x, icon.Margin * (int)2.5), new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
x += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
FModel/Creator/Icons/Watermark.cs
Normal file
31
FModel/Creator/Icons/Watermark.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using SkiaSharp;
|
||||
using System.IO;
|
||||
|
||||
namespace FModel.Creator.Icons
|
||||
{
|
||||
static class Watermark
|
||||
{
|
||||
public static void DrawWatermark(SKCanvas c)
|
||||
{
|
||||
if (Properties.Settings.Default.UseIconWatermark && !string.IsNullOrWhiteSpace(Properties.Settings.Default.IconWatermarkPath))
|
||||
{
|
||||
using SKBitmap watermarkBase = SKBitmap.Decode(new FileInfo(Properties.Settings.Default.IconWatermarkPath).Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
|
||||
int sizeX = watermarkBase.Width * (int)Properties.Settings.Default.IconWatermarkScale / 512;
|
||||
int sizeY = watermarkBase.Height * (int)Properties.Settings.Default.IconWatermarkScale / 512;
|
||||
SKBitmap watermark = watermarkBase.Resize(sizeX, sizeY);
|
||||
|
||||
float left = Properties.Settings.Default.IconWatermarkX;
|
||||
float top = Properties.Settings.Default.IconWatermarkY;
|
||||
float right = left + watermark.Width;
|
||||
float bottom = top + watermark.Height;
|
||||
c.DrawBitmap(watermark, new SKRect(left, top, right, bottom),
|
||||
new SKPaint
|
||||
{
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
IsAntialias = true,
|
||||
Color = SKColors.Transparent.WithAlpha((byte)Properties.Settings.Default.IconWatermarkOpacity)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
FModel/Creator/Rarities/EFortRarity.cs
Normal file
15
FModel/Creator/Rarities/EFortRarity.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
namespace FModel.Creator.Rarities
|
||||
{
|
||||
public enum EFortRarity : int
|
||||
{
|
||||
Common,
|
||||
Uncommon,
|
||||
Rare,
|
||||
Epic,
|
||||
Legendary,
|
||||
Mythic,
|
||||
Transcendent,
|
||||
Unattainable,
|
||||
//Impossible
|
||||
}
|
||||
}
|
||||
183
FModel/Creator/Rarities/Rarity.cs
Normal file
183
FModel/Creator/Rarities/Rarity.cs
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System.Linq;
|
||||
|
||||
namespace FModel.Creator.Rarities
|
||||
{
|
||||
static class Rarity
|
||||
{
|
||||
public static void GetInGameRarity(BaseIcon icon, EnumProperty e)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/Balance/RarityData");
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UObject>();
|
||||
if (d != null)
|
||||
{
|
||||
EFortRarity rarity = EFortRarity.Uncommon;
|
||||
switch (e?.Value.String)
|
||||
{
|
||||
case "EFortRarity::Common":
|
||||
case "EFortRarity::Handmade":
|
||||
rarity = EFortRarity.Common;
|
||||
break;
|
||||
case "EFortRarity::Rare":
|
||||
case "EFortRarity::Sturdy":
|
||||
rarity = EFortRarity.Rare;
|
||||
break;
|
||||
case "EFortRarity::Epic":
|
||||
case "EFortRarity::Quality":
|
||||
rarity = EFortRarity.Epic;
|
||||
break;
|
||||
case "EFortRarity::Legendary":
|
||||
case "EFortRarity::Fine":
|
||||
rarity = EFortRarity.Legendary;
|
||||
break;
|
||||
case "EFortRarity::Mythic":
|
||||
case "EFortRarity::Elegant":
|
||||
rarity = EFortRarity.Mythic;
|
||||
break;
|
||||
case "EFortRarity::Transcendent":
|
||||
case "EFortRarity::Masterwork":
|
||||
rarity = EFortRarity.Transcendent;
|
||||
break;
|
||||
case "EFortRarity::Unattainable":
|
||||
case "EFortRarity::Badass":
|
||||
rarity = EFortRarity.Unattainable;
|
||||
break;
|
||||
}
|
||||
|
||||
if (d.Values.ElementAt((int)rarity) is StructProperty s && s.Value is UObject colors)
|
||||
{
|
||||
if (colors.TryGetValue("Color1", out var c1) && c1 is StructProperty s1 && s1.Value is FLinearColor color1 &&
|
||||
colors.TryGetValue("Color2", out var c2) && c2 is StructProperty s2 && s2.Value is FLinearColor color2 &&
|
||||
colors.TryGetValue("Color3", out var c3) && c3 is StructProperty s3 && s3.Value is FLinearColor color3)
|
||||
{
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse(color1.Hex), SKColor.Parse(color3.Hex) };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else GetHardCodedRarity(icon, e);
|
||||
}
|
||||
|
||||
public static void GetHardCodedRarity(BaseIcon icon, EnumProperty e)
|
||||
{
|
||||
switch (e?.Value.String)
|
||||
{
|
||||
case "EFortRarity::Common":
|
||||
case "EFortRarity::Handmade":
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse("6D6D6D"), SKColor.Parse("333333") };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse("9E9E9E"), SKColor.Parse("9E9E9E") };
|
||||
break;
|
||||
case "EFortRarity::Rare":
|
||||
case "EFortRarity::Sturdy":
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse("3669BB"), SKColor.Parse("133254") };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse("5180EE"), SKColor.Parse("5180EE") };
|
||||
break;
|
||||
case "EFortRarity::Epic":
|
||||
case "EFortRarity::Quality":
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse("8138C2"), SKColor.Parse("35155C") };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse("B251ED"), SKColor.Parse("B251ED") };
|
||||
break;
|
||||
case "EFortRarity::Legendary":
|
||||
case "EFortRarity::Fine":
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse("C06A38"), SKColor.Parse("5C2814") };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse("EC9650"), SKColor.Parse("EC9650") };
|
||||
break;
|
||||
case "EFortRarity::Mythic":
|
||||
case "EFortRarity::Elegant":
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse("BA9C36"), SKColor.Parse("594415") };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse("EED951"), SKColor.Parse("EED951") };
|
||||
break;
|
||||
case "EFortRarity::Transcendent":
|
||||
case "EFortRarity::Masterwork":
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse("D51944"), SKColor.Parse("660522") };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse("FF3F58"), SKColor.Parse("FF3F58") };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawRarity(SKCanvas c, BaseIcon icon)
|
||||
{
|
||||
// border
|
||||
c.DrawRect(new SKRect(0, 0, icon.Size, icon.Size),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateLinearGradient(
|
||||
new SKPoint(icon.Size / 2, icon.Size),
|
||||
new SKPoint(icon.Size, icon.Size / 4),
|
||||
icon.RarityBorderColor,
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
|
||||
switch ((EIconDesign)Properties.Settings.Default.AssetsIconDesign)
|
||||
{
|
||||
case EIconDesign.Flat:
|
||||
{
|
||||
if (icon.RarityBackgroundImage != null)
|
||||
c.DrawBitmap(icon.RarityBackgroundImage, new SKRect(icon.Margin, icon.Margin, icon.Size - icon.Margin, icon.Size - icon.Margin),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
else
|
||||
{
|
||||
c.DrawRect(new SKRect(icon.Margin, icon.Margin, icon.Size - icon.Margin, icon.Size - icon.Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = icon.RarityBackgroundColors[0]
|
||||
});
|
||||
|
||||
var paint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = icon.RarityBackgroundColors[1].WithAlpha(75)
|
||||
};
|
||||
var pathTop = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
pathTop.MoveTo(icon.Margin, icon.Margin);
|
||||
pathTop.LineTo(icon.Margin + (icon.Size / 17 * 10), icon.Margin);
|
||||
pathTop.LineTo(icon.Margin, icon.Margin + (icon.Size / 17));
|
||||
pathTop.Close();
|
||||
c.DrawPath(pathTop, paint);
|
||||
|
||||
var pathBottom = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
pathBottom.MoveTo(icon.Margin, icon.Size - icon.Margin);
|
||||
pathBottom.LineTo(icon.Margin, icon.Size - icon.Margin - (icon.Size / 17 * 2.5f));
|
||||
pathBottom.LineTo(icon.Size - icon.Margin, icon.Size - icon.Margin - (icon.Size / 17 * 4.5f));
|
||||
pathBottom.LineTo(icon.Size - icon.Margin, icon.Size - icon.Margin);
|
||||
pathBottom.Close();
|
||||
c.DrawPath(pathBottom, paint);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (icon.RarityBackgroundImage != null)
|
||||
c.DrawBitmap(icon.RarityBackgroundImage, new SKRect(icon.Margin, icon.Margin, icon.Size - icon.Margin, icon.Size - icon.Margin),
|
||||
new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
else
|
||||
c.DrawRect(new SKRect(icon.Margin, icon.Margin, icon.Size - icon.Margin, icon.Size - icon.Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateRadialGradient(
|
||||
new SKPoint(icon.Size / 2, icon.Size / 2),
|
||||
icon.Size / 5 * 4,
|
||||
icon.RarityBackgroundColors,
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
FModel/Creator/Rarities/Serie.cs
Normal file
40
FModel/Creator/Rarities/Serie.cs
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Rarities
|
||||
{
|
||||
static class Serie
|
||||
{
|
||||
public static void GetRarity(BaseIcon icon, ObjectProperty o)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(o.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
if (obj != null)
|
||||
GetRarity(icon, obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetRarity(BaseIcon icon, IUExport export)
|
||||
{
|
||||
if (export.TryGetValue("BackgroundTexture", out var t) && t is SoftObjectProperty sop)
|
||||
icon.RarityBackgroundImage = Utils.GetSoftObjectTexture(sop);
|
||||
|
||||
if (export.TryGetValue("Colors", out var v) && v is StructProperty s && s.Value is UObject colors)
|
||||
{
|
||||
if (colors.TryGetValue("Color1", out var c1) && c1 is StructProperty s1 && s1.Value is FLinearColor color1 &&
|
||||
colors.TryGetValue("Color2", out var c2) && c2 is StructProperty s2 && s2.Value is FLinearColor color2 &&
|
||||
colors.TryGetValue("Color4", out var c4) && c4 is StructProperty s4 && s4.Value is FLinearColor color4)
|
||||
{
|
||||
icon.RarityBackgroundColors = new SKColor[2] { SKColor.Parse(color1.Hex), SKColor.Parse(color4.Hex) };
|
||||
icon.RarityBorderColor = new SKColor[2] { SKColor.Parse(color2.Hex), SKColor.Parse(color1.Hex) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
FModel/Creator/Stats/Statistic.cs
Normal file
12
FModel/Creator/Stats/Statistic.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator.Stats
|
||||
{
|
||||
public class Statistic
|
||||
{
|
||||
public SKBitmap Icon;
|
||||
public string Description;
|
||||
public string DisplayName;
|
||||
public int Height;
|
||||
}
|
||||
}
|
||||
204
FModel/Creator/Stats/Statistics.cs
Normal file
204
FModel/Creator/Stats/Statistics.cs
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Texts;
|
||||
using FModel.Utils;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace FModel.Creator.Stats
|
||||
{
|
||||
static class Statistics
|
||||
{
|
||||
public static void GetAmmoData(BaseIcon icon, SoftObjectProperty ammoData)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(ammoData.Value.AssetPathName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
if (obj != null)
|
||||
{
|
||||
if (obj.TryGetValue("DisplayName", out var v1) && v1 is TextProperty displayName &&
|
||||
obj.TryGetValue("SmallPreviewImage", out var v2) && v2 is SoftObjectProperty smallPreviewImage)
|
||||
{
|
||||
icon.Stats.Add(new Statistic
|
||||
{
|
||||
Icon = Utils.GetSoftObjectTexture(smallPreviewImage),
|
||||
Description = Text.GetTextPropertyBase(displayName).ToUpper()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetWeaponStats(BaseIcon icon, StructProperty weaponStatHandle)
|
||||
{
|
||||
if (weaponStatHandle.Value is UObject o1 &&
|
||||
o1.TryGetValue("DataTable", out var c1) && c1 is ObjectProperty dataTable &&
|
||||
o1.TryGetValue("RowName", out var c2) && c2 is NameProperty rowName)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(dataTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var table = p.GetExport<UDataTable>();
|
||||
if (table != null)
|
||||
{
|
||||
if (table.TryGetValue(rowName.Value.String, out var v1) && v1 is UObject stats)
|
||||
{
|
||||
if (stats.TryGetValue("ReloadTime", out var s1) && s1 is FloatProperty reloadTime && reloadTime.Value != 0)
|
||||
icon.Stats.Add(new Statistic
|
||||
{
|
||||
Icon = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_ReloadTime_Weapon_Stats.png")).Stream),
|
||||
Description = $"{Localizations.GetLocalization(string.Empty, "6EA26D1A4252034FBD869A90F9A6E49A", "Reload Time")} ({Localizations.GetLocalization(string.Empty, "6BA53D764BA5CC13E821D2A807A72365", "seconds")}) : {reloadTime.Value:0.0}".ToUpper()
|
||||
});
|
||||
|
||||
if (stats.TryGetValue("ClipSize", out var s2) && s2 is IntProperty clipSize && clipSize.Value != 0)
|
||||
icon.Stats.Add(new Statistic
|
||||
{
|
||||
Icon = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_ClipSize_Weapon_Stats.png")).Stream),
|
||||
Description = $"{Localizations.GetLocalization(string.Empty, "068239DD4327B36124498C9C5F61C038", "Magazine Size")} : {clipSize.Value}".ToUpper()
|
||||
});
|
||||
|
||||
if (stats.TryGetValue("DmgPB", out var s3) && s3 is FloatProperty dmgPB && dmgPB.Value != 0)
|
||||
icon.Stats.Add(new Statistic
|
||||
{
|
||||
Icon = SKBitmap.Decode(Application.GetResourceStream(new Uri("pack://application:,,,/Resources/T_DamagePerBullet_Weapon_Stats.png")).Stream),
|
||||
Description = $"{Localizations.GetLocalization(string.Empty, "BF7E3CF34A9ACFF52E95EAAD4F09F133", "Damage to Player")} : {dmgPB.Value}".ToUpper()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GetHeroStats(BaseIcon icon, ObjectProperty heroGameplayDefinition)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(heroGameplayDefinition.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var obj = p.GetExport<UObject>();
|
||||
if (obj != null)
|
||||
{
|
||||
if (obj.TryGetValue("HeroPerk", out var v1) && v1 is StructProperty s1 && s1.Value is UObject heroPerk)
|
||||
{
|
||||
GetAbilityKit(icon, heroPerk);
|
||||
}
|
||||
|
||||
if (obj.TryGetValue("TierAbilityKits", out var v2) && v2 is ArrayProperty tierAbilityKits)
|
||||
{
|
||||
foreach (StructProperty abilityKit in tierAbilityKits.Value)
|
||||
{
|
||||
if (abilityKit.Value is UObject kit)
|
||||
{
|
||||
GetAbilityKit(icon, kit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetAbilityKit(BaseIcon icon, UObject parent)
|
||||
{
|
||||
if (parent.TryGetValue("GrantedAbilityKit", out var v) && v is SoftObjectProperty grantedAbilityKit)
|
||||
{
|
||||
PakPackage k = Utils.GetPropertyPakPackage(grantedAbilityKit.Value.AssetPathName.String);
|
||||
if (k.HasExport() && !k.Equals(default))
|
||||
{
|
||||
var kit = k.GetExport<UObject>();
|
||||
if (kit != null &&
|
||||
kit.GetExport<TextProperty>("DisplayName") is TextProperty displayName &&
|
||||
kit.GetExport<StructProperty>("IconBrush") is StructProperty brush && brush.Value is UObject iconBrush &&
|
||||
iconBrush.TryGetValue("ResourceObject", out var s) && s is ObjectProperty resourceObject)
|
||||
{
|
||||
icon.Stats.Add(new Statistic
|
||||
{
|
||||
Icon = Utils.GetObjectTexture(resourceObject),
|
||||
Description = Text.GetTextPropertyBase(displayName).ToUpper()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawStats(SKCanvas c, BaseIcon icon)
|
||||
{
|
||||
int size = 48;
|
||||
int iconSize = 40;
|
||||
int textSize = 25;
|
||||
int y = icon.Size;
|
||||
foreach (Statistic stat in icon.Stats)
|
||||
{
|
||||
c.DrawRect(new SKRect(0, y, icon.Size, y + size),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateLinearGradient(
|
||||
new SKPoint(icon.Size / 2, icon.Size),
|
||||
new SKPoint(icon.Size, icon.Size / 4),
|
||||
icon.RarityBorderColor,
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
|
||||
|
||||
if ((EIconDesign)Properties.Settings.Default.AssetsIconDesign == EIconDesign.Flat)
|
||||
{
|
||||
c.DrawRect(new SKRect(icon.Margin, y, icon.Size - icon.Margin, y + size - icon.Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = icon.RarityBackgroundColors[0]
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
c.DrawRect(new SKRect(icon.Margin, y, icon.Size - icon.Margin, y + size - icon.Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Shader = SKShader.CreateRadialGradient(
|
||||
new SKPoint(icon.Size / 2, icon.Size / 2),
|
||||
icon.Size / 5 * 4,
|
||||
icon.RarityBackgroundColors,
|
||||
SKShaderTileMode.Clamp)
|
||||
});
|
||||
}
|
||||
|
||||
c.DrawRect(new SKRect(icon.Margin, y, icon.Size - icon.Margin, y + size - icon.Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = new SKColor(0, 0, 50, 75)
|
||||
});
|
||||
|
||||
c.DrawBitmap(stat.Icon.Resize(iconSize, iconSize), new SKPoint(icon.Margin * (int)2.5, y + 4), new SKPaint { FilterQuality = SKFilterQuality.High, IsAntialias = true });
|
||||
|
||||
var statPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = Text.TypeFaces.DisplayNameTypeface,
|
||||
TextSize = textSize,
|
||||
Color = SKColors.White,
|
||||
TextAlign = SKTextAlign.Center,
|
||||
};
|
||||
|
||||
// resize if too long
|
||||
while (statPaint.MeasureText(stat.Description) > (icon.Size - (icon.Margin * 2) - iconSize))
|
||||
{
|
||||
statPaint.TextSize = textSize -= 2;
|
||||
}
|
||||
|
||||
c.DrawText(stat.Description, icon.Size / 2, y + 32, statPaint);
|
||||
|
||||
y += size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
FModel/Creator/Texts/ETextSide.cs
Normal file
9
FModel/Creator/Texts/ETextSide.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
namespace FModel.Creator.Texts
|
||||
{
|
||||
public enum ETextSide
|
||||
{
|
||||
Center,
|
||||
Right,
|
||||
Left
|
||||
}
|
||||
}
|
||||
73
FModel/Creator/Texts/GameplayTag.cs
Normal file
73
FModel/Creator/Texts/GameplayTag.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using FModel.Creator.Bases;
|
||||
using FModel.Creator.Icons;
|
||||
using FModel.Utils;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
|
||||
namespace FModel.Creator.Texts
|
||||
{
|
||||
static class GameplayTag
|
||||
{
|
||||
public static void GetGameplayTags(BaseIcon icon, StructProperty s, string exportType)
|
||||
{
|
||||
if (s.Value is FGameplayTagContainer g)
|
||||
{
|
||||
if (g.GameplayTags.TryGetGameplayTag("Cosmetics.Source.", out var source))
|
||||
icon.CosmeticSource = source.String.Substring("Cosmetics.Source.".Length);
|
||||
else if(g.GameplayTags.TryGetGameplayTag("Athena.ItemAction.", out var action))
|
||||
icon.CosmeticSource = action.String.Substring("Athena.ItemAction.".Length);
|
||||
|
||||
if (g.GameplayTags.TryGetGameplayTag("Cosmetics.Set.", out var set))
|
||||
icon.Description += GetCosmeticSet(set.String);
|
||||
if (g.GameplayTags.TryGetGameplayTag("Cosmetics.Filter.Season.", out var season))
|
||||
icon.Description += GetCosmeticSeason(season.String);
|
||||
|
||||
UserFacingFlag.GetUserFacingFlags(
|
||||
g.GameplayTags.GetAllGameplayTag("Cosmetics.UserFacingFlags.", "Homebase.Class.", "NPC.CharacterType.Survivor.Defender."),
|
||||
icon, exportType);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetCosmeticSet(string setName)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage("/Game/Athena/Items/Cosmetics/Metadata/CosmeticSets");
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var d = p.GetExport<UDataTable>();
|
||||
if (d != null && d.TryGetValue(setName, out var obj) && obj is UObject o)
|
||||
{
|
||||
if (o.TryGetValue("DisplayName", out var displayName) && displayName is TextProperty t)
|
||||
{
|
||||
(string n, string k, string s) = Text.GetTextPropertyBases(t);
|
||||
string set = Localizations.GetLocalization(n, k, s);
|
||||
string format = Localizations.GetLocalization("Fort.Cosmetics", "CosmeticItemDescription_SetMembership_NotRich", "\nPart of the {0} set.");
|
||||
return string.Format(format, set);
|
||||
}
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private static string GetCosmeticSeason(string seasonNumber)
|
||||
{
|
||||
string s = seasonNumber.Substring("Cosmetics.Filter.Season.".Length);
|
||||
int number = int.Parse(s);
|
||||
if (number == 10)
|
||||
s = "X";
|
||||
|
||||
string season = Localizations.GetLocalization("AthenaSeasonItemDefinitionInternal", "SeasonTextFormat", "Season {0}");
|
||||
string introduced = Localizations.GetLocalization("Fort.Cosmetics", "CosmeticItemDescription_Season", "\nIntroduced in <SeasonText>{0}</>.")
|
||||
.Replace("<SeasonText>", string.Empty).Replace("</>", string.Empty);
|
||||
if (number > 10)
|
||||
{
|
||||
string chapter = Localizations.GetLocalization("AthenaSeasonItemDefinitionInternal", "ChapterTextFormat", "Chapter {0}");
|
||||
string chapterFormat = Localizations.GetLocalization("AthenaSeasonItemDefinitionInternal", "ChapterSeasonTextFormat", "{0}, {1}");
|
||||
string d = string.Format(chapterFormat, string.Format(chapter, number / 10 + 1), string.Format(season, s[^1..]));
|
||||
return string.Format(introduced, d);
|
||||
}
|
||||
else return string.Format(introduced, string.Format(season, s));
|
||||
}
|
||||
}
|
||||
}
|
||||
111
FModel/Creator/Texts/Helper.cs
Normal file
111
FModel/Creator/Texts/Helper.cs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
using FModel.Creator.Bases;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FModel.Creator.Texts
|
||||
{
|
||||
static class Helper
|
||||
{
|
||||
public class Line
|
||||
{
|
||||
public string Value { get; set; }
|
||||
public float Width { get; set; }
|
||||
}
|
||||
|
||||
public static void DrawCenteredMultilineText(SKCanvas canvas, string text, int maxLineCount, BaseIcon icon, ETextSide side, SKRect area, SKPaint paint)
|
||||
=> DrawCenteredMultilineText(canvas, text, maxLineCount, icon.Size, icon.Margin, side, area, paint);
|
||||
public static void DrawCenteredMultilineText(SKCanvas canvas, string text, int maxLineCount, int size, int margin, ETextSide side, SKRect area, SKPaint paint)
|
||||
{
|
||||
float lineHeight = paint.TextSize * 1.2f;
|
||||
Line[] lines = SplitLines(text, paint, area.Width - margin);
|
||||
|
||||
if (lines == null)
|
||||
return;
|
||||
if (lines.Length <= maxLineCount)
|
||||
maxLineCount = lines.Length;
|
||||
|
||||
float height = maxLineCount * lineHeight;
|
||||
float y = area.MidY - height / 2;
|
||||
for (int i = 0; i < maxLineCount; i++)
|
||||
{
|
||||
y += lineHeight;
|
||||
float x = side switch
|
||||
{
|
||||
ETextSide.Center => area.MidX - lines[i].Width / 2,
|
||||
ETextSide.Right => size - margin - lines[i].Width,
|
||||
ETextSide.Left => margin,
|
||||
_ => area.MidX - lines[i].Width / 2
|
||||
};
|
||||
canvas.DrawText(lines[i].Value.TrimEnd(), x, y, paint);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawMultilineText(SKCanvas canvas, string text, int size, int margin, ETextSide side, SKRect area, SKPaint paint, out int yPos)
|
||||
{
|
||||
float lineHeight = paint.TextSize * 1.2f;
|
||||
Line[] lines = SplitLines(text, paint, area.Width);
|
||||
if (lines == null)
|
||||
{
|
||||
yPos = (int)area.Top;
|
||||
return;
|
||||
}
|
||||
|
||||
float y = area.Top;
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
float x = side switch
|
||||
{
|
||||
ETextSide.Center => area.MidX - lines[i].Width / 2,
|
||||
ETextSide.Right => size - margin - lines[i].Width,
|
||||
ETextSide.Left => area.Left,
|
||||
_ => area.MidX - lines[i].Width / 2
|
||||
};
|
||||
canvas.DrawText(lines[i].Value.TrimEnd(), x, y, paint);
|
||||
y += lineHeight;
|
||||
}
|
||||
yPos = (int)area.Top + ((int)lineHeight * lines.Length);
|
||||
}
|
||||
|
||||
public static Line[] SplitLines(string text, SKPaint paint, float maxWidth)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return null;
|
||||
|
||||
float spaceWidth = paint.MeasureText(" ");
|
||||
string[] lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
List<Line> ret = new List<Line>(lines.Length);
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lines[i]))
|
||||
continue;
|
||||
|
||||
float width = 0;
|
||||
var lineResult = new StringBuilder();
|
||||
string[] words = lines[i].Split(' ', StringSplitOptions.None);
|
||||
foreach (var word in words)
|
||||
{
|
||||
float wordWidth = paint.MeasureText(word);
|
||||
float wordWithSpaceWidth = wordWidth + spaceWidth;
|
||||
string wordWithSpace = word + " ";
|
||||
|
||||
if (width + wordWidth > maxWidth)
|
||||
{
|
||||
ret.Add(new Line { Value = lineResult.ToString(), Width = width });
|
||||
lineResult = new StringBuilder(wordWithSpace);
|
||||
width = wordWithSpaceWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineResult.Append(wordWithSpace);
|
||||
width += wordWithSpaceWidth;
|
||||
}
|
||||
}
|
||||
ret.Add(new Line { Value = lineResult.ToString(), Width = width });
|
||||
}
|
||||
return ret.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
220
FModel/Creator/Texts/Text.cs
Normal file
220
FModel/Creator/Texts/Text.cs
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
using FModel.Creator.Bases;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.Objects;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Creator.Texts
|
||||
{
|
||||
static class Text
|
||||
{
|
||||
public static Typefaces TypeFaces = new Typefaces();
|
||||
private const int _STARTER_TEXT_POSITION = 380;
|
||||
private static int _BOTTOM_TEXT_SIZE = 15;
|
||||
private static int _NAME_TEXT_SIZE = 45;
|
||||
|
||||
public static string GetTextPropertyBase(TextProperty t)
|
||||
{
|
||||
if (t.Value is FText text)
|
||||
if (text.Text is FTextHistory.None n)
|
||||
return n.CultureInvariantString;
|
||||
else if (text.Text is FTextHistory.Base b)
|
||||
return b.SourceString.Replace("<Emphasized>", string.Empty).Replace("</>", string.Empty);
|
||||
else if (text.Text is FTextHistory.StringTableEntry s)
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(s.TableId.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var table = p.GetExport<UStringTable>();
|
||||
if (table != null)
|
||||
{
|
||||
if (table.TryGetValue("StringTable", out var v1) && v1 is FStringTable stringTable &&
|
||||
stringTable.KeysToMetadata.TryGetValue(stringTable.TableNamespace, out var v2) && v2 is Dictionary<string, string> dico &&
|
||||
dico.TryGetValue(s.Key, out var ret))
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
public static string GetTextPropertyBase(ArrayProperty a)
|
||||
{
|
||||
if (a.Value.Length > 0 && a.Value[0] is TextProperty t)
|
||||
return GetTextPropertyBase(t);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static (string, string, string) GetTextPropertyBases(TextProperty t)
|
||||
{
|
||||
if (t.Value is FText text && text.Text is FTextHistory.Base b)
|
||||
return (b.Namespace, b.Key, b.SourceString);
|
||||
return (string.Empty, string.Empty, string.Empty);
|
||||
}
|
||||
|
||||
public static string GetMaxStackSize(StructProperty maxStackSize)
|
||||
{
|
||||
if (maxStackSize.Value is UObject o1)
|
||||
{
|
||||
if (o1.TryGetValue("Value", out var c) && c is FloatProperty value && value.Value != -1) // old way
|
||||
return $"MaxStackSize : {value.Value}";
|
||||
else if (
|
||||
o1.TryGetValue("Curve", out var c1) && c1 is StructProperty curve && curve.Value is UObject o2 &&
|
||||
o2.TryGetValue("CurveTable", out var c2) && c2 is ObjectProperty curveTable &&
|
||||
o2.TryGetValue("RowName", out var c3) && c3 is NameProperty rowName) // new way
|
||||
{
|
||||
PakPackage p = Utils.GetPropertyPakPackage(curveTable.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
var table = p.GetExport<UCurveTable>();
|
||||
if (table != null)
|
||||
{
|
||||
if (table.TryGetValue(rowName.Value.String, out var v1) && v1 is UObject maxStackAmount &&
|
||||
maxStackAmount.TryGetValue("Keys", out var v2) && v2 is ArrayProperty keys &&
|
||||
keys.Value.Length > 0 && (keys.Value[0] as StructProperty).Value is FSimpleCurveKey amount &&
|
||||
amount.KeyValue != -1)
|
||||
{
|
||||
return $"MaxStackSize : {amount.KeyValue}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static void DrawBackground(SKCanvas c, BaseIcon icon)
|
||||
{
|
||||
switch ((EIconDesign)Properties.Settings.Default.AssetsIconDesign)
|
||||
{
|
||||
case EIconDesign.Flat:
|
||||
{
|
||||
var pathBottom = new SKPath { FillType = SKPathFillType.EvenOdd };
|
||||
pathBottom.MoveTo(icon.Margin, icon.Size - icon.Margin);
|
||||
pathBottom.LineTo(icon.Margin, icon.Size - icon.Margin - (icon.Size / 17 * 2.5f));
|
||||
pathBottom.LineTo(icon.Size - icon.Margin, icon.Size - icon.Margin - (icon.Size / 17 * 4.5f));
|
||||
pathBottom.LineTo(icon.Size - icon.Margin, icon.Size - icon.Margin);
|
||||
pathBottom.Close();
|
||||
c.DrawPath(pathBottom, new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = new SKColor(0, 0, 50, 75),
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
c.DrawRect(
|
||||
new SKRect(icon.Margin, _STARTER_TEXT_POSITION, icon.Size - icon.Margin, icon.Size - icon.Margin),
|
||||
new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Color = new SKColor(0, 0, 50, 75),
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawDisplayName(SKCanvas c, BaseIcon icon)
|
||||
{
|
||||
_NAME_TEXT_SIZE = 45;
|
||||
string text = icon.DisplayName;
|
||||
SKTextAlign side = SKTextAlign.Center;
|
||||
int x = icon.Size / 2;
|
||||
int y = _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE;
|
||||
switch ((EIconDesign)Properties.Settings.Default.AssetsIconDesign)
|
||||
{
|
||||
case EIconDesign.Mini:
|
||||
{
|
||||
_NAME_TEXT_SIZE = 47;
|
||||
text = text.ToUpper();
|
||||
break;
|
||||
}
|
||||
case EIconDesign.Flat:
|
||||
{
|
||||
_NAME_TEXT_SIZE = 47;
|
||||
side = SKTextAlign.Right;
|
||||
x = icon.Size - icon.Margin * 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SKPaint namePaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = TypeFaces.DisplayNameTypeface,
|
||||
TextSize = _NAME_TEXT_SIZE,
|
||||
Color = SKColors.White,
|
||||
TextAlign = side,
|
||||
};
|
||||
|
||||
// resize if too long
|
||||
while (namePaint.MeasureText(text) > (icon.Size - (icon.Margin * 2)))
|
||||
{
|
||||
namePaint.TextSize = _NAME_TEXT_SIZE -= 2;
|
||||
}
|
||||
|
||||
c.DrawText(text, x, y, namePaint);
|
||||
}
|
||||
|
||||
public static void DrawDescription(SKCanvas c, BaseIcon icon)
|
||||
{
|
||||
int maxLine = 4;
|
||||
_BOTTOM_TEXT_SIZE = 15;
|
||||
string text = icon.Description;
|
||||
ETextSide side = ETextSide.Center;
|
||||
switch ((EIconDesign)Properties.Settings.Default.AssetsIconDesign)
|
||||
{
|
||||
case EIconDesign.Mini:
|
||||
{
|
||||
maxLine = 5;
|
||||
_BOTTOM_TEXT_SIZE = icon.Margin;
|
||||
text = text.ToUpper();
|
||||
break;
|
||||
}
|
||||
case EIconDesign.Flat:
|
||||
{
|
||||
side = ETextSide.Right;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SKPaint descriptionPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = TypeFaces.DescriptionTypeface,
|
||||
TextSize = 13,
|
||||
Color = SKColors.White,
|
||||
};
|
||||
|
||||
// wrap if too long
|
||||
Helper.DrawCenteredMultilineText(c, text, maxLine, icon, side,
|
||||
new SKRect(icon.Margin, _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE, icon.Size - icon.Margin, icon.Size - _BOTTOM_TEXT_SIZE),
|
||||
descriptionPaint);
|
||||
}
|
||||
|
||||
public static void DrawToBottom(SKCanvas c, BaseIcon icon, ETextSide side, string text)
|
||||
{
|
||||
SKPaint shortDescriptionPaint = new SKPaint
|
||||
{
|
||||
IsAntialias = true,
|
||||
FilterQuality = SKFilterQuality.High,
|
||||
Typeface = side == ETextSide.Left ? TypeFaces.DisplayNameTypeface : TypeFaces.DefaultTypeface,
|
||||
TextSize = 15,
|
||||
Color = SKColors.White,
|
||||
TextAlign = side == ETextSide.Left ? SKTextAlign.Left : SKTextAlign.Right,
|
||||
};
|
||||
|
||||
c.DrawText(text, side == ETextSide.Left ? icon.Margin * 2.5f : icon.Size - (icon.Margin * 2.5f), icon.Size - (icon.Margin * 2.5f), shortDescriptionPaint);
|
||||
}
|
||||
}
|
||||
}
|
||||
152
FModel/Creator/Texts/Typefaces.cs
Normal file
152
FModel/Creator/Texts/Typefaces.cs
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
using FModel.Utils;
|
||||
using FModel.ViewModels.DataGrid;
|
||||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Windows;
|
||||
|
||||
namespace FModel.Creator.Texts
|
||||
{
|
||||
public class Typefaces
|
||||
{
|
||||
#pragma warning disable IDE0051
|
||||
private const string _FORTNITE_BASE_PATH = "/Game/UI/Foundation/Fonts/";
|
||||
private const string _ASIA_ERINM = "AsiaERINM"; // korean fortnite
|
||||
private const string _BURBANK_BIG_CONDENSED_BLACK = "BurbankBigCondensed-Black"; // russian
|
||||
private readonly Uri _BURBANK_BIG_CONDENSED_BOLD = new Uri("pack://application:,,,/Resources/BurbankBigCondensed-Bold.ttf"); // other languages fortnite unofficial
|
||||
private const string _BURBANK_BIG_REGULAR_BLACK = "BurbankBigRegular-Black";
|
||||
private const string _BURBANK_BIG_REGULAR_BOLD = "BurbankBigRegular-Bold"; // official fortnite ig
|
||||
private const string _BURBANK_SMALL_MEDIUM = "BurbankSmall-Medium";
|
||||
private const string _DROID_SANS_FORTNITE_SUBSET = "DroidSans-Fortnite-Subset";
|
||||
private const string _NIS_JYAU = "NIS_JYAU"; // japanese fortnite
|
||||
private const string _NOTO_COLOR_EMOJI = "NotoColorEmoji";
|
||||
private const string _NOTO_SANS_BOLD = "NotoSans-Bold";
|
||||
private const string _NOTO_SANS_FORTNITE_BOLD = "NotoSans-Fortnite-Bold";
|
||||
private const string _NOTO_SANS_FORTNITE_ITALIC = "NotoSans-Fortnite-Italic";
|
||||
private const string _NOTO_SANS_FORTNITE_REGULAR = "NotoSans-Fortnite-Regular";
|
||||
private const string _NOTO_SANS_ITALIC = "NotoSans-Italic";
|
||||
private const string _NOTO_SANS_REGULAR = "NotoSans-Regular";
|
||||
private const string _NOTO_SANS_ARABIC_BLACK = "NotoSansArabic-Black"; // arabic fortnite
|
||||
private const string _NOTO_SANS_ARABIC_BOLD = "NotoSansArabic-Bold";
|
||||
private const string _NOTO_SANS_ARABIC_REGULAR = "NotoSansArabic-Regular";
|
||||
private const string _NOTO_SANS_JP_BOLD = "NotoSansJP-Bold";
|
||||
private const string _NOTO_SANS_KR_REGULAR = "NotoSansKR-Regular";
|
||||
private const string _NOTO_SANS_SC_BLACK = "NotoSansSC-Black"; // simplified chinese fortnite
|
||||
private const string _NOTO_SANS_SC_REGULAR = "NotoSansSC-Regular";
|
||||
private const string _NOTO_SANS_TC_BLACK = "NotoSansTC-Black"; // traditional chinese fortnite
|
||||
private const string _NOTO_SANS_TC_REGULAR = "NotoSansTC-Regular";
|
||||
private const string _BURBANK_SMALL_BLACK = "burbanksmall-black";
|
||||
private const string _BURBANK_SMALL_BOLD = "burbanksmall-bold";
|
||||
|
||||
private const string _VALORANT_BASE_PATH = "/Game/";
|
||||
private const string _TUNGSTEN_BOLD = "Tungsten-Bold";
|
||||
private const string _DINNEXT_LTARABIC_HEAVY = "UI/Fonts/FinalFonts/LOCFonts/DIN_Next_Arabic/DINNextLTArabic-Heavy";
|
||||
private const string _TUNGSTEN_CYRILLIC = "UI/Fonts/FinalFonts/LOCFonts/LOC_Tungsten/Cyrillic_Tungsten";
|
||||
private const string _TUNGSTEN_JAPANESE = "UI/Fonts/FinalFonts/LOCFonts/LOC_Tungsten/JP_Tungsten";
|
||||
private const string _TUNGSTEN_KOREAN = "UI/Fonts/FinalFonts/LOCFonts/LOC_Tungsten/KR_Tungsten";
|
||||
private const string _TUNGSTEN_SIMPLIFIED_CHINESE = "UI/Fonts/FinalFonts/LOCFonts/LOC_Tungsten/zh-CN_Tungsten";
|
||||
private const string _TUNGSTEN_TRADITIONAL_CHINESE = "UI/Fonts/FinalFonts/LOCFonts/LOC_Tungsten/zh-TW_Tungsten";
|
||||
private const string _DINNEXT_W1G_LIGHT = "UI/Fonts/FinalFonts/DINNextW1G-Light";
|
||||
private const string _DINNEXT_LTARABIC_LIGHT = "UI/Fonts/FinalFonts/LOCFonts/DIN_Next_Arabic/DINNextLTArabic-Light";
|
||||
private const string _NOTOSANS_CJK_LIGHT = "UI/Fonts/FinalFonts/LOCFonts/CJK/NotoSansCJK-Light"; // chinese, japanese, korean
|
||||
private const string _DINNEXT_W1G_BOLD = "UI/Fonts/FinalFonts/DINNextW1G-Bold";
|
||||
#pragma warning restore IDE0051
|
||||
|
||||
public SKTypeface DefaultTypeface; // used as default font for all untranslated strings (item source, ...)
|
||||
public SKTypeface BundleDefaultTypeface; // used for the last folder string
|
||||
public SKTypeface DisplayNameTypeface;
|
||||
public SKTypeface DescriptionTypeface;
|
||||
public SKTypeface BundleDisplayNameTypeface;
|
||||
|
||||
public Typefaces()
|
||||
{
|
||||
DefaultTypeface = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD).Stream);
|
||||
if (Globals.Game.ActualGame == EGame.Fortnite)
|
||||
{
|
||||
ArraySegment<byte>[] t = Utils.GetPropertyArraySegmentByte(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else BundleDefaultTypeface = DefaultTypeface;
|
||||
|
||||
string namePath = _FORTNITE_BASE_PATH + (
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? _ASIA_ERINM :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Russian ? _BURBANK_BIG_CONDENSED_BLACK :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Japanese ? _NIS_JYAU :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Arabic ? _NOTO_SANS_ARABIC_BLACK :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.TraditionalChinese ? _NOTO_SANS_TC_BLACK :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTO_SANS_SC_BLACK :
|
||||
string.Empty);
|
||||
if (!namePath.Equals(_FORTNITE_BASE_PATH))
|
||||
{
|
||||
t = Utils.GetPropertyArraySegmentByte(namePath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
else DisplayNameTypeface = DefaultTypeface;
|
||||
|
||||
string descriptionPath = _FORTNITE_BASE_PATH + (
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? _NOTO_SANS_KR_REGULAR :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Japanese ? _NOTO_SANS_JP_BOLD :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Arabic ? _NOTO_SANS_ARABIC_REGULAR :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.TraditionalChinese ? _NOTO_SANS_TC_REGULAR :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTO_SANS_SC_REGULAR :
|
||||
_NOTO_SANS_REGULAR);
|
||||
t = Utils.GetPropertyArraySegmentByte(descriptionPath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else DescriptionTypeface = DefaultTypeface;
|
||||
|
||||
string bundleNamePath = _FORTNITE_BASE_PATH + (
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? _ASIA_ERINM :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Russian ? _BURBANK_BIG_CONDENSED_BLACK :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Japanese ? _NIS_JYAU :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Arabic ? _NOTO_SANS_ARABIC_BLACK :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.TraditionalChinese ? _NOTO_SANS_TC_BLACK :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTO_SANS_SC_BLACK :
|
||||
string.Empty);
|
||||
if (!bundleNamePath.Equals(_FORTNITE_BASE_PATH))
|
||||
{
|
||||
t = Utils.GetPropertyArraySegmentByte(bundleNamePath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
}
|
||||
else BundleDisplayNameTypeface = BundleDefaultTypeface;
|
||||
}
|
||||
else if (Globals.Game.ActualGame == EGame.Valorant)
|
||||
{
|
||||
string namePath = _VALORANT_BASE_PATH + (
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? _TUNGSTEN_KOREAN :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Russian ? _TUNGSTEN_CYRILLIC :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Japanese ? _TUNGSTEN_JAPANESE :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Arabic ? _DINNEXT_LTARABIC_HEAVY :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.TraditionalChinese ? _TUNGSTEN_TRADITIONAL_CHINESE :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _TUNGSTEN_SIMPLIFIED_CHINESE :
|
||||
_TUNGSTEN_BOLD);
|
||||
ArraySegment<byte>[] t = Utils.GetPropertyArraySegmentByte(namePath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DisplayNameTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else DisplayNameTypeface = DefaultTypeface;
|
||||
|
||||
string descriptionPath = _VALORANT_BASE_PATH + (
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Korean ? _NOTOSANS_CJK_LIGHT :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Japanese ? _NOTOSANS_CJK_LIGHT :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Arabic ? _DINNEXT_LTARABIC_LIGHT :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.TraditionalChinese ? _NOTOSANS_CJK_LIGHT :
|
||||
Properties.Settings.Default.AssetsLanguage == (long)ELanguage.Chinese ? _NOTOSANS_CJK_LIGHT :
|
||||
_DINNEXT_W1G_LIGHT);
|
||||
t = Utils.GetPropertyArraySegmentByte(descriptionPath);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
DescriptionTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else DescriptionTypeface = DefaultTypeface;
|
||||
|
||||
t = Utils.GetPropertyArraySegmentByte(_VALORANT_BASE_PATH + _DINNEXT_W1G_BOLD);
|
||||
if (t != null && t.Length == 3 && t[2].Array != null)
|
||||
BundleDefaultTypeface = SKTypeface.FromStream(t[2].AsStream());
|
||||
else BundleDefaultTypeface = DefaultTypeface;
|
||||
}
|
||||
}
|
||||
|
||||
public bool NeedReload(bool forceReload) => forceReload ?
|
||||
DataGridVm.dataGridViewModel.Count > 0 : //reload only if at least one pak is loaded
|
||||
DataGridVm.dataGridViewModel.Count > 0 && (BundleDefaultTypeface == DefaultTypeface && DisplayNameTypeface == DefaultTypeface && DescriptionTypeface == DefaultTypeface && BundleDisplayNameTypeface == BundleDefaultTypeface);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,199 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace FModel.Creator;
|
||||
|
||||
public class Typefaces
|
||||
{
|
||||
private readonly Uri _BURBANK_BIG_CONDENSED_BOLD = new("pack://application:,,,/Resources/BurbankBigCondensed-Bold.ttf");
|
||||
private const string _EXT = ".ufont";
|
||||
|
||||
// FortniteGame
|
||||
private const string _FORTNITE_BASE_PATH = "/Game/UI/Foundation/Fonts/";
|
||||
private const string _ASIA_ERINM = "AsiaERINM"; // korean fortnite
|
||||
private const string _BURBANK_BIG_CONDENSED_BLACK = "BurbankBigCondensed-Black"; // russian
|
||||
private const string _BURBANK_BIG_REGULAR_BLACK = "BurbankBigRegular-Black";
|
||||
private const string _BURBANK_BIG_REGULAR_BOLD = "BurbankBigRegular-Bold"; // official fortnite ig
|
||||
private const string _BURBANK_SMALL_MEDIUM = "BurbankSmall-Medium";
|
||||
private const string _DROID_SANS_FORTNITE_SUBSET = "DroidSans-Fortnite-Subset";
|
||||
private const string _NOTO_COLOR_EMOJI = "NotoColorEmoji";
|
||||
private const string _NOTO_SANS_BOLD = "NotoSans-Bold";
|
||||
private const string _NOTO_SANS_FORTNITE_BOLD = "NotoSans-Fortnite-Bold";
|
||||
private const string _NOTO_SANS_FORTNITE_ITALIC = "NotoSans-Fortnite-Italic";
|
||||
private const string _NOTO_SANS_FORTNITE_REGULAR = "NotoSans-Fortnite-Regular";
|
||||
private const string _NOTO_SANS_ITALIC = "NotoSans-Italic";
|
||||
private const string _NOTO_SANS_REGULAR = "NotoSans-Regular";
|
||||
private const string _NOTO_SANS_ARABIC_BLACK = "NotoSansArabic-Black"; // arabic fortnite
|
||||
private const string _NOTO_SANS_ARABIC_BOLD = "NotoSansArabic-Bold";
|
||||
private const string _NOTO_SANS_ARABIC_REGULAR = "NotoSansArabic-Regular";
|
||||
private const string _NOTO_SANS_JP_BOLD = "NotoSansJP-Bold"; // japanese fortnite
|
||||
private const string _NOTO_SANS_KR_REGULAR = "NotoSansKR-Regular";
|
||||
private const string _NOTO_SANS_SC_BLACK = "NotoSansSC-Black"; // simplified chinese fortnite
|
||||
private const string _NOTO_SANS_SC_REGULAR = "NotoSansSC-Regular";
|
||||
private const string _NOTO_SANS_TC_BLACK = "NotoSansTC-Black"; // traditional chinese fortnite
|
||||
private const string _NOTO_SANS_TC_REGULAR = "NotoSansTC-Regular";
|
||||
private const string _BURBANK_SMALL_BLACK = "burbanksmall-black";
|
||||
private const string _BURBANK_SMALL_BOLD = "burbanksmall-bold";
|
||||
|
||||
// PandaGame
|
||||
private const string _PANDAGAME_BASE_PATH = "/Game/Panda_Main/UI/Fonts/";
|
||||
private const string _NORMS_STD_CONDENSED_EXTRABOLD_ITALIC = "Norms/TT_Norms_Std_Condensed_ExtraBold_Italic";
|
||||
private const string _NORMS_PRO_EXTRABOLD_ITALIC = "Norms/TT_Norms_Pro_ExtraBold_Italic";
|
||||
private const string _NORMS_STD_CONDENSED_MEDIUM = "Norms/TT_Norms_Std_Condensed_Medium";
|
||||
private const string _XIANGHEHEI_SC_PRO_BLACK = "XiangHeHei_SC/MXiangHeHeiSCPro-Black";
|
||||
private const string _XIANGHEHEI_SC_PRO_HEAVY = "XiangHeHei_SC/MXiangHeHeiSCPro-Heavy";
|
||||
|
||||
private readonly CUE4ParseViewModel _viewModel;
|
||||
|
||||
public readonly SKTypeface Default; // used as a fallback font for all untranslated strings (item source, ...)
|
||||
public readonly SKTypeface DisplayName;
|
||||
public readonly SKTypeface Description;
|
||||
public readonly SKTypeface Bottom; // must be null for non-latin base languages
|
||||
public readonly SKTypeface Bundle;
|
||||
public readonly SKTypeface BundleNumber;
|
||||
public readonly SKTypeface TandemDisplayName;
|
||||
public readonly SKTypeface TandemGenDescription;
|
||||
public readonly SKTypeface TandemAddDescription;
|
||||
|
||||
public Typefaces(CUE4ParseViewModel viewModel)
|
||||
{
|
||||
_viewModel = viewModel;
|
||||
var language = UserSettings.Default.AssetLanguage;
|
||||
|
||||
Default = SKTypeface.FromStream(Application.GetResourceStream(_BURBANK_BIG_CONDENSED_BOLD)?.Stream);
|
||||
|
||||
switch (viewModel.Provider.InternalGameName.ToUpperInvariant())
|
||||
{
|
||||
case "FORTNITEGAME":
|
||||
{
|
||||
DisplayName = OnTheFly(_FORTNITE_BASE_PATH +
|
||||
language switch
|
||||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
_ => string.Empty
|
||||
} + _EXT);
|
||||
|
||||
Description = OnTheFly(_FORTNITE_BASE_PATH +
|
||||
language switch
|
||||
{
|
||||
ELanguage.Korean => _NOTO_SANS_KR_REGULAR,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_REGULAR,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_REGULAR,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_REGULAR,
|
||||
_ => _NOTO_SANS_REGULAR
|
||||
} + _EXT);
|
||||
|
||||
Bottom = OnTheFly(_FORTNITE_BASE_PATH +
|
||||
language switch
|
||||
{
|
||||
ELanguage.Korean => string.Empty,
|
||||
ELanguage.Japanese => string.Empty,
|
||||
ELanguage.Arabic => string.Empty,
|
||||
ELanguage.TraditionalChinese => string.Empty,
|
||||
ELanguage.Chinese => string.Empty,
|
||||
_ => _BURBANK_SMALL_BOLD
|
||||
} + _EXT, true);
|
||||
|
||||
BundleNumber = OnTheFly(_FORTNITE_BASE_PATH + _BURBANK_BIG_CONDENSED_BLACK + _EXT);
|
||||
|
||||
Bundle = OnTheFly(_FORTNITE_BASE_PATH +
|
||||
language switch
|
||||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
_ => string.Empty
|
||||
} + _EXT, true) ?? BundleNumber;
|
||||
|
||||
TandemDisplayName = OnTheFly(_FORTNITE_BASE_PATH +
|
||||
language switch
|
||||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Russian => _BURBANK_BIG_CONDENSED_BLACK,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
_ => _BURBANK_BIG_REGULAR_BLACK
|
||||
} + _EXT);
|
||||
|
||||
TandemGenDescription = OnTheFly(_FORTNITE_BASE_PATH +
|
||||
language switch
|
||||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
_ => _BURBANK_SMALL_BLACK
|
||||
} + _EXT);
|
||||
|
||||
TandemAddDescription = OnTheFly(_FORTNITE_BASE_PATH +
|
||||
language switch
|
||||
{
|
||||
ELanguage.Korean => _ASIA_ERINM,
|
||||
ELanguage.Japanese => _NOTO_SANS_JP_BOLD,
|
||||
ELanguage.Arabic => _NOTO_SANS_ARABIC_BLACK,
|
||||
ELanguage.TraditionalChinese => _NOTO_SANS_TC_BLACK,
|
||||
ELanguage.Chinese => _NOTO_SANS_SC_BLACK,
|
||||
_ => _BURBANK_SMALL_BOLD
|
||||
} + _EXT);
|
||||
break;
|
||||
}
|
||||
case "MULTIVERSUS":
|
||||
{
|
||||
DisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch
|
||||
{
|
||||
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY,
|
||||
_ => _NORMS_PRO_EXTRABOLD_ITALIC
|
||||
} + _EXT);
|
||||
|
||||
Description = OnTheFly(_PANDAGAME_BASE_PATH + language switch
|
||||
{
|
||||
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK,
|
||||
_ => _NORMS_STD_CONDENSED_MEDIUM
|
||||
} + _EXT);
|
||||
|
||||
TandemDisplayName = OnTheFly(_PANDAGAME_BASE_PATH + language switch
|
||||
{
|
||||
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_BLACK,
|
||||
_ => _NORMS_STD_CONDENSED_EXTRABOLD_ITALIC
|
||||
} + _EXT);
|
||||
|
||||
TandemGenDescription = OnTheFly(_PANDAGAME_BASE_PATH + language switch
|
||||
{
|
||||
ELanguage.Chinese => _XIANGHEHEI_SC_PRO_HEAVY,
|
||||
_ => _NORMS_STD_CONDENSED_MEDIUM
|
||||
} + _EXT);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
DisplayName = Default;
|
||||
Description = Default;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SKTypeface OnTheFly(string path, bool fallback = false)
|
||||
{
|
||||
if (!_viewModel.Provider.TrySaveAsset(path, out var data)) return fallback ? null : Default;
|
||||
var m = new MemoryStream(data) { Position = 0 };
|
||||
return SKTypeface.FromStream(m);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,420 +1,82 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using CUE4Parse.UE4.Assets.Exports;
|
||||
using CUE4Parse.UE4.Assets.Exports.Material;
|
||||
using CUE4Parse.UE4.Assets.Exports.Texture;
|
||||
using CUE4Parse.UE4.Objects.UObject;
|
||||
using CUE4Parse.UE4.Versions;
|
||||
using CUE4Parse_Conversion.Textures;
|
||||
using CUE4Parse.UE4.Assets.Objects;
|
||||
using FModel.Framework;
|
||||
using FModel.Extensions;
|
||||
using FModel.Services;
|
||||
using FModel.Settings;
|
||||
using FModel.ViewModels;
|
||||
using FModel.Utils;
|
||||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Class;
|
||||
using PakReader.Parsers.PropertyTagData;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
using System;
|
||||
|
||||
namespace FModel.Creator;
|
||||
|
||||
public static class Utils
|
||||
namespace FModel.Creator
|
||||
{
|
||||
private static ApplicationViewModel _applicationView => ApplicationService.ApplicationView;
|
||||
private static readonly Regex _htmlRegex = new("<.*?>");
|
||||
public static Typefaces Typefaces;
|
||||
|
||||
public static string RemoveHtmlTags(string s)
|
||||
static class Utils
|
||||
{
|
||||
var match = _htmlRegex.Match(s);
|
||||
while (match.Success)
|
||||
public static string GetFullPath(string partialPath)
|
||||
{
|
||||
s = s.Replace(match.Value, string.Empty);
|
||||
match = match.NextMatch();
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public static bool TryGetDisplayAsset(UObject uObject, out SKBitmap preview)
|
||||
{
|
||||
if (uObject.TryGetValue(out FSoftObjectPath sidePanelIcon, "SidePanelIcon"))
|
||||
{
|
||||
preview = GetBitmap(sidePanelIcon);
|
||||
return preview != null;
|
||||
}
|
||||
|
||||
var path = $"/Game/Catalog/MI_OfferImages/MI_{uObject.Name.Replace("Athena_Commando_", string.Empty)}";
|
||||
if (!TryLoadObject(path, out UMaterialInstanceConstant material)) // non-obfuscated item definition
|
||||
{
|
||||
if (!TryLoadObject($"{path[..path.LastIndexOf('_')]}_{path.SubstringAfterLast('_').ToLower()}", out material)) // Try to get MI with lowercase obfuscation
|
||||
TryLoadObject(path[..path.LastIndexOf('_')], out material); // hopefully gets obfuscated item definition
|
||||
}
|
||||
|
||||
preview = GetBitmap(material);
|
||||
return preview != null;
|
||||
}
|
||||
|
||||
public static SKBitmap GetBitmap(FPackageIndex packageIndex)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!TryGetPackageIndexExport(packageIndex, out UObject export)) return null;
|
||||
switch (export)
|
||||
{
|
||||
case UTexture2D texture:
|
||||
return GetBitmap(texture);
|
||||
case UMaterialInstanceConstant material:
|
||||
return GetBitmap(material);
|
||||
default:
|
||||
foreach (var fileReader in Globals.CachedPakFiles.Values)
|
||||
if (fileReader.TryGetPartialKey(partialPath, out var fullPath))
|
||||
{
|
||||
if (export.TryGetValue(out FInstancedStruct[] dataList, "DataList")) return GetBitmap(dataList);
|
||||
if (export.TryGetValue(out FSoftObjectPath previewImage, "LargePreviewImage", "SmallPreviewImage")) return GetBitmap(previewImage);
|
||||
if (export.TryGetValue(out string largePreview, "LargePreviewImage")) return GetBitmap(largePreview);
|
||||
if (export.TryGetValue(out FPackageIndex smallPreview, "SmallPreviewImage"))
|
||||
{
|
||||
packageIndex = smallPreview;
|
||||
continue;
|
||||
}
|
||||
|
||||
return null;
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static SKBitmap GetBitmap(FInstancedStruct[] structs)
|
||||
{
|
||||
if (structs.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FSoftObjectPath p, "LargeIcon") == true && !p.AssetPathName.IsNone) is { NonConstStruct: not null } isl)
|
||||
{
|
||||
return GetBitmap(isl.NonConstStruct.Get<FSoftObjectPath>("LargeIcon"));
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
if (structs.FirstOrDefault(d => d.NonConstStruct?.TryGetValue(out FSoftObjectPath p, "Icon") == true && !p.AssetPathName.IsNone) is { NonConstStruct: not null } isi)
|
||||
public static PakPackage GetPropertyPakPackage(string value)
|
||||
{
|
||||
return GetBitmap(isi.NonConstStruct.Get<FSoftObjectPath>("Icon"));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static SKBitmap GetBitmap(UMaterialInstanceConstant material)
|
||||
{
|
||||
if (material == null) return null;
|
||||
foreach (var textureParameter in material.TextureParameterValues)
|
||||
{
|
||||
if (!textureParameter.ParameterValue.TryLoad<UTexture2D>(out var texture)) continue;
|
||||
switch (textureParameter.ParameterInfo.Name.Text)
|
||||
{
|
||||
case "MainTex":
|
||||
case "Texture":
|
||||
case "TextureA":
|
||||
case "TextureB":
|
||||
case "OfferImage":
|
||||
case "KeyArtTexture":
|
||||
case "NPC-Portrait":
|
||||
string path = Strings.FixPath(value);
|
||||
foreach (var fileReader in Globals.CachedPakFiles.Values)
|
||||
if (fileReader.TryGetValue(path, out var entry))
|
||||
{
|
||||
return GetBitmap(texture);
|
||||
// kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf(".")).Length);
|
||||
return Assets.GetPakPackage(entry, mount);
|
||||
}
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static SKBitmap GetB64Bitmap(string b64) => SKBitmap.Decode(new MemoryStream(Convert.FromBase64String(b64)) { Position = 0 });
|
||||
public static SKBitmap GetBitmap(FSoftObjectPath softObjectPath) => GetBitmap(softObjectPath.AssetPathName.Text);
|
||||
public static SKBitmap GetBitmap(string fullPath) => TryLoadObject(fullPath, out UTexture2D texture) ? GetBitmap(texture) : null;
|
||||
public static SKBitmap GetBitmap(UTexture2D texture) => texture.Decode(UserSettings.Default.CurrentDir.TexturePlatform);
|
||||
public static SKBitmap GetBitmap(byte[] data) => SKBitmap.Decode(data);
|
||||
|
||||
public static SKBitmap ResizeWithRatio(this SKBitmap me, double width, double height)
|
||||
{
|
||||
var ratioX = width / me.Width;
|
||||
var ratioY = height / me.Height;
|
||||
return ResizeWithRatio(me, ratioX < ratioY ? ratioX : ratioY);
|
||||
}
|
||||
public static SKBitmap ResizeWithRatio(this SKBitmap me, double ratio)
|
||||
{
|
||||
return me.Resize(Convert.ToInt32(me.Width * ratio), Convert.ToInt32(me.Height * ratio));
|
||||
}
|
||||
|
||||
public static SKBitmap Resize(this SKBitmap me, int size) => me.Resize(size, size);
|
||||
public static SKBitmap Resize(this SKBitmap me, int width, int height)
|
||||
{
|
||||
var bmp = new SKBitmap(new SKImageInfo(width, height), SKBitmapAllocFlags.ZeroPixels);
|
||||
using var pixmap = bmp.PeekPixels();
|
||||
me.ScalePixels(pixmap, SKFilterQuality.Medium);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
public static bool TryGetPackageIndexExport<T>(FPackageIndex packageIndex, out T export) where T : UObject
|
||||
{
|
||||
return packageIndex.TryLoad(out export);
|
||||
}
|
||||
|
||||
// fullpath must be either without any extension or with the export objectname
|
||||
public static bool TryLoadObject<T>(string fullPath, out T export) where T : UObject
|
||||
{
|
||||
return _applicationView.CUE4Parse.Provider.TryLoadObject(fullPath, out export);
|
||||
}
|
||||
|
||||
public static IEnumerable<UObject> LoadExports(string packagePath)
|
||||
{
|
||||
return _applicationView.CUE4Parse.Provider.LoadAllObjects(packagePath);
|
||||
}
|
||||
|
||||
public static float GetMaxFontSize(double sectorSize, SKTypeface typeface, string text, float degreeOfCertainty = 1f, float maxFont = 100f)
|
||||
{
|
||||
var max = maxFont;
|
||||
var min = 0f;
|
||||
var last = -1f;
|
||||
float value;
|
||||
while (true)
|
||||
public static ArraySegment<byte>[] GetPropertyArraySegmentByte(string value)
|
||||
{
|
||||
value = min + ((max - min) / 2);
|
||||
using (SKFont ft = new SKFont(typeface, value))
|
||||
using (SKPaint paint = new SKPaint(ft))
|
||||
{
|
||||
if (paint.MeasureText(text) > sectorSize)
|
||||
string path = Strings.FixPath(value);
|
||||
foreach (var fileReader in Globals.CachedPakFiles.Values)
|
||||
if (fileReader.TryGetValue(path, out var entry))
|
||||
{
|
||||
last = value;
|
||||
max = value;
|
||||
// kinda sad to use Globals.CachedPakFileMountPoint when the mount point is already in the path ¯\_(ツ)_/¯
|
||||
string mount = path.Substring(0, path.Length - entry.Name.Substring(0, entry.Name.LastIndexOf(".")).Length);
|
||||
return Assets.GetArraySegmentByte(entry, mount);
|
||||
}
|
||||
else
|
||||
{
|
||||
min = value;
|
||||
if (Math.Abs(last - value) <= degreeOfCertainty)
|
||||
return last;
|
||||
|
||||
last = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetLocalizedResource(string @namespace, string key, string defaultValue)
|
||||
{
|
||||
return _applicationView.CUE4Parse.Provider.GetLocalizedString(@namespace, key, defaultValue);
|
||||
}
|
||||
public static string GetLocalizedResource<T>(T @enum) where T : Enum
|
||||
{
|
||||
var resource = _applicationView.CUE4Parse.Provider.GetLocalizedString("", @enum.GetDescription(), @enum.ToString());
|
||||
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(resource.ToLower());
|
||||
}
|
||||
|
||||
public static string GetFullPath(string partialPath)
|
||||
{
|
||||
var regex = new Regex(partialPath, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
foreach (var path in _applicationView.CUE4Parse.Provider.Files.Keys)
|
||||
{
|
||||
if (regex.IsMatch(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static void DrawCenteredMultilineText(SKCanvas c, string text, int maxCount, int size, int margin, SKTextAlign side, SKRect area, SKPaint paint)
|
||||
{
|
||||
var lineHeight = paint.TextSize * 1.2f;
|
||||
var lines = SplitLines(text, paint, area.Width - margin);
|
||||
|
||||
#if DEBUG
|
||||
c.DrawRect(new SKRect(area.Left, area.Top, area.Right, area.Bottom), new SKPaint { Color = SKColors.Red, IsStroke = true });
|
||||
#endif
|
||||
|
||||
if (lines == null) return;
|
||||
if (lines.Count <= maxCount) maxCount = lines.Count;
|
||||
var height = maxCount * lineHeight;
|
||||
var y = area.MidY - height / 2;
|
||||
|
||||
var shaper = new CustomSKShaper(paint.Typeface);
|
||||
for (var i = 0; i < maxCount; i++)
|
||||
public static SKBitmap NewZeroedBitmap(int width, int height) => new SKBitmap(new SKImageInfo(width, height), SKBitmapAllocFlags.ZeroPixels);
|
||||
public static SKBitmap Resize(this SKBitmap me, int width, int height)
|
||||
{
|
||||
var line = lines[i];
|
||||
if (line == null) continue;
|
||||
|
||||
var lineText = line.Trim();
|
||||
var shapedText = shaper.Shape(lineText, paint);
|
||||
|
||||
y += lineHeight;
|
||||
var x = side switch
|
||||
{
|
||||
SKTextAlign.Center => area.MidX - shapedText.Width / 2,
|
||||
SKTextAlign.Right => size - margin - shapedText.Width,
|
||||
SKTextAlign.Left => margin,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
c.DrawShapedText(shaper, lineText, x, y, paint);
|
||||
}
|
||||
}
|
||||
|
||||
public static void DrawMultilineText(SKCanvas c, string text, int size, int margin, SKTextAlign side, SKRect area, SKPaint paint, out float yPos)
|
||||
{
|
||||
yPos = area.Top;
|
||||
var lineHeight = paint.TextSize * 1.2f;
|
||||
var lines = SplitLines(text, paint, area.Width);
|
||||
if (lines == null) return;
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var fontSize = GetMaxFontSize(area.Width, paint.Typeface, line);
|
||||
if (paint.TextSize > fontSize) // if the text is not fitting in the line decrease the font size (CKJ languages)
|
||||
{
|
||||
paint.TextSize = fontSize;
|
||||
lineHeight = paint.TextSize * 1.2f;
|
||||
}
|
||||
|
||||
if (line == null) continue;
|
||||
var lineText = line.Trim();
|
||||
var shaper = new CustomSKShaper(paint.Typeface);
|
||||
var shapedText = shaper.Shape(lineText, paint);
|
||||
|
||||
var x = side switch
|
||||
{
|
||||
SKTextAlign.Center => area.MidX - shapedText.Width / 2,
|
||||
SKTextAlign.Right => size - margin - shapedText.Width,
|
||||
SKTextAlign.Left => area.Left,
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
c.DrawShapedText(shaper, lineText, x, yPos, paint);
|
||||
yPos += lineHeight;
|
||||
var bmp = NewZeroedBitmap(width, height);
|
||||
using var pixmap = bmp.PeekPixels();
|
||||
me.ScalePixels(pixmap, SKFilterQuality.Medium);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
c.DrawRect(new SKRect(area.Left, area.Top - paint.TextSize, area.Right, yPos), new SKPaint { Color = SKColors.Red, IsStroke = true });
|
||||
#endif
|
||||
}
|
||||
|
||||
#region Chinese, Korean and Japanese text split
|
||||
|
||||
// https://github.com/YoungjaeKim/mikan.sharp/blob/master/MikanSharp/Mikan/Mikan.cs
|
||||
|
||||
static string joshi = @"(でなければ|について|かしら|くらい|けれど|なのか|ばかり|ながら|ことよ|こそ|こと|さえ|しか|した|たり|だけ|だに|だの|つつ|ても|てよ|でも|とも|から|など|なり|ので|のに|ほど|まで|もの|やら|より|って|で|と|な|に|ね|の|も|は|ば|へ|や|わ|を|か|が|さ|し|ぞ|て)";
|
||||
static string keywords = @"(\ |[a-zA-Z0-9]+\.[a-z]{2,}|[一-龠々〆ヵヶゝ]+|[ぁ-んゝ]+|[ァ-ヴー]+|[a-zA-Z0-9]+|[a-zA-Z0-9]+)";
|
||||
static string periods = @"([\.\,。、!\!?\?]+)$";
|
||||
static string bracketsBegin = @"([〈《「『「((\[【〔〚〖〘❮❬❪❨(<{❲❰{❴])";
|
||||
static string bracketsEnd = @"([〉》」』」))\]】〕〗〙〛}>\)❩❫❭❯❱❳❵}])";
|
||||
|
||||
public static string[] SplitCKJText(string str)
|
||||
{
|
||||
var line1 = Regex.Split(str, keywords).ToList();
|
||||
var line2 = line1.SelectMany((o, _) => Regex.Split(o, joshi)).ToList();
|
||||
var line3 = line2.SelectMany((o, _) => Regex.Split(o, bracketsBegin)).ToList();
|
||||
var line4 = line3.SelectMany((o, _) => Regex.Split(o, bracketsEnd)).ToList();
|
||||
var words = line4.Where(o => !string.IsNullOrEmpty(o)).ToList();
|
||||
|
||||
var prevType = string.Empty;
|
||||
var prevWord = string.Empty;
|
||||
List<string> result = new List<string>();
|
||||
|
||||
words.ForEach(word =>
|
||||
public static SKBitmap GetObjectTexture(ObjectProperty o) => GetTexture(o.Value.Resource.OuterIndex.Resource.ObjectName.String);
|
||||
public static SKBitmap GetSoftObjectTexture(SoftObjectProperty s) => GetTexture(s.Value.AssetPathName.String);
|
||||
public static SKBitmap GetTexture(string s)
|
||||
{
|
||||
var token = Regex.IsMatch(word, periods) || Regex.IsMatch(word, joshi);
|
||||
// FortniteGame/Content/Catalog/DisplayAssets/DA_BattlePassBundle_2020.uasset
|
||||
if (s != null && s.Equals("/Game/UI/Foundation/Textures/BattleRoyale/FeaturedItems/Outfit/T_UI_InspectScreen_annualPass"))
|
||||
s += "_1024";
|
||||
|
||||
if (Regex.IsMatch(word, bracketsBegin))
|
||||
PakPackage p = GetPropertyPakPackage(s);
|
||||
if (p.HasExport() && !p.Equals(default))
|
||||
{
|
||||
prevType = "braketBegin";
|
||||
prevWord = word;
|
||||
return;
|
||||
var i = p.GetExport<UTexture2D>();
|
||||
if (i != null)
|
||||
return SKBitmap.Decode(i.Image.Encode());
|
||||
|
||||
var u = p.GetExport<UObject>();
|
||||
if (u != null)
|
||||
if (u.TryGetValue("TextureParameterValues", out var v) && v is ArrayProperty a)
|
||||
if (a.Value.Length > 0 && a.Value[0] is StructProperty str && str.Value is UObject o)
|
||||
if (o.TryGetValue("ParameterValue", out var obj) && obj is ObjectProperty parameterValue)
|
||||
return GetObjectTexture(parameterValue);
|
||||
}
|
||||
|
||||
if (Regex.IsMatch(word, bracketsEnd))
|
||||
{
|
||||
result[result.Count - 1] += word;
|
||||
prevType = "braketEnd";
|
||||
prevWord = word;
|
||||
return;
|
||||
}
|
||||
|
||||
if (prevType == "braketBegin")
|
||||
{
|
||||
word = prevWord + word;
|
||||
prevWord = string.Empty;
|
||||
prevType = string.Empty;
|
||||
}
|
||||
|
||||
// すでに文字が入っている上で助詞が続く場合は結合する
|
||||
if (result.Count > 0 && token && prevType == string.Empty)
|
||||
{
|
||||
result[result.Count - 1] += word;
|
||||
prevType = "keyword";
|
||||
prevWord = word;
|
||||
return;
|
||||
}
|
||||
|
||||
// 単語のあとの文字がひらがななら結合する
|
||||
if (result.Count > 1 && token || (prevType == "keyword" && Regex.IsMatch(word, @"[ぁ-んゝ]+")))
|
||||
{
|
||||
result[result.Count - 1] += word;
|
||||
prevType = string.Empty;
|
||||
prevWord = word;
|
||||
return;
|
||||
}
|
||||
|
||||
result.Add(word);
|
||||
prevType = "keyword";
|
||||
prevWord = word;
|
||||
});
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static List<string> SplitLines(string text, SKPaint paint, float maxWidth)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return null;
|
||||
|
||||
var spaceWidth = paint.MeasureText(" ");
|
||||
var lines = text.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
var ret = new List<string>(lines.Length);
|
||||
foreach (var line in lines)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line)) continue;
|
||||
|
||||
float width = 0;
|
||||
var isCJK = false;
|
||||
var words = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (words.Length <= 1 && UserSettings.Default.AssetLanguage is ELanguage.Japanese or ELanguage.Korean or ELanguage.Chinese or ELanguage.TraditionalChinese)
|
||||
{
|
||||
words = SplitCKJText(line);
|
||||
isCJK = true;
|
||||
}
|
||||
|
||||
var lineResult = new StringBuilder();
|
||||
foreach (var word in words)
|
||||
{
|
||||
var wordWidth = paint.MeasureText(word);
|
||||
var wordWithSpaceWidth = wordWidth + spaceWidth;
|
||||
var wordWithSpace = isCJK ? word : word + " ";
|
||||
|
||||
if (width + wordWidth > maxWidth)
|
||||
{
|
||||
ret.Add(lineResult.ToString());
|
||||
lineResult = new StringBuilder(wordWithSpace);
|
||||
width = wordWithSpaceWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
lineResult.Append(wordWithSpace);
|
||||
width += wordWithSpaceWidth;
|
||||
}
|
||||
}
|
||||
|
||||
ret.Add(lineResult.ToString());
|
||||
return null;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
70
FModel/Discord/DiscordIntegration.cs
Normal file
70
FModel/Discord/DiscordIntegration.cs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
using DiscordRPC;
|
||||
using DiscordRPC.Logging;
|
||||
using FModel.Logger;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FModel.Discord
|
||||
{
|
||||
static class DiscordIntegration
|
||||
{
|
||||
private const string _DISCORD_APP_ID = "684489366189768767";
|
||||
|
||||
private static readonly Timestamps _baseTimestamp = new Timestamps { Start = DateTime.UtcNow };
|
||||
private static readonly Assets _assets = new Assets
|
||||
{
|
||||
LargeImageKey = "official_logo",
|
||||
SmallImageKey = "verified",
|
||||
SmallImageText = $"v{Assembly.GetExecutingAssembly().GetName().Version.ToString().Substring(0, 5)}"
|
||||
};
|
||||
private static readonly DiscordRpcClient _client = new DiscordRpcClient(_DISCORD_APP_ID);
|
||||
private static RichPresence _presence;
|
||||
|
||||
public static void Dispose() => _client.Dispose();
|
||||
public static void Deinitialize() => _client.Deinitialize();
|
||||
private static void Initialize()
|
||||
{
|
||||
_client.Logger = new ConsoleLogger() { Level = LogLevel.Warning };
|
||||
_client.OnReady += (sender, e) =>
|
||||
{
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[Discord RPC]", $"Ready for {e.User.Username}#{e.User.Discriminator} ({e.User.ID})");
|
||||
};
|
||||
_client.Initialize();
|
||||
}
|
||||
|
||||
public static void StartClient()
|
||||
{
|
||||
_client.SetPresence(new RichPresence
|
||||
{
|
||||
Assets = _assets,
|
||||
Timestamps = _baseTimestamp,
|
||||
State = string.Format(Properties.Resources.Idling, Globals.Game.GetName())
|
||||
});
|
||||
Initialize();
|
||||
SaveCurrentPresence();
|
||||
}
|
||||
|
||||
public static void Update(string detail = null, string state = null)
|
||||
{
|
||||
_client.SetPresence(new RichPresence
|
||||
{
|
||||
Assets = _assets,
|
||||
Timestamps = _baseTimestamp,
|
||||
Details = string.IsNullOrEmpty(detail) ? _presence?.Details : detail,
|
||||
State = string.IsNullOrEmpty(state) ? _presence?.State : state
|
||||
});
|
||||
_client.Invoke();
|
||||
}
|
||||
|
||||
public static void Restore()
|
||||
{
|
||||
if (_presence != null)
|
||||
{
|
||||
_client.SetPresence(_presence);
|
||||
_client.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveCurrentPresence() => _presence = _client.CurrentPresence;
|
||||
}
|
||||
}
|
||||
176
FModel/Enums.cs
176
FModel/Enums.cs
|
|
@ -1,116 +1,76 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FModel;
|
||||
|
||||
public enum EBuildKind
|
||||
namespace FModel
|
||||
{
|
||||
Debug,
|
||||
Release,
|
||||
Unknown
|
||||
}
|
||||
public enum EGame
|
||||
{
|
||||
Unknown,
|
||||
Fortnite,
|
||||
Valorant,
|
||||
DeadByDaylight,
|
||||
Borderlands3,
|
||||
MinecraftDungeons,
|
||||
BattleBreakers,
|
||||
Spellbreak,
|
||||
StateOfDecay2, // WIP
|
||||
TheCycleEA // TODO: Early Access version, change when game is released
|
||||
}
|
||||
|
||||
public enum EErrorKind
|
||||
{
|
||||
Ignore,
|
||||
Restart,
|
||||
ResetSettings
|
||||
}
|
||||
public enum EFModel
|
||||
{
|
||||
Debug,
|
||||
Release,
|
||||
Unknown
|
||||
}
|
||||
|
||||
public enum SettingsOut
|
||||
{
|
||||
ReloadLocres,
|
||||
ReloadMappings
|
||||
}
|
||||
public enum EPakLoader
|
||||
{
|
||||
Single,
|
||||
All,
|
||||
New,
|
||||
Modified,
|
||||
NewModified
|
||||
}
|
||||
|
||||
public enum EStatusKind
|
||||
{
|
||||
Ready, // ready
|
||||
Loading, // doing stuff
|
||||
Stopping, // trying to stop
|
||||
Stopped, // stopped
|
||||
Failed, // crashed
|
||||
Completed // worked
|
||||
}
|
||||
public enum ECopy
|
||||
{
|
||||
Path,
|
||||
PathNoExt,
|
||||
PathNoFile,
|
||||
File,
|
||||
FileNoExt
|
||||
}
|
||||
|
||||
public enum EAesReload
|
||||
{
|
||||
[Description("Always")]
|
||||
Always,
|
||||
[Description("Never")]
|
||||
Never,
|
||||
[Description("Once Per Day")]
|
||||
OncePerDay
|
||||
}
|
||||
public enum ELanguage : long
|
||||
{
|
||||
English,
|
||||
French,
|
||||
German,
|
||||
Italian,
|
||||
Spanish,
|
||||
SpanishLatin,
|
||||
Arabic,
|
||||
Japanese,
|
||||
Korean,
|
||||
Polish,
|
||||
PortugueseBrazil,
|
||||
Russian,
|
||||
Turkish,
|
||||
Chinese,
|
||||
TraditionalChinese,
|
||||
AustralianEnglish
|
||||
}
|
||||
|
||||
public enum EDiscordRpc
|
||||
{
|
||||
[Description("Always")]
|
||||
Always,
|
||||
[Description("Never")]
|
||||
Never
|
||||
}
|
||||
public enum EJsonType: long
|
||||
{
|
||||
Default,
|
||||
Positioned
|
||||
}
|
||||
|
||||
public enum ELoadingMode
|
||||
{
|
||||
[Description("Multiple")]
|
||||
Multiple,
|
||||
[Description("All")]
|
||||
All,
|
||||
[Description("All (New)")]
|
||||
AllButNew,
|
||||
[Description("All (Modified)")]
|
||||
AllButModified
|
||||
}
|
||||
|
||||
// public enum EUpdateMode
|
||||
// {
|
||||
// [Description("Stable")]
|
||||
// Stable,
|
||||
// [Description("Beta")]
|
||||
// Beta,
|
||||
// [Description("QA Testing")]
|
||||
// Qa
|
||||
// }
|
||||
|
||||
public enum ECompressedAudio
|
||||
{
|
||||
[Description("Play the decompressed data")]
|
||||
PlayDecompressed,
|
||||
[Description("Play the compressed data (might not always be a valid audio data)")]
|
||||
PlayCompressed
|
||||
}
|
||||
|
||||
public enum EIconStyle
|
||||
{
|
||||
[Description("Default")]
|
||||
Default,
|
||||
[Description("No Background")]
|
||||
NoBackground,
|
||||
[Description("No Text")]
|
||||
NoText,
|
||||
[Description("Flat")]
|
||||
Flat,
|
||||
[Description("Cataba")]
|
||||
Cataba,
|
||||
// [Description("Community")]
|
||||
// CommunityMade
|
||||
}
|
||||
|
||||
public enum EEndpointType
|
||||
{
|
||||
Aes,
|
||||
Mapping
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum EBulkType
|
||||
{
|
||||
None = 0,
|
||||
Auto = 1 << 0,
|
||||
Properties = 1 << 1,
|
||||
Textures = 1 << 2,
|
||||
Meshes = 1 << 3,
|
||||
Skeletons = 1 << 4,
|
||||
Animations = 1 << 5
|
||||
public enum EIconDesign : long
|
||||
{
|
||||
Default,
|
||||
NoBackground,
|
||||
NoText,
|
||||
Mini,
|
||||
Flat
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Xml;
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public static class AvalonExtensions
|
||||
{
|
||||
private static readonly IHighlightingDefinition _jsonHighlighter = LoadHighlighter("Json.xshd");
|
||||
private static readonly IHighlightingDefinition _iniHighlighter = LoadHighlighter("Ini.xshd");
|
||||
private static readonly IHighlightingDefinition _xmlHighlighter = LoadHighlighter("Xml.xshd");
|
||||
private static readonly IHighlightingDefinition _cppHighlighter = LoadHighlighter("Cpp.xshd");
|
||||
private static readonly IHighlightingDefinition _changelogHighlighter = LoadHighlighter("Changelog.xshd");
|
||||
private static readonly IHighlightingDefinition _verseHighlighter = LoadHighlighter("Verse.xshd");
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static IHighlightingDefinition LoadHighlighter(string resourceName)
|
||||
{
|
||||
var executingAssembly = Assembly.GetExecutingAssembly();
|
||||
using var stream = executingAssembly.GetManifestResourceStream($"{executingAssembly.GetName().Name}.Resources.{resourceName}");
|
||||
using var reader = new XmlTextReader(stream);
|
||||
return HighlightingLoader.Load(reader, HighlightingManager.Instance);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static IHighlightingDefinition HighlighterSelector(string ext)
|
||||
{
|
||||
switch (ext)
|
||||
{
|
||||
case "ini":
|
||||
case "csv":
|
||||
return _iniHighlighter;
|
||||
case "xml":
|
||||
case "tps":
|
||||
return _xmlHighlighter;
|
||||
case "h":
|
||||
case "cpp":
|
||||
return _cppHighlighter;
|
||||
case "changelog":
|
||||
return _changelogHighlighter;
|
||||
case "verse":
|
||||
return _verseHighlighter;
|
||||
case "bat":
|
||||
case "txt":
|
||||
case "pem":
|
||||
case "po":
|
||||
return null;
|
||||
default:
|
||||
return _jsonHighlighter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
using SkiaSharp;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public static class ClipboardExtensions
|
||||
{
|
||||
public static void SetImage(byte[] pngBytes, string fileName = null)
|
||||
{
|
||||
Clipboard.Clear();
|
||||
var data = new DataObject();
|
||||
using var pngMs = new MemoryStream(pngBytes);
|
||||
using var image = Image.FromStream(pngMs);
|
||||
// As standard bitmap, without transparency support
|
||||
data.SetData(DataFormats.Bitmap, image, true);
|
||||
// As PNG. Gimp will prefer this over the other two
|
||||
data.SetData("PNG", pngMs, false);
|
||||
// As DIB. This is (wrongly) accepted as ARGB by many applications
|
||||
using var dibMemStream = new MemoryStream(ConvertToDib(image));
|
||||
data.SetData(DataFormats.Dib, dibMemStream, false);
|
||||
// Optional fileName
|
||||
if (!string.IsNullOrEmpty(fileName))
|
||||
{
|
||||
var htmlFragment = GenerateHTMLFragment($"<img src=\"{fileName}\"/>");
|
||||
data.SetData(DataFormats.Html, htmlFragment);
|
||||
}
|
||||
// The 'copy=true' argument means the MemoryStreams can be safely disposed after the operation
|
||||
Clipboard.SetDataObject(data, true);
|
||||
}
|
||||
|
||||
public static byte[] ConvertToDib(Image image)
|
||||
{
|
||||
byte[] bm32bData;
|
||||
var width = image.Width;
|
||||
var height = image.Height;
|
||||
|
||||
// Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
|
||||
using (var bm32b = new Bitmap(width, height, PixelFormat.Format32bppPArgb))
|
||||
{
|
||||
using (var gr = Graphics.FromImage(bm32b))
|
||||
{
|
||||
gr.DrawImage(image, new Rectangle(0, 0, width, height));
|
||||
}
|
||||
|
||||
// Bitmap format has its lines reversed.
|
||||
bm32b.RotateFlip(RotateFlipType.Rotate180FlipX);
|
||||
bm32bData = GetRawBytes(bm32b);
|
||||
}
|
||||
|
||||
// BITMAPINFOHEADER struct for DIB.
|
||||
const int hdrSize = 0x28;
|
||||
var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
|
||||
//Int32 biSize;
|
||||
WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
|
||||
//Int32 biWidth;
|
||||
WriteIntToByteArray(fullImage, 0x04, 4, true, (uint) width);
|
||||
//Int32 biHeight;
|
||||
WriteIntToByteArray(fullImage, 0x08, 4, true, (uint) height);
|
||||
//Int16 biPlanes;
|
||||
WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
|
||||
//Int16 biBitCount;
|
||||
WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
|
||||
//BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
|
||||
WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
|
||||
//Int32 biSizeImage;
|
||||
WriteIntToByteArray(fullImage, 0x14, 4, true, (uint) bm32bData.Length);
|
||||
// These are all 0. Since .net clears new arrays, don't bother writing them.
|
||||
//Int32 biXPelsPerMeter = 0;
|
||||
//Int32 biYPelsPerMeter = 0;
|
||||
//Int32 biClrUsed = 0;
|
||||
//Int32 biClrImportant = 0;
|
||||
|
||||
// The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
|
||||
WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
|
||||
|
||||
Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint) bm32bData.Length);
|
||||
return fullImage;
|
||||
}
|
||||
|
||||
private static byte[] ConvertToDib(byte[] pngBytes = null)
|
||||
{
|
||||
byte[] bm32bData;
|
||||
int width, height;
|
||||
|
||||
using (var skBmp = SKBitmap.Decode(pngBytes))
|
||||
{
|
||||
width = skBmp.Width;
|
||||
height = skBmp.Height;
|
||||
using var rotated = new SKBitmap(new SKImageInfo(width, height, skBmp.ColorType));
|
||||
using var canvas = new SKCanvas(rotated);
|
||||
canvas.Scale(1, -1, 0, height / 2.0f);
|
||||
canvas.DrawBitmap(skBmp, SKPoint.Empty);
|
||||
canvas.Flush();
|
||||
bm32bData = rotated.Bytes;
|
||||
}
|
||||
|
||||
// BITMAPINFOHEADER struct for DIB.
|
||||
const int hdrSize = 0x28;
|
||||
var fullImage = new byte[hdrSize + 12 + bm32bData.Length];
|
||||
//Int32 biSize;
|
||||
WriteIntToByteArray(fullImage, 0x00, 4, true, hdrSize);
|
||||
//Int32 biWidth;
|
||||
WriteIntToByteArray(fullImage, 0x04, 4, true, (uint) width);
|
||||
//Int32 biHeight;
|
||||
WriteIntToByteArray(fullImage, 0x08, 4, true, (uint) height);
|
||||
//Int16 biPlanes;
|
||||
WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
|
||||
//Int16 biBitCount;
|
||||
WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
|
||||
//BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
|
||||
WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
|
||||
//Int32 biSizeImage;
|
||||
WriteIntToByteArray(fullImage, 0x14, 4, true, (uint) bm32bData.Length);
|
||||
// These are all 0. Since .net clears new arrays, don't bother writing them.
|
||||
//Int32 biXPelsPerMeter = 0;
|
||||
//Int32 biYPelsPerMeter = 0;
|
||||
//Int32 biClrUsed = 0;
|
||||
//Int32 biClrImportant = 0;
|
||||
|
||||
// The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
|
||||
WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
|
||||
WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
|
||||
|
||||
Unsafe.CopyBlockUnaligned(ref fullImage[hdrSize + 12], ref bm32bData[0], (uint) bm32bData.Length);
|
||||
return fullImage;
|
||||
}
|
||||
|
||||
public static unsafe byte[] GetRawBytes(Bitmap bmp)
|
||||
{
|
||||
var rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
|
||||
var bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
|
||||
var bytes = (uint) (Math.Abs(bmpData.Stride) * bmp.Height);
|
||||
var buffer = new byte[bytes];
|
||||
fixed (byte* pBuffer = buffer)
|
||||
{
|
||||
Unsafe.CopyBlockUnaligned(pBuffer, bmpData.Scan0.ToPointer(), bytes);
|
||||
}
|
||||
|
||||
bmp.UnlockBits(bmpData);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static void WriteIntToByteArray(byte[] data, int startIndex, int bytes, bool littleEndian, uint value)
|
||||
{
|
||||
var lastByte = bytes - 1;
|
||||
|
||||
if (data.Length < startIndex + bytes)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(startIndex), "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
|
||||
}
|
||||
|
||||
for (var index = 0; index < bytes; index++)
|
||||
{
|
||||
var offs = startIndex + (littleEndian ? index : lastByte - index);
|
||||
data[offs] = (byte) (value >> 8 * index & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateHTMLFragment(string html)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
const string header = "Version:0.9\r\nStartHTML:<<<<<<<<<1\r\nEndHTML:<<<<<<<<<2\r\nStartFragment:<<<<<<<<<3\r\nEndFragment:<<<<<<<<<4\r\n";
|
||||
const string startHTML = "<html>\r\n<body>\r\n";
|
||||
const string startFragment = "<!--StartFragment-->";
|
||||
const string endFragment = "<!--EndFragment-->";
|
||||
const string endHTML = "\r\n</body>\r\n</html>";
|
||||
|
||||
sb.Append(header);
|
||||
|
||||
var startHTMLLength = header.Length;
|
||||
var startFragmentLength = startHTMLLength + startHTML.Length + startFragment.Length;
|
||||
var endFragmentLength = startFragmentLength + Encoding.UTF8.GetByteCount(html);
|
||||
var endHTMLLength = endFragmentLength + endFragment.Length + endHTML.Length;
|
||||
|
||||
sb.Replace("<<<<<<<<<1", startHTMLLength.ToString("D10"));
|
||||
sb.Replace("<<<<<<<<<2", endHTMLLength.ToString("D10"));
|
||||
sb.Replace("<<<<<<<<<3", startFragmentLength.ToString("D10"));
|
||||
sb.Replace("<<<<<<<<<4", endFragmentLength.ToString("D10"));
|
||||
|
||||
sb.Append(startHTML);
|
||||
sb.Append(startFragment);
|
||||
sb.Append(html);
|
||||
sb.Append(endFragment);
|
||||
sb.Append(endHTML);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public static class CollectionExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Next<T>(this IList<T> collection, T value)
|
||||
{
|
||||
var i = collection.IndexOf(value) + 1;
|
||||
return i >= collection.Count ? collection.First() : collection[i];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Next<T>(this IList<T> collection, int index)
|
||||
{
|
||||
var i = index + 1;
|
||||
return i >= collection.Count ? collection.First() : collection[i];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Previous<T>(this IList<T> collection, T value)
|
||||
{
|
||||
var i = collection.IndexOf(value) - 1;
|
||||
return i < 0 ? collection.Last() : collection[i];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Previous<T>(this IList<T> collection, int index)
|
||||
{
|
||||
var i = index - 1;
|
||||
return i < 0 ? collection.Last() : collection[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public static class EnumExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string GetDescription(this Enum value)
|
||||
{
|
||||
var fi = value.GetType().GetField(value.ToString());
|
||||
if (fi == null) return $"{value} ({value:D})";
|
||||
|
||||
var attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||
if (attributes.Length > 0) return attributes[0].Description;
|
||||
|
||||
|
||||
var suffix = $"{value:D}";
|
||||
var current = Convert.ToInt32(suffix);
|
||||
var target = current & ~0xF;
|
||||
if (current != target)
|
||||
{
|
||||
var values = Enum.GetValues(value.GetType());
|
||||
var index = Array.IndexOf(values, value);
|
||||
suffix = values.GetValue(index - (current - target))?.ToString();
|
||||
}
|
||||
return $"{value} ({suffix})";
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T ToEnum<T>(this string value, T defaultValue) where T : struct => !Enum.TryParse(value, true, out T ret) ? defaultValue : ret;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool HasAnyFlags<T>(this T flags, T contains) where T : Enum, IConvertible => (flags.ToInt32(null) & contains.ToInt32(null)) != 0;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetIndex(this Enum value)
|
||||
{
|
||||
var values = Enum.GetValues(value.GetType());
|
||||
return Array.IndexOf(values, value);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Next<T>(this Enum value)
|
||||
{
|
||||
var values = Enum.GetValues(value.GetType());
|
||||
var i = Array.IndexOf(values, value) + 1;
|
||||
return i == values.Length ? (T) values.GetValue(0) : (T) values.GetValue(i);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T Previous<T>(this Enum value)
|
||||
{
|
||||
var values = Enum.GetValues(value.GetType());
|
||||
var i = Array.IndexOf(values, value) - 1;
|
||||
return i == -1 ? (T) values.GetValue(values.Length - 1) : (T) values.GetValue(i);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public enum Endianness
|
||||
{
|
||||
LittleEndian,
|
||||
BigEndian
|
||||
}
|
||||
|
||||
public static class StreamExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ReadUInt32(this Stream s, Endianness endian = Endianness.LittleEndian)
|
||||
{
|
||||
var b1 = s.ReadByte();
|
||||
var b2 = s.ReadByte();
|
||||
var b3 = s.ReadByte();
|
||||
var b4 = s.ReadByte();
|
||||
|
||||
return endian switch
|
||||
{
|
||||
Endianness.LittleEndian => (uint) (b4 << 24 | b3 << 16 | b2 << 8 | b1),
|
||||
Endianness.BigEndian => (uint) (b1 << 24 | b2 << 16 | b3 << 8 | b4),
|
||||
_ => throw new Exception("unknown endianness")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,185 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
|
||||
namespace FModel.Extensions;
|
||||
|
||||
public static class StringExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string GetReadableSize(double size)
|
||||
{
|
||||
if (size == 0) return "0 B";
|
||||
|
||||
string[] sizes = { "B", "KB", "MB", "GB", "TB" };
|
||||
var order = 0;
|
||||
while (size >= 1024 && order < sizes.Length - 1)
|
||||
{
|
||||
order++;
|
||||
size /= 1024;
|
||||
}
|
||||
|
||||
return $"{size:# ###.##} {sizes[order]}".TrimStart();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringBefore(this string s, char delimiter)
|
||||
{
|
||||
var index = s.IndexOf(delimiter);
|
||||
return index == -1 ? s : s[..index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringBefore(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
|
||||
{
|
||||
var index = s.IndexOf(delimiter, comparisonType);
|
||||
return index == -1 ? s : s[..index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringAfter(this string s, char delimiter)
|
||||
{
|
||||
var index = s.IndexOf(delimiter);
|
||||
return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringAfter(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
|
||||
{
|
||||
var index = s.IndexOf(delimiter, comparisonType);
|
||||
return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringBeforeLast(this string s, char delimiter)
|
||||
{
|
||||
var index = s.LastIndexOf(delimiter);
|
||||
return index == -1 ? s : s[..index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringBeforeLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
|
||||
{
|
||||
var index = s.LastIndexOf(delimiter, comparisonType);
|
||||
return index == -1 ? s : s[..index];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringBeforeWithLast(this string s, char delimiter)
|
||||
{
|
||||
var index = s.LastIndexOf(delimiter);
|
||||
return index == -1 ? s : s[..(index + 1)];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringBeforeWithLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
|
||||
{
|
||||
var index = s.LastIndexOf(delimiter, comparisonType);
|
||||
return index == -1 ? s : s[..(index + 1)];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringAfterLast(this string s, char delimiter)
|
||||
{
|
||||
var index = s.LastIndexOf(delimiter);
|
||||
return index == -1 ? s : s.Substring(index + 1, s.Length - index - 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string SubstringAfterLast(this string s, string delimiter, StringComparison comparisonType = StringComparison.Ordinal)
|
||||
{
|
||||
var index = s.LastIndexOf(delimiter, comparisonType);
|
||||
return index == -1 ? s : s.Substring(index + delimiter.Length, s.Length - index - delimiter.Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetNameLineNumber(this string s, string lineToFind)
|
||||
{
|
||||
if (int.TryParse(lineToFind, out var index))
|
||||
return s.GetLineNumber(index);
|
||||
|
||||
lineToFind = $" \"Name\": \"{lineToFind}\",";
|
||||
using var reader = new StringReader(s);
|
||||
var lineNum = 0;
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
lineNum++;
|
||||
if (line.Equals(lineToFind, StringComparison.OrdinalIgnoreCase))
|
||||
return lineNum;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static string GetParentExportType(this TextDocument doc, int startOffset)
|
||||
{
|
||||
var line = doc.GetLineByOffset(startOffset);
|
||||
var lineNumber = line.LineNumber - 1;
|
||||
|
||||
while (doc.GetText(line.Offset, line.Length) is { } content)
|
||||
{
|
||||
if (content.StartsWith(" \"Type\": \"", StringComparison.OrdinalIgnoreCase))
|
||||
return content.Split("\"")[3];
|
||||
|
||||
lineNumber--;
|
||||
if (lineNumber < 1) break;
|
||||
line = doc.GetLineByNumber(lineNumber);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetKismetLineNumber(this string s, string input)
|
||||
{
|
||||
var match = Regex.Match(input, @"^(.+)\[(\d+)\]$");
|
||||
var name = match.Groups[1].Value;
|
||||
int index = int.Parse(match.Groups[2].Value);
|
||||
var lineToFind = $" \"Name\": \"{name}\",";
|
||||
var offset = $"\"StatementIndex\": {index}";
|
||||
using var reader = new StringReader(s);
|
||||
var lineNum = 0;
|
||||
string line;
|
||||
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
lineNum++;
|
||||
if (line.Equals(lineToFind, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var objectLine = lineNum;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
lineNum++;
|
||||
if (line.Contains(offset, StringComparison.OrdinalIgnoreCase))
|
||||
return lineNum;
|
||||
}
|
||||
return objectLine;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static int GetLineNumber(this string s, int index)
|
||||
{
|
||||
using var reader = new StringReader(s);
|
||||
var lineNum = 0;
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
lineNum++;
|
||||
if (line.Equals(" {"))
|
||||
index--;
|
||||
|
||||
if (index == -1)
|
||||
return lineNum + 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,260 +1,231 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>FModel.ico</ApplicationIcon>
|
||||
<Version>4.4.4.0</Version>
|
||||
<AssemblyVersion>4.4.4.0</AssemblyVersion>
|
||||
<FileVersion>4.4.4.0</FileVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsPublishable>true</IsPublishable>
|
||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||
<PublishSingleFile Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">true</PublishSingleFile>
|
||||
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
|
||||
<StartupObject>FModel.App</StartupObject>
|
||||
<Authors>Asval</Authors>
|
||||
<Company></Company>
|
||||
<AssemblyVersion>3.1.1.2</AssemblyVersion>
|
||||
<FileVersion>3.1.1.2</FileVersion>
|
||||
<PackageIcon>FModel.ico</PackageIcon>
|
||||
<PackageIconUrl />
|
||||
<PackageProjectUrl>https://github.com/iAmAsval/FModel</PackageProjectUrl>
|
||||
<Description></Description>
|
||||
<Version>3.1.1</Version>
|
||||
<Platforms>x64</Platforms>
|
||||
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn>1701;1702;NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn>1701;1702;NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn>NU1701</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\android.png" />
|
||||
<None Remove="Resources\apple.png" />
|
||||
<None Remove="Resources\battlebreakers.png" />
|
||||
<None Remove="Resources\blueprint.png" />
|
||||
<None Remove="Resources\borderlands.png" />
|
||||
<None Remove="Resources\empty_folder.png" />
|
||||
<None Remove="Resources\engine.png" />
|
||||
<None Remove="Resources\fallenorder.png" />
|
||||
<None Remove="Resources\FModel.ico" />
|
||||
<None Remove="Resources\folder.png" />
|
||||
<None Remove="Resources\label.png" />
|
||||
<None Remove="Resources\fortnite.png" />
|
||||
<None Remove="Resources\fortnitebr.png" />
|
||||
<None Remove="Resources\gear.png" />
|
||||
<None Remove="Resources\localization.png" />
|
||||
<None Remove="Resources\materialicon.png" />
|
||||
<None Remove="Resources\square.png" />
|
||||
<None Remove="Resources\square_off.png" />
|
||||
<None Remove="Resources\cube.png" />
|
||||
<None Remove="Resources\cube_off.png" />
|
||||
<None Remove="Resources\light.png" />
|
||||
<None Remove="Resources\light_off.png" />
|
||||
<None Remove="Resources\pc.png" />
|
||||
<None Remove="Resources\puzzle.png" />
|
||||
<None Remove="Resources\roguecompany.png" />
|
||||
<None Remove="Resources\sound.png" />
|
||||
<None Remove="Resources\creative.png" />
|
||||
<None Remove="Resources\spellbreak.png" />
|
||||
<None Remove="Resources\texture.png" />
|
||||
<None Remove="Resources\thecycle.png" />
|
||||
<None Remove="Resources\ui.png" />
|
||||
<None Remove="Resources\valorant.png" />
|
||||
<None Remove="Resources\weapon.png" />
|
||||
<None Remove="Resources\windows.png" />
|
||||
<None Remove="Resources\cinematics.png" />
|
||||
<None Remove="Resources\archive.png" />
|
||||
<None Remove="Resources\archive_enabled.png" />
|
||||
<None Remove="Resources\archive_disabled.png" />
|
||||
<None Remove="Resources\unknown_asset.png" />
|
||||
<None Remove="Resources\asset.png" />
|
||||
<None Remove="Resources\asset_ini.png" />
|
||||
<None Remove="Resources\asset_psd.png" />
|
||||
<None Remove="Resources\asset_png.png" />
|
||||
<None Remove="Resources\athena.png" />
|
||||
<None Remove="Resources\Json.xshd" />
|
||||
<None Remove="Resources\Ini.xshd" />
|
||||
<None Remove="Resources\Verse.xshd" />
|
||||
<None Remove="Resources\Xml.xshd" />
|
||||
<None Remove="FModel.ico" />
|
||||
<None Remove="Resources\alert.ico" />
|
||||
<None Remove="Resources\alpha-a-box.png" />
|
||||
<None Remove="Resources\api-off.ico" />
|
||||
<None Remove="Resources\api.ico" />
|
||||
<None Remove="Resources\backup-restore.png" />
|
||||
<None Remove="Resources\battlebreakers.ico" />
|
||||
<None Remove="Resources\borderlands3.ico" />
|
||||
<None Remove="Resources\bug.png" />
|
||||
<None Remove="Resources\BurbankBigCondensed-Bold.ttf" />
|
||||
<None Remove="Resources\cast-audio.png" />
|
||||
<None Remove="Resources\challenge-theme-creator.png" />
|
||||
<None Remove="Resources\check-circle.ico" />
|
||||
<None Remove="Resources\ColorPickerOne.png" />
|
||||
<None Remove="Resources\ColorPickerTwo.png" />
|
||||
<None Remove="Resources\content-copy.png" />
|
||||
<None Remove="Resources\content-save.png" />
|
||||
<None Remove="Resources\Cpp.xshd" />
|
||||
<None Remove="Resources\Changelog.xshd" />
|
||||
<None Remove="Resources\unix.png" />
|
||||
<None Remove="Resources\linux.png" />
|
||||
<None Remove="Resources\stateofdecay2.png" />
|
||||
<None Remove="Resources\T_Placeholder_Item_Image.png" />
|
||||
<None Remove="Resources\checker.png" />
|
||||
<None Remove="Resources\T_ClipSize_Weapon_Stats.png" />
|
||||
<None Remove="Resources\T_DamagePerBullet_Weapon_Stats.png" />
|
||||
<None Remove="Resources\T_ReloadTime_Weapon_Stats.png" />
|
||||
<None Remove="Resources\delete-forever.png" />
|
||||
<None Remove="Resources\discord.png" />
|
||||
<None Remove="Resources\egl2.ico" />
|
||||
<None Remove="Resources\EIconDesign_Default.png" />
|
||||
<None Remove="Resources\EIconDesign_Flat.png" />
|
||||
<None Remove="Resources\EIconDesign_Mini.png" />
|
||||
<None Remove="Resources\EIconDesign_NoBackground.png" />
|
||||
<None Remove="Resources\EIconDesign_NoText.png" />
|
||||
<None Remove="Resources\file-export.png" />
|
||||
<None Remove="Resources\file-image.ico" />
|
||||
<None Remove="Resources\file-multiple.png" />
|
||||
<None Remove="Resources\file-restore.png" />
|
||||
<None Remove="Resources\file.png" />
|
||||
<None Remove="Resources\fmodel.png" />
|
||||
<None Remove="Resources\folder-download.png" />
|
||||
<None Remove="Resources\folder-open.png" />
|
||||
<None Remove="Resources\fortnite.ico" />
|
||||
<None Remove="Resources\gamepad-variant.png" />
|
||||
<None Remove="Resources\github-circle.png" />
|
||||
<None Remove="Resources\icon-creator.png" />
|
||||
<None Remove="Resources\image-filter-black-white.png" />
|
||||
<None Remove="Resources\image-move.png" />
|
||||
<None Remove="Resources\image-plus.png" />
|
||||
<None Remove="Resources\image-remove.png" />
|
||||
<None Remove="Resources\information.png" />
|
||||
<None Remove="Resources\Ini.xshd" />
|
||||
<None Remove="Resources\Json.xshd" />
|
||||
<None Remove="Resources\key.png" />
|
||||
<None Remove="Resources\lock-open-variant.ico" />
|
||||
<None Remove="Resources\magnify.png" />
|
||||
<None Remove="Resources\minecraftdungeons.ico" />
|
||||
<None Remove="Resources\open-in-new.png" />
|
||||
<None Remove="Resources\pause.png" />
|
||||
<None Remove="Resources\paypal.png" />
|
||||
<None Remove="Resources\pencil.png" />
|
||||
<None Remove="Resources\play.png" />
|
||||
<None Remove="Resources\playlist-plus.png" />
|
||||
<None Remove="Resources\power.png" />
|
||||
<None Remove="Resources\progress-download.png" />
|
||||
<None Remove="Resources\refresh.png" />
|
||||
<None Remove="Resources\settings.png" />
|
||||
<None Remove="Resources\share-all.png" />
|
||||
<None Remove="Resources\share.png" />
|
||||
<None Remove="Resources\sign-direction-plus.png" />
|
||||
<None Remove="Resources\sign-direction-remove.png" />
|
||||
<None Remove="Resources\sign-direction.png" />
|
||||
<None Remove="Resources\spellbreak.ico" />
|
||||
<None Remove="Resources\stop.png" />
|
||||
<None Remove="Resources\sod2.ico" />
|
||||
<None Remove="Resources\T-Icon-Pets-64.png" />
|
||||
<None Remove="Resources\T-Icon-Quests-64.png" />
|
||||
<None Remove="Resources\Default.png" />
|
||||
<None Remove="Resources\NoBackground.png" />
|
||||
<None Remove="Resources\NoText.png" />
|
||||
<None Remove="Resources\Flat.png" />
|
||||
<None Remove="Resources\Cataba.png" />
|
||||
<None Remove="Resources\BurbankBigCondensed-Bold.ttf" />
|
||||
<None Remove="Resources\add_directory.png" />
|
||||
<None Remove="Resources\delete.png" />
|
||||
<None Remove="Resources\edit.png" />
|
||||
<None Remove="Resources\go_to_directory.png" />
|
||||
<None Remove="Resources\npcleftside.png" />
|
||||
<None Remove="Resources\default.frag" />
|
||||
<None Remove="Resources\default.vert" />
|
||||
<None Remove="Resources\grid.frag" />
|
||||
<None Remove="Resources\grid.vert" />
|
||||
<None Remove="Resources\skybox.frag" />
|
||||
<None Remove="Resources\skybox.vert" />
|
||||
<None Remove="Resources\framebuffer.frag" />
|
||||
<None Remove="Resources\framebuffer.vert" />
|
||||
<None Remove="Resources\outline.frag" />
|
||||
<None Remove="Resources\outline.vert" />
|
||||
<None Remove="Resources\picking.frag" />
|
||||
<None Remove="Resources\picking.vert" />
|
||||
<None Remove="Resources\light.frag" />
|
||||
<None Remove="Resources\light.vert" />
|
||||
<None Remove="Resources\bone.frag" />
|
||||
<None Remove="Resources\bone.vert" />
|
||||
<None Remove="Resources\collision.vert" />
|
||||
<None Remove="Resources\thecycle.ico" />
|
||||
<None Remove="Resources\trello.png" />
|
||||
<None Remove="Resources\T_ClipSize_Weapon_Stats.png" />
|
||||
<None Remove="Resources\T_DamagePerBullet_Weapon_Stats.png" />
|
||||
<None Remove="Resources\T_Placeholder_Challenge_Image.png" />
|
||||
<None Remove="Resources\T_Placeholder_Item_Image.png" />
|
||||
<None Remove="Resources\T_ReloadTime_Weapon_Stats.png" />
|
||||
<None Remove="Resources\valorant.live.ico" />
|
||||
<None Remove="Resources\view-dashboard.png" />
|
||||
<None Remove="Resources\volume-minus.png" />
|
||||
<None Remove="Resources\volume-mute.png" />
|
||||
<None Remove="Resources\volume-plus.png" />
|
||||
<None Remove="Resources\wifi-strength-off.ico" />
|
||||
<None Remove="Resources\Xml.xshd" />
|
||||
<None Remove="Resources\zip-box.png" />
|
||||
<None Include="FModel.ico">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Json.xshd" />
|
||||
<EmbeddedResource Include="Resources\Ini.xshd" />
|
||||
<EmbeddedResource Include="Resources\Verse.xshd" />
|
||||
<EmbeddedResource Include="Resources\Xml.xshd" />
|
||||
<EmbeddedResource Include="Resources\Cpp.xshd" />
|
||||
<EmbeddedResource Include="Resources\Changelog.xshd" />
|
||||
<EmbeddedResource Include="Resources\default.frag" />
|
||||
<EmbeddedResource Include="Resources\default.vert" />
|
||||
<EmbeddedResource Include="Resources\grid.frag" />
|
||||
<EmbeddedResource Include="Resources\grid.vert" />
|
||||
<EmbeddedResource Include="Resources\skybox.frag" />
|
||||
<EmbeddedResource Include="Resources\skybox.vert" />
|
||||
<EmbeddedResource Include="Resources\framebuffer.frag" />
|
||||
<EmbeddedResource Include="Resources\framebuffer.vert" />
|
||||
<EmbeddedResource Include="Resources\outline.frag" />
|
||||
<EmbeddedResource Include="Resources\outline.vert" />
|
||||
<EmbeddedResource Include="Resources\picking.frag" />
|
||||
<EmbeddedResource Include="Resources\picking.vert" />
|
||||
<EmbeddedResource Include="Resources\light.frag" />
|
||||
<EmbeddedResource Include="Resources\light.vert" />
|
||||
<EmbeddedResource Include="Resources\bone.frag" />
|
||||
<EmbeddedResource Include="Resources\bone.vert" />
|
||||
<EmbeddedResource Include="Resources\collision.vert" />
|
||||
<EmbeddedResource Include="Resources\Ini.xshd" />
|
||||
<EmbeddedResource Include="Resources\Json.xshd" />
|
||||
<EmbeddedResource Include="Resources\Xml.xshd" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AdonisUI" Version="1.17.1" />
|
||||
<PackageReference Include="AdonisUI.ClassicTheme" Version="1.17.1" />
|
||||
<PackageReference Include="Autoupdater.NET.Official" Version="1.9.2" />
|
||||
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
|
||||
<PackageReference Include="Autoupdater.NET.Official" Version="1.6.2" />
|
||||
<PackageReference Include="AvalonEdit" Version="6.0.1" />
|
||||
<PackageReference Include="CSCore" Version="1.2.1.2" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageReference Include="EpicManifestParser" Version="2.3.3" />
|
||||
<PackageReference Include="ImGui.NET" Version="1.91.0.1" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.3.8" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.5" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||
<PackageReference Include="OpenTK" Version="4.8.2" />
|
||||
<PackageReference Include="RestSharp" Version="112.1.0" />
|
||||
<PackageReference Include="Serilog" Version="4.0.2" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
|
||||
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.88.8" />
|
||||
<PackageReference Include="SkiaSharp.Svg" Version="1.60.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.0.150" />
|
||||
<PackageReference Include="DotNetZip" Version="1.13.8" />
|
||||
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.0.1" />
|
||||
<PackageReference Include="K4os.Compression.LZ4.Streams" Version="1.1.11" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="NVorbis" Version="0.10.1" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="1.1.0" />
|
||||
<PackageReference Include="SkiaSharp" Version="2.80.1" />
|
||||
<PackageReference Include="ToastNotifications" Version="2.5.1" />
|
||||
<PackageReference Include="ToastNotifications.Messages" Version="2.5.1" />
|
||||
<PackageReference Include="WriteableBitmapEx" Version="1.6.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CUE4Parse\CUE4Parse-Conversion\CUE4Parse-Conversion.csproj" />
|
||||
<ProjectReference Include="..\CUE4Parse\CUE4Parse\CUE4Parse.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\android.png" />
|
||||
<Resource Include="Resources\apple.png" />
|
||||
<Resource Include="Resources\battlebreakers.png" />
|
||||
<Resource Include="Resources\blueprint.png" />
|
||||
<Resource Include="Resources\borderlands.png" />
|
||||
<Resource Include="Resources\fallenorder.png" />
|
||||
<Resource Include="Resources\FModel.ico" />
|
||||
<Resource Include="Resources\folder.png" />
|
||||
<Resource Include="Resources\label.png" />
|
||||
<Resource Include="Resources\fortnite.png" />
|
||||
<Resource Include="Resources\fortnitebr.png" />
|
||||
<Resource Include="Resources\empty_folder.png" />
|
||||
<Resource Include="Resources\engine.png" />
|
||||
<Resource Include="Resources\gear.png" />
|
||||
<Resource Include="Resources\localization.png" />
|
||||
<Resource Include="Resources\materialicon.png" />
|
||||
<Resource Include="Resources\square.png" />
|
||||
<Resource Include="Resources\square_off.png" />
|
||||
<Resource Include="Resources\cube.png" />
|
||||
<Resource Include="Resources\cube_off.png" />
|
||||
<Resource Include="Resources\light.png" />
|
||||
<Resource Include="Resources\light_off.png" />
|
||||
<Resource Include="Resources\pc.png" />
|
||||
<Resource Include="Resources\puzzle.png" />
|
||||
<Resource Include="Resources\roguecompany.png" />
|
||||
<Resource Include="Resources\spellbreak.png" />
|
||||
<Resource Include="Resources\sound.png" />
|
||||
<Resource Include="Resources\creative.png" />
|
||||
<Resource Include="Resources\texture.png" />
|
||||
<Resource Include="Resources\thecycle.png" />
|
||||
<Resource Include="Resources\valorant.png" />
|
||||
<Resource Include="Resources\ui.png" />
|
||||
<Resource Include="Resources\weapon.png" />
|
||||
<Resource Include="Resources\windows.png" />
|
||||
<Resource Include="Resources\cinematics.png" />
|
||||
<Resource Include="Resources\archive.png" />
|
||||
<Resource Include="Resources\archive_enabled.png" />
|
||||
<Resource Include="Resources\archive_disabled.png" />
|
||||
<Resource Include="Resources\unknown_asset.png" />
|
||||
<Resource Include="Resources\asset.png" />
|
||||
<Resource Include="Resources\asset_ini.png" />
|
||||
<Resource Include="Resources\asset_psd.png" />
|
||||
<Resource Include="Resources\asset_png.png" />
|
||||
<Resource Include="Resources\athena.png" />
|
||||
<Resource Include="Resources\unix.png" />
|
||||
<Resource Include="Resources\linux.png" />
|
||||
<Resource Include="Resources\stateofdecay2.png" />
|
||||
<Resource Include="Resources\T_Placeholder_Item_Image.png" />
|
||||
<Resource Include="Resources\checker.png" />
|
||||
<Resource Include="Resources\T_ClipSize_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\T_DamagePerBullet_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\T_ReloadTime_Weapon_Stats.png" />
|
||||
<Resource Include="FModel.ico" />
|
||||
<Resource Include="Resources\alert.ico" />
|
||||
<Resource Include="Resources\alpha-a-box.png" />
|
||||
<Resource Include="Resources\api-off.ico" />
|
||||
<Resource Include="Resources\api.ico" />
|
||||
<Resource Include="Resources\backup-restore.png" />
|
||||
<Resource Include="Resources\battlebreakers.ico" />
|
||||
<Resource Include="Resources\borderlands3.ico" />
|
||||
<Resource Include="Resources\bug.png" />
|
||||
<Resource Include="Resources\BurbankBigCondensed-Bold.ttf" />
|
||||
<Resource Include="Resources\cast-audio.png" />
|
||||
<Resource Include="Resources\challenge-theme-creator.png" />
|
||||
<Resource Include="Resources\check-circle.ico" />
|
||||
<Resource Include="Resources\ColorPickerOne.png" />
|
||||
<Resource Include="Resources\ColorPickerTwo.png" />
|
||||
<Resource Include="Resources\content-copy.png" />
|
||||
<Resource Include="Resources\content-save.png" />
|
||||
<Resource Include="Resources\delete-forever.png" />
|
||||
<Resource Include="Resources\discord.png" />
|
||||
<Resource Include="Resources\egl2.ico" />
|
||||
<Resource Include="Resources\EIconDesign_Default.png" />
|
||||
<Resource Include="Resources\EIconDesign_Flat.png" />
|
||||
<Resource Include="Resources\EIconDesign_Mini.png" />
|
||||
<Resource Include="Resources\EIconDesign_NoBackground.png" />
|
||||
<Resource Include="Resources\EIconDesign_NoText.png" />
|
||||
<Resource Include="Resources\file-export.png" />
|
||||
<Resource Include="Resources\file-image.ico" />
|
||||
<Resource Include="Resources\file-multiple.png" />
|
||||
<Resource Include="Resources\file-restore.png" />
|
||||
<Resource Include="Resources\file.png" />
|
||||
<Resource Include="Resources\fmodel.png" />
|
||||
<Resource Include="Resources\folder-download.png" />
|
||||
<Resource Include="Resources\folder-open.png" />
|
||||
<Resource Include="Resources\fortnite.ico" />
|
||||
<Resource Include="Resources\gamepad-variant.png" />
|
||||
<Resource Include="Resources\github-circle.png" />
|
||||
<Resource Include="Resources\icon-creator.png" />
|
||||
<Resource Include="Resources\image-filter-black-white.png" />
|
||||
<Resource Include="Resources\image-move.png" />
|
||||
<Resource Include="Resources\image-plus.png" />
|
||||
<Resource Include="Resources\image-remove.png" />
|
||||
<Resource Include="Resources\information.png" />
|
||||
<Resource Include="Resources\key.png" />
|
||||
<Resource Include="Resources\lock-open-variant.ico" />
|
||||
<Resource Include="Resources\magnify.png" />
|
||||
<Resource Include="Resources\minecraftdungeons.ico" />
|
||||
<Resource Include="Resources\open-in-new.png" />
|
||||
<Resource Include="Resources\pause.png" />
|
||||
<Resource Include="Resources\paypal.png" />
|
||||
<Resource Include="Resources\pencil.png" />
|
||||
<Resource Include="Resources\play.png" />
|
||||
<Resource Include="Resources\playlist-plus.png" />
|
||||
<Resource Include="Resources\power.png" />
|
||||
<Resource Include="Resources\progress-download.png" />
|
||||
<Resource Include="Resources\refresh.png" />
|
||||
<Resource Include="Resources\settings.png" />
|
||||
<Resource Include="Resources\share-all.png" />
|
||||
<Resource Include="Resources\share.png" />
|
||||
<Resource Include="Resources\sign-direction-plus.png" />
|
||||
<Resource Include="Resources\sign-direction-remove.png" />
|
||||
<Resource Include="Resources\sign-direction.png" />
|
||||
<Resource Include="Resources\spellbreak.ico" />
|
||||
<Resource Include="Resources\stop.png" />
|
||||
<Resource Include="Resources\sod2.ico" />
|
||||
<Resource Include="Resources\T-Icon-Pets-64.png" />
|
||||
<Resource Include="Resources\T-Icon-Quests-64.png" />
|
||||
<Resource Include="Resources\Default.png" />
|
||||
<Resource Include="Resources\NoBackground.png" />
|
||||
<Resource Include="Resources\NoText.png" />
|
||||
<Resource Include="Resources\Flat.png" />
|
||||
<Resource Include="Resources\Cataba.png" />
|
||||
<Resource Include="Resources\BurbankBigCondensed-Bold.ttf" />
|
||||
<Resource Include="Resources\add_directory.png" />
|
||||
<Resource Include="Resources\delete.png" />
|
||||
<Resource Include="Resources\edit.png" />
|
||||
<Resource Include="Resources\go_to_directory.png" />
|
||||
<Resource Include="Resources\npcleftside.png" />
|
||||
<Resource Include="Resources\nx.png" />
|
||||
<Resource Include="Resources\ny.png" />
|
||||
<Resource Include="Resources\nz.png" />
|
||||
<Resource Include="Resources\px.png" />
|
||||
<Resource Include="Resources\py.png" />
|
||||
<Resource Include="Resources\pz.png" />
|
||||
<Resource Include="Resources\pointlight.png" />
|
||||
<Resource Include="Resources\spotlight.png" />
|
||||
<Resource Include="Resources\link_on.png" />
|
||||
<Resource Include="Resources\link_off.png" />
|
||||
<Resource Include="Resources\link_has.png" />
|
||||
<Resource Include="Resources\tl_play.png" />
|
||||
<Resource Include="Resources\tl_pause.png" />
|
||||
<Resource Include="Resources\tl_rewind.png" />
|
||||
<Resource Include="Resources\tl_forward.png" />
|
||||
<Resource Include="Resources\tl_previous.png" />
|
||||
<Resource Include="Resources\tl_next.png" />
|
||||
<Resource Include="Resources\trello.png" />
|
||||
<Resource Include="Resources\thecycle.ico" />
|
||||
<Resource Include="Resources\T_ClipSize_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\T_DamagePerBullet_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\T_Placeholder_Challenge_Image.png" />
|
||||
<Resource Include="Resources\T_Placeholder_Item_Image.png" />
|
||||
<Resource Include="Resources\T_ReloadTime_Weapon_Stats.png" />
|
||||
<Resource Include="Resources\valorant.live.ico" />
|
||||
<Resource Include="Resources\view-dashboard.png" />
|
||||
<Resource Include="Resources\volume-minus.png" />
|
||||
<Resource Include="Resources\volume-mute.png" />
|
||||
<Resource Include="Resources\volume-plus.png" />
|
||||
<Resource Include="Resources\wifi-strength-off.ico" />
|
||||
<Resource Include="Resources\zip-box.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -263,6 +234,11 @@
|
|||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Properties\Settings.Designer.cs">
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -272,4 +248,10 @@
|
|||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Properties\Settings.settings">
|
||||
<Generator>PublicSettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31912.275
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FModel", "FModel.csproj", "{B1F494EA-90A6-4C24-800E-2F724A1884CA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUE4Parse", "..\CUE4Parse\CUE4Parse\CUE4Parse.csproj", "{C4620341-BBB7-4384-AC7D-5082D3E0386E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CUE4Parse-Conversion", "..\CUE4Parse\CUE4Parse-Conversion\CUE4Parse-Conversion.csproj", "{D0E1E8F7-F56D-469A-8E24-C2439B9FFD83}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B1F494EA-90A6-4C24-800E-2F724A1884CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B1F494EA-90A6-4C24-800E-2F724A1884CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B1F494EA-90A6-4C24-800E-2F724A1884CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B1F494EA-90A6-4C24-800E-2F724A1884CA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C4620341-BBB7-4384-AC7D-5082D3E0386E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C4620341-BBB7-4384-AC7D-5082D3E0386E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C4620341-BBB7-4384-AC7D-5082D3E0386E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C4620341-BBB7-4384-AC7D-5082D3E0386E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D0E1E8F7-F56D-469A-8E24-C2439B9FFD83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D0E1E8F7-F56D-469A-8E24-C2439B9FFD83}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D0E1E8F7-F56D-469A-8E24-C2439B9FFD83}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D0E1E8F7-F56D-469A-8E24-C2439B9FFD83}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {53DB7A15-4E15-4575-9402-0110BDF2794E}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class AsyncQueue<T> : IAsyncEnumerable<T>
|
||||
{
|
||||
private readonly SemaphoreSlim _semaphore = new(1);
|
||||
private readonly BufferBlock<T> _buffer = new();
|
||||
|
||||
public int Count => _buffer.Count;
|
||||
|
||||
public void Enqueue(T item) => _buffer.Post(item);
|
||||
|
||||
public async IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken token = default)
|
||||
{
|
||||
await _semaphore.WaitAsync(token);
|
||||
try
|
||||
{
|
||||
while (Count > 0)
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
yield return await _buffer.ReceiveAsync(token);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public abstract class Command : ICommand
|
||||
{
|
||||
public abstract void Execute(object parameter);
|
||||
|
||||
public abstract bool CanExecute(object parameter);
|
||||
|
||||
public void RaiseCanExecuteChanged()
|
||||
{
|
||||
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
using System;
|
||||
using HarfBuzzSharp;
|
||||
using SkiaSharp;
|
||||
using SkiaSharp.HarfBuzz;
|
||||
using Buffer = HarfBuzzSharp.Buffer;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class CustomSKShaper : SKShaper
|
||||
{
|
||||
private const int _FONT_SIZE_SCALE = 512;
|
||||
private readonly Font _font;
|
||||
|
||||
public CustomSKShaper(SKTypeface typeface) : base(typeface)
|
||||
{
|
||||
using var blob = Typeface.OpenStream(out var index).ToHarfBuzzBlob();
|
||||
using var face = new Face(blob, index);
|
||||
face.Index = index;
|
||||
face.UnitsPerEm = Typeface.UnitsPerEm;
|
||||
|
||||
_font = new Font(face);
|
||||
_font.SetScale(_FONT_SIZE_SCALE, _FONT_SIZE_SCALE);
|
||||
_font.SetFunctionsOpenType();
|
||||
}
|
||||
|
||||
public new Result Shape(Buffer buffer, float xOffset, float yOffset, SKPaint paint)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
|
||||
if (paint == null)
|
||||
throw new ArgumentNullException(nameof(paint));
|
||||
|
||||
// do the shaping
|
||||
_font.Shape(buffer);
|
||||
|
||||
// get the shaping results
|
||||
var len = buffer.Length;
|
||||
var info = buffer.GlyphInfos;
|
||||
var pos = buffer.GlyphPositions;
|
||||
|
||||
// get the sizes
|
||||
var textSizeY = paint.TextSize / _FONT_SIZE_SCALE;
|
||||
var textSizeX = textSizeY * paint.TextScaleX;
|
||||
|
||||
var points = new SKPoint[len];
|
||||
var clusters = new uint[len];
|
||||
var codepoints = new uint[len];
|
||||
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
// move the cursor
|
||||
xOffset += pos[i].XAdvance * textSizeX;
|
||||
yOffset += pos[i].YAdvance * textSizeY;
|
||||
|
||||
codepoints[i] = info[i].Codepoint;
|
||||
clusters[i] = info[i].Cluster;
|
||||
points[i] = new SKPoint(xOffset + pos[i].XOffset * textSizeX, yOffset - pos[i].YOffset * textSizeY);
|
||||
}
|
||||
|
||||
return new Result(codepoints, clusters, points, points[^1].X);
|
||||
}
|
||||
|
||||
public new Result Shape(string text, SKPaint paint) => Shape(text, 0, 0, paint);
|
||||
|
||||
public new Result Shape(string text, float xOffset, float yOffset, SKPaint paint)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return new Result();
|
||||
|
||||
using var buffer = new Buffer();
|
||||
switch (paint.TextEncoding)
|
||||
{
|
||||
case SKTextEncoding.Utf8:
|
||||
buffer.AddUtf8(text);
|
||||
break;
|
||||
case SKTextEncoding.Utf16:
|
||||
buffer.AddUtf16(text);
|
||||
break;
|
||||
case SKTextEncoding.Utf32:
|
||||
buffer.AddUtf32(text);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException("TextEncoding of type GlyphId is not supported.");
|
||||
}
|
||||
|
||||
buffer.GuessSegmentProperties();
|
||||
return Shape(buffer, xOffset, yOffset, paint);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
using RestSharp;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class FRestRequest : RestRequest
|
||||
{
|
||||
private const int TimeoutSeconds = 5;
|
||||
|
||||
public FRestRequest(string url, Method method = Method.Get) : base(url, method)
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(TimeoutSeconds);
|
||||
}
|
||||
|
||||
public FRestRequest(Uri uri, Method method = Method.Get) : base(uri, method)
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(TimeoutSeconds);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
namespace FModel.Framework;
|
||||
|
||||
public class FStatus : ViewModel
|
||||
{
|
||||
private bool _isReady;
|
||||
public bool IsReady
|
||||
{
|
||||
get => _isReady;
|
||||
private set => SetProperty(ref _isReady, value);
|
||||
}
|
||||
|
||||
private EStatusKind _kind;
|
||||
public EStatusKind Kind
|
||||
{
|
||||
get => _kind;
|
||||
private set
|
||||
{
|
||||
SetProperty(ref _kind, value);
|
||||
IsReady = Kind != EStatusKind.Loading && Kind != EStatusKind.Stopping;
|
||||
}
|
||||
}
|
||||
|
||||
private string _label;
|
||||
public string Label
|
||||
{
|
||||
get => _label;
|
||||
private set => SetProperty(ref _label, value);
|
||||
}
|
||||
|
||||
public FStatus()
|
||||
{
|
||||
SetStatus(EStatusKind.Loading);
|
||||
}
|
||||
|
||||
public void SetStatus(EStatusKind kind, string label = "")
|
||||
{
|
||||
Kind = kind;
|
||||
UpdateStatusLabel(label);
|
||||
}
|
||||
|
||||
public void UpdateStatusLabel(string label, string prefix = null)
|
||||
{
|
||||
Label = Kind == EStatusKind.Loading ? $"{prefix ?? Kind.ToString()} {label}".Trim() : Kind.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class FullyObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
|
||||
{
|
||||
/// <summary>
|
||||
/// Occurs when a property is changed within an item.
|
||||
/// </summary>
|
||||
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
|
||||
|
||||
public FullyObservableCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public FullyObservableCollection(List<T> list) : base(list)
|
||||
{
|
||||
ObserveAll();
|
||||
}
|
||||
|
||||
public FullyObservableCollection(IEnumerable<T> enumerable) : base(enumerable)
|
||||
{
|
||||
ObserveAll();
|
||||
}
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.Action is NotifyCollectionChangedAction.Remove or
|
||||
NotifyCollectionChangedAction.Replace)
|
||||
{
|
||||
foreach (T item in e.OldItems)
|
||||
item.PropertyChanged -= ChildPropertyChanged;
|
||||
}
|
||||
|
||||
if (e.Action is NotifyCollectionChangedAction.Add or
|
||||
NotifyCollectionChangedAction.Replace)
|
||||
{
|
||||
foreach (T item in e.NewItems)
|
||||
item.PropertyChanged += ChildPropertyChanged;
|
||||
}
|
||||
|
||||
base.OnCollectionChanged(e);
|
||||
}
|
||||
|
||||
protected void OnItemPropertyChanged(ItemPropertyChangedEventArgs e)
|
||||
{
|
||||
ItemPropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
protected void OnItemPropertyChanged(int index, PropertyChangedEventArgs e)
|
||||
{
|
||||
OnItemPropertyChanged(new ItemPropertyChangedEventArgs(index, e));
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
foreach (T item in Items)
|
||||
item.PropertyChanged -= ChildPropertyChanged;
|
||||
|
||||
base.ClearItems();
|
||||
}
|
||||
|
||||
private void ObserveAll()
|
||||
{
|
||||
foreach (var item in Items)
|
||||
item.PropertyChanged += ChildPropertyChanged;
|
||||
}
|
||||
|
||||
private void ChildPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
var typedSender = (T) sender;
|
||||
var i = Items.IndexOf(typedSender);
|
||||
|
||||
if (i < 0)
|
||||
throw new ArgumentException("Received property notification from item not in collection");
|
||||
|
||||
OnItemPropertyChanged(i, e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides data for the <see cref="FullyObservableCollection{T}.ItemPropertyChanged"/> event.
|
||||
/// </summary>
|
||||
public class ItemPropertyChangedEventArgs : PropertyChangedEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the index in the collection for which the property change has occurred.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// Index in parent collection.
|
||||
/// </value>
|
||||
public int CollectionIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="index">The index in the collection of changed item.</param>
|
||||
/// <param name="name">The name of the property that changed.</param>
|
||||
public ItemPropertyChangedEventArgs(int index, string name) : base(name)
|
||||
{
|
||||
CollectionIndex = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ItemPropertyChangedEventArgs"/> class.
|
||||
/// </summary>
|
||||
/// <param name="index">The index.</param>
|
||||
/// <param name="args">The <see cref="PropertyChangedEventArgs"/> instance containing the event data.</param>
|
||||
public ItemPropertyChangedEventArgs(int index, PropertyChangedEventArgs args) : this(index, args.PropertyName)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
using System.Text;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class Hotkey : ViewModel
|
||||
{
|
||||
private Key _key;
|
||||
public Key Key
|
||||
{
|
||||
get => _key;
|
||||
set => SetProperty(ref _key, value);
|
||||
}
|
||||
|
||||
private ModifierKeys _modifiers;
|
||||
public ModifierKeys Modifiers
|
||||
{
|
||||
get => _modifiers;
|
||||
set => SetProperty(ref _modifiers, value);
|
||||
}
|
||||
|
||||
public Hotkey(Key key, ModifierKeys modifiers = ModifierKeys.None)
|
||||
{
|
||||
Key = key;
|
||||
Modifiers = modifiers;
|
||||
}
|
||||
|
||||
public bool IsTriggered(Key e)
|
||||
{
|
||||
return e == Key && Keyboard.Modifiers.HasFlag(Modifiers);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var str = new StringBuilder();
|
||||
|
||||
if (Modifiers.HasFlag(ModifierKeys.Control))
|
||||
str.Append("Ctrl + ");
|
||||
if (Modifiers.HasFlag(ModifierKeys.Shift))
|
||||
str.Append("Shift + ");
|
||||
if (Modifiers.HasFlag(ModifierKeys.Alt))
|
||||
str.Append("Alt + ");
|
||||
if (Modifiers.HasFlag(ModifierKeys.Windows))
|
||||
str.Append("Win + ");
|
||||
|
||||
str.Append(Key);
|
||||
return str.ToString();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,608 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using FModel.Settings;
|
||||
using ImGuiNET;
|
||||
using OpenTK.Graphics.OpenGL4;
|
||||
using OpenTK.Windowing.Desktop;
|
||||
using OpenTK.Windowing.GraphicsLibraryFramework;
|
||||
using ErrorCode = OpenTK.Graphics.OpenGL4.ErrorCode;
|
||||
using Keys = OpenTK.Windowing.GraphicsLibraryFramework.Keys;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class ImGuiController : IDisposable
|
||||
{
|
||||
private bool _frameBegun;
|
||||
|
||||
private int _vertexArray;
|
||||
private int _vertexBuffer;
|
||||
private int _vertexBufferSize;
|
||||
private int _indexBuffer;
|
||||
private int _indexBufferSize;
|
||||
|
||||
//private Texture _fontTexture;
|
||||
|
||||
private int _fontTexture;
|
||||
|
||||
private int _shader;
|
||||
private int _shaderFontTextureLocation;
|
||||
private int _shaderProjectionMatrixLocation;
|
||||
|
||||
private int _windowWidth;
|
||||
private int _windowHeight;
|
||||
|
||||
public ImFontPtr FontNormal;
|
||||
public ImFontPtr FontBold;
|
||||
public ImFontPtr FontSemiBold;
|
||||
|
||||
private readonly Vector2 _scaleFactor = Vector2.One;
|
||||
public readonly float DpiScale = GetDpiScale();
|
||||
|
||||
private static bool KHRDebugAvailable = false;
|
||||
|
||||
public ImGuiController(int width, int height)
|
||||
{
|
||||
_windowWidth = width;
|
||||
_windowHeight = height;
|
||||
|
||||
int major = GL.GetInteger(GetPName.MajorVersion);
|
||||
int minor = GL.GetInteger(GetPName.MinorVersion);
|
||||
|
||||
KHRDebugAvailable = (major == 4 && minor >= 3) || IsExtensionSupported("KHR_debug");
|
||||
|
||||
IntPtr context = ImGui.CreateContext();
|
||||
ImGui.SetCurrentContext(context);
|
||||
|
||||
var io = ImGui.GetIO();
|
||||
unsafe
|
||||
{
|
||||
var iniFileNamePtr = Marshal.StringToCoTaskMemUTF8(Path.Combine(UserSettings.Default.OutputDirectory, ".data", "imgui.ini"));
|
||||
io.NativePtr->IniFilename = (byte*)iniFileNamePtr;
|
||||
}
|
||||
FontNormal = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeui.ttf", 16 * DpiScale);
|
||||
FontBold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\segoeuib.ttf", 16 * DpiScale);
|
||||
FontSemiBold = io.Fonts.AddFontFromFileTTF("C:\\Windows\\Fonts\\seguisb.ttf", 16 * DpiScale);
|
||||
|
||||
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
|
||||
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
|
||||
io.ConfigFlags |= ImGuiConfigFlags.DockingEnable;
|
||||
io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines;
|
||||
|
||||
CreateDeviceResources();
|
||||
|
||||
SetPerFrameImGuiData(1f / 60f);
|
||||
|
||||
ImGui.NewFrame();
|
||||
_frameBegun = true;
|
||||
}
|
||||
|
||||
public void Bold() => PushFont(FontBold);
|
||||
public void SemiBold() => PushFont(FontSemiBold);
|
||||
|
||||
public void PopFont()
|
||||
{
|
||||
ImGui.PopFont();
|
||||
PushFont(FontNormal);
|
||||
}
|
||||
|
||||
private void PushFont(ImFontPtr ptr) => ImGui.PushFont(ptr);
|
||||
|
||||
public void WindowResized(int width, int height)
|
||||
{
|
||||
_windowWidth = width;
|
||||
_windowHeight = height;
|
||||
}
|
||||
|
||||
public void DestroyDeviceObjects()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void CreateDeviceResources()
|
||||
{
|
||||
_vertexBufferSize = 10000;
|
||||
_indexBufferSize = 2000;
|
||||
|
||||
int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding);
|
||||
int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding);
|
||||
|
||||
_vertexArray = GL.GenVertexArray();
|
||||
GL.BindVertexArray(_vertexArray);
|
||||
LabelObject(ObjectLabelIdentifier.VertexArray, _vertexArray, "ImGui");
|
||||
|
||||
_vertexBuffer = GL.GenBuffer();
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer);
|
||||
LabelObject(ObjectLabelIdentifier.Buffer, _vertexBuffer, "VBO: ImGui");
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, _vertexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw);
|
||||
|
||||
_indexBuffer = GL.GenBuffer();
|
||||
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _indexBuffer);
|
||||
LabelObject(ObjectLabelIdentifier.Buffer, _indexBuffer, "EBO: ImGui");
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, _indexBufferSize, IntPtr.Zero, BufferUsageHint.DynamicDraw);
|
||||
|
||||
RecreateFontDeviceTexture();
|
||||
|
||||
string VertexSource = @"#version 460 core
|
||||
uniform mat4 projection_matrix;
|
||||
layout(location = 0) in vec2 in_position;
|
||||
layout(location = 1) in vec2 in_texCoord;
|
||||
layout(location = 2) in vec4 in_color;
|
||||
out vec4 color;
|
||||
out vec2 texCoord;
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection_matrix * vec4(in_position, 0, 1);
|
||||
color = in_color;
|
||||
texCoord = in_texCoord;
|
||||
}";
|
||||
string FragmentSource = @"#version 460 core
|
||||
uniform sampler2D in_fontTexture;
|
||||
in vec4 color;
|
||||
in vec2 texCoord;
|
||||
out vec4 outputColor;
|
||||
void main()
|
||||
{
|
||||
outputColor = color * texture(in_fontTexture, texCoord);
|
||||
}";
|
||||
|
||||
_shader = CreateProgram("ImGui", VertexSource, FragmentSource);
|
||||
_shaderProjectionMatrixLocation = GL.GetUniformLocation(_shader, "projection_matrix");
|
||||
_shaderFontTextureLocation = GL.GetUniformLocation(_shader, "in_fontTexture");
|
||||
|
||||
int stride = Unsafe.SizeOf<ImDrawVert>();
|
||||
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, stride, 0);
|
||||
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, stride, 8);
|
||||
GL.VertexAttribPointer(2, 4, VertexAttribPointerType.UnsignedByte, true, stride, 16);
|
||||
|
||||
GL.EnableVertexAttribArray(0);
|
||||
GL.EnableVertexAttribArray(1);
|
||||
GL.EnableVertexAttribArray(2);
|
||||
|
||||
GL.BindVertexArray(prevVAO);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer);
|
||||
|
||||
CheckGLError("End of ImGui setup");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recreates the device texture used to render text.
|
||||
/// </summary>
|
||||
public void RecreateFontDeviceTexture()
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
io.Fonts.GetTexDataAsRGBA32(out IntPtr pixels, out int width, out int height, out int bytesPerPixel);
|
||||
|
||||
int mips = (int)Math.Floor(Math.Log(Math.Max(width, height), 2));
|
||||
|
||||
int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture);
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
int prevTexture2D = GL.GetInteger(GetPName.TextureBinding2D);
|
||||
|
||||
_fontTexture = GL.GenTexture();
|
||||
GL.BindTexture(TextureTarget.Texture2D, _fontTexture);
|
||||
GL.TexStorage2D(TextureTarget2d.Texture2D, mips, SizedInternalFormat.Rgba8, width, height);
|
||||
LabelObject(ObjectLabelIdentifier.Texture, _fontTexture, "ImGui Text Atlas");
|
||||
|
||||
GL.TexSubImage2D(TextureTarget.Texture2D, 0, 0, 0, width, height, PixelFormat.Bgra, PixelType.UnsignedByte, pixels);
|
||||
|
||||
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
|
||||
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
|
||||
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, mips - 1);
|
||||
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
|
||||
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
|
||||
|
||||
// Restore state
|
||||
GL.BindTexture(TextureTarget.Texture2D, prevTexture2D);
|
||||
GL.ActiveTexture((TextureUnit)prevActiveTexture);
|
||||
|
||||
io.Fonts.SetTexID((IntPtr)_fontTexture);
|
||||
|
||||
io.Fonts.ClearTexData();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the ImGui draw list data.
|
||||
/// </summary>
|
||||
public void Render()
|
||||
{
|
||||
if (_frameBegun)
|
||||
{
|
||||
_frameBegun = false;
|
||||
ImGui.Render();
|
||||
RenderImDrawData(ImGui.GetDrawData());
|
||||
}
|
||||
CheckGLError("End of frame");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates ImGui input and IO configuration state.
|
||||
/// </summary>
|
||||
public void Update(GameWindow wnd, float deltaSeconds)
|
||||
{
|
||||
if (_frameBegun)
|
||||
{
|
||||
ImGui.Render();
|
||||
}
|
||||
|
||||
SetPerFrameImGuiData(deltaSeconds);
|
||||
UpdateImGuiInput(wnd);
|
||||
|
||||
_frameBegun = true;
|
||||
ImGui.NewFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets per-frame data based on the associated window.
|
||||
/// This is called by Update(float).
|
||||
/// </summary>
|
||||
private void SetPerFrameImGuiData(float deltaSeconds)
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
// if (io.WantSaveIniSettings) ImGui.SaveIniSettingsToDisk(_iniPath);
|
||||
io.DisplaySize = new Vector2(
|
||||
_windowWidth / _scaleFactor.X,
|
||||
_windowHeight / _scaleFactor.Y);
|
||||
io.DisplayFramebufferScale = _scaleFactor;
|
||||
io.DeltaTime = deltaSeconds; // DeltaTime is in seconds.
|
||||
}
|
||||
|
||||
readonly List<char> PressedChars = new List<char>();
|
||||
|
||||
private void UpdateImGuiInput(GameWindow wnd)
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
var mState = wnd.MouseState;
|
||||
var kState = wnd.KeyboardState;
|
||||
|
||||
io.AddMousePosEvent(mState.X, mState.Y);
|
||||
io.AddMouseButtonEvent(0, mState[MouseButton.Left]);
|
||||
io.AddMouseButtonEvent(1, mState[MouseButton.Right]);
|
||||
io.AddMouseButtonEvent(2, mState[MouseButton.Middle]);
|
||||
io.AddMouseButtonEvent(3, mState[MouseButton.Button1]);
|
||||
io.AddMouseButtonEvent(4, mState[MouseButton.Button2]);
|
||||
io.AddMouseWheelEvent(mState.ScrollDelta.X, mState.ScrollDelta.Y);
|
||||
|
||||
foreach (Keys key in Enum.GetValues(typeof(Keys)))
|
||||
{
|
||||
if (key == Keys.Unknown) continue;
|
||||
io.AddKeyEvent(TranslateKey(key), kState.IsKeyDown(key));
|
||||
}
|
||||
|
||||
foreach (var c in PressedChars)
|
||||
{
|
||||
io.AddInputCharacter(c);
|
||||
}
|
||||
PressedChars.Clear();
|
||||
|
||||
io.KeyShift = kState.IsKeyDown(Keys.LeftShift) || kState.IsKeyDown(Keys.RightShift);
|
||||
io.KeyCtrl = kState.IsKeyDown(Keys.LeftControl) || kState.IsKeyDown(Keys.RightControl);
|
||||
io.KeyAlt = kState.IsKeyDown(Keys.LeftAlt) || kState.IsKeyDown(Keys.RightAlt);
|
||||
io.KeySuper = kState.IsKeyDown(Keys.LeftSuper) || kState.IsKeyDown(Keys.RightSuper);
|
||||
}
|
||||
|
||||
public void PressChar(char keyChar)
|
||||
{
|
||||
PressedChars.Add(keyChar);
|
||||
}
|
||||
|
||||
private void RenderImDrawData(ImDrawDataPtr draw_data)
|
||||
{
|
||||
if (draw_data.CmdListsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get intial state.
|
||||
int prevVAO = GL.GetInteger(GetPName.VertexArrayBinding);
|
||||
int prevArrayBuffer = GL.GetInteger(GetPName.ArrayBufferBinding);
|
||||
int prevProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||
bool prevBlendEnabled = GL.GetBoolean(GetPName.Blend);
|
||||
bool prevScissorTestEnabled = GL.GetBoolean(GetPName.ScissorTest);
|
||||
int prevBlendEquationRgb = GL.GetInteger(GetPName.BlendEquationRgb);
|
||||
int prevBlendEquationAlpha = GL.GetInteger(GetPName.BlendEquationAlpha);
|
||||
int prevBlendFuncSrcRgb = GL.GetInteger(GetPName.BlendSrcRgb);
|
||||
int prevBlendFuncSrcAlpha = GL.GetInteger(GetPName.BlendSrcAlpha);
|
||||
int prevBlendFuncDstRgb = GL.GetInteger(GetPName.BlendDstRgb);
|
||||
int prevBlendFuncDstAlpha = GL.GetInteger(GetPName.BlendDstAlpha);
|
||||
bool prevCullFaceEnabled = GL.GetBoolean(GetPName.CullFace);
|
||||
bool prevDepthTestEnabled = GL.GetBoolean(GetPName.DepthTest);
|
||||
int prevActiveTexture = GL.GetInteger(GetPName.ActiveTexture);
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
int prevTexture2D = GL.GetInteger(GetPName.TextureBinding2D);
|
||||
Span<int> prevScissorBox = stackalloc int[4];
|
||||
unsafe
|
||||
{
|
||||
fixed (int* iptr = &prevScissorBox[0])
|
||||
{
|
||||
GL.GetInteger(GetPName.ScissorBox, iptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the element buffer (thru the VAO) so that we can resize it.
|
||||
GL.BindVertexArray(_vertexArray);
|
||||
// Bind the vertex buffer so that we can resize it.
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBuffer);
|
||||
for (int i = 0; i < draw_data.CmdListsCount; i++)
|
||||
{
|
||||
ImDrawListPtr cmd_list = draw_data.CmdLists[i];
|
||||
|
||||
int vertexSize = cmd_list.VtxBuffer.Size * Unsafe.SizeOf<ImDrawVert>();
|
||||
if (vertexSize > _vertexBufferSize)
|
||||
{
|
||||
int newSize = (int)Math.Max(_vertexBufferSize * 1.5f, vertexSize);
|
||||
|
||||
GL.BufferData(BufferTarget.ArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw);
|
||||
_vertexBufferSize = newSize;
|
||||
}
|
||||
|
||||
int indexSize = cmd_list.IdxBuffer.Size * sizeof(ushort);
|
||||
if (indexSize > _indexBufferSize)
|
||||
{
|
||||
int newSize = (int)Math.Max(_indexBufferSize * 1.5f, indexSize);
|
||||
GL.BufferData(BufferTarget.ElementArrayBuffer, newSize, IntPtr.Zero, BufferUsageHint.DynamicDraw);
|
||||
_indexBufferSize = newSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup orthographic projection matrix into our constant buffer
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
var mvp = OpenTK.Mathematics.Matrix4.CreateOrthographicOffCenter(
|
||||
0.0f,
|
||||
io.DisplaySize.X,
|
||||
io.DisplaySize.Y,
|
||||
0.0f,
|
||||
-1.0f,
|
||||
1.0f);
|
||||
|
||||
GL.UseProgram(_shader);
|
||||
GL.UniformMatrix4(_shaderProjectionMatrixLocation, false, ref mvp);
|
||||
GL.Uniform1(_shaderFontTextureLocation, 0);
|
||||
CheckGLError("Projection");
|
||||
|
||||
GL.BindVertexArray(_vertexArray);
|
||||
CheckGLError("VAO");
|
||||
|
||||
draw_data.ScaleClipRects(io.DisplayFramebufferScale);
|
||||
|
||||
GL.Enable(EnableCap.Blend);
|
||||
GL.Enable(EnableCap.ScissorTest);
|
||||
GL.BlendEquation(BlendEquationMode.FuncAdd);
|
||||
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
|
||||
GL.Disable(EnableCap.CullFace);
|
||||
GL.Disable(EnableCap.DepthTest);
|
||||
|
||||
// Render command lists
|
||||
for (int n = 0; n < draw_data.CmdListsCount; n++)
|
||||
{
|
||||
ImDrawListPtr cmd_list = draw_data.CmdLists[n];
|
||||
|
||||
GL.BufferSubData(BufferTarget.ArrayBuffer, IntPtr.Zero, cmd_list.VtxBuffer.Size * Unsafe.SizeOf<ImDrawVert>(), cmd_list.VtxBuffer.Data);
|
||||
CheckGLError($"Data Vert {n}");
|
||||
|
||||
GL.BufferSubData(BufferTarget.ElementArrayBuffer, IntPtr.Zero, cmd_list.IdxBuffer.Size * sizeof(ushort), cmd_list.IdxBuffer.Data);
|
||||
CheckGLError($"Data Idx {n}");
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list.CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
ImDrawCmdPtr pcmd = cmd_list.CmdBuffer[cmd_i];
|
||||
if (pcmd.UserCallback != IntPtr.Zero)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.ActiveTexture(TextureUnit.Texture0);
|
||||
GL.BindTexture(TextureTarget.Texture2D, (int)pcmd.TextureId);
|
||||
CheckGLError("Texture");
|
||||
|
||||
// We do _windowHeight - (int)clip.W instead of (int)clip.Y because gl has flipped Y when it comes to these coordinates
|
||||
var clip = pcmd.ClipRect;
|
||||
GL.Scissor((int)clip.X, _windowHeight - (int)clip.W, (int)(clip.Z - clip.X), (int)(clip.W - clip.Y));
|
||||
CheckGLError("Scissor");
|
||||
|
||||
if ((io.BackendFlags & ImGuiBackendFlags.RendererHasVtxOffset) != 0)
|
||||
{
|
||||
GL.DrawElementsBaseVertex(PrimitiveType.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (IntPtr)(pcmd.IdxOffset * sizeof(ushort)), unchecked((int)pcmd.VtxOffset));
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.DrawElements(BeginMode.Triangles, (int)pcmd.ElemCount, DrawElementsType.UnsignedShort, (int)pcmd.IdxOffset * sizeof(ushort));
|
||||
}
|
||||
CheckGLError("Draw");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GL.Disable(EnableCap.Blend);
|
||||
GL.Disable(EnableCap.ScissorTest);
|
||||
|
||||
// Reset state
|
||||
GL.BindTexture(TextureTarget.Texture2D, prevTexture2D);
|
||||
GL.ActiveTexture((TextureUnit)prevActiveTexture);
|
||||
GL.UseProgram(prevProgram);
|
||||
GL.BindVertexArray(prevVAO);
|
||||
GL.Scissor(prevScissorBox[0], prevScissorBox[1], prevScissorBox[2], prevScissorBox[3]);
|
||||
GL.BindBuffer(BufferTarget.ArrayBuffer, prevArrayBuffer);
|
||||
GL.BlendEquationSeparate((BlendEquationMode)prevBlendEquationRgb, (BlendEquationMode)prevBlendEquationAlpha);
|
||||
GL.BlendFuncSeparate(
|
||||
(BlendingFactorSrc)prevBlendFuncSrcRgb,
|
||||
(BlendingFactorDest)prevBlendFuncDstRgb,
|
||||
(BlendingFactorSrc)prevBlendFuncSrcAlpha,
|
||||
(BlendingFactorDest)prevBlendFuncDstAlpha);
|
||||
if (prevBlendEnabled) GL.Enable(EnableCap.Blend); else GL.Disable(EnableCap.Blend);
|
||||
if (prevDepthTestEnabled) GL.Enable(EnableCap.DepthTest); else GL.Disable(EnableCap.DepthTest);
|
||||
if (prevCullFaceEnabled) GL.Enable(EnableCap.CullFace); else GL.Disable(EnableCap.CullFace);
|
||||
if (prevScissorTestEnabled) GL.Enable(EnableCap.ScissorTest); else GL.Disable(EnableCap.ScissorTest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Frees all graphics resources used by the renderer.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
GL.DeleteVertexArray(_vertexArray);
|
||||
GL.DeleteBuffer(_vertexBuffer);
|
||||
GL.DeleteBuffer(_indexBuffer);
|
||||
|
||||
GL.DeleteTexture(_fontTexture);
|
||||
GL.DeleteProgram(_shader);
|
||||
}
|
||||
|
||||
public static void LabelObject(ObjectLabelIdentifier objLabelIdent, int glObject, string name)
|
||||
{
|
||||
if (KHRDebugAvailable)
|
||||
GL.ObjectLabel(objLabelIdent, glObject, name.Length, name);
|
||||
}
|
||||
|
||||
static bool IsExtensionSupported(string name)
|
||||
{
|
||||
int n = GL.GetInteger(GetPName.NumExtensions);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
string extension = GL.GetString(StringNameIndexed.Extensions, i);
|
||||
if (extension == name) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int CreateProgram(string name, string vertexSource, string fragmentSoruce)
|
||||
{
|
||||
int program = GL.CreateProgram();
|
||||
LabelObject(ObjectLabelIdentifier.Program, program, $"Program: {name}");
|
||||
|
||||
int vertex = CompileShader(name, ShaderType.VertexShader, vertexSource);
|
||||
int fragment = CompileShader(name, ShaderType.FragmentShader, fragmentSoruce);
|
||||
|
||||
GL.AttachShader(program, vertex);
|
||||
GL.AttachShader(program, fragment);
|
||||
|
||||
GL.LinkProgram(program);
|
||||
|
||||
GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int success);
|
||||
if (success == 0)
|
||||
{
|
||||
string info = GL.GetProgramInfoLog(program);
|
||||
Debug.WriteLine($"GL.LinkProgram had info log [{name}]:\n{info}");
|
||||
}
|
||||
|
||||
GL.DetachShader(program, vertex);
|
||||
GL.DetachShader(program, fragment);
|
||||
|
||||
GL.DeleteShader(vertex);
|
||||
GL.DeleteShader(fragment);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
private static int CompileShader(string name, ShaderType type, string source)
|
||||
{
|
||||
int shader = GL.CreateShader(type);
|
||||
LabelObject(ObjectLabelIdentifier.Shader, shader, $"Shader: {name}");
|
||||
|
||||
GL.ShaderSource(shader, source);
|
||||
GL.CompileShader(shader);
|
||||
|
||||
GL.GetShader(shader, ShaderParameter.CompileStatus, out int success);
|
||||
if (success == 0)
|
||||
{
|
||||
string info = GL.GetShaderInfoLog(shader);
|
||||
Debug.WriteLine($"GL.CompileShader for shader '{name}' [{type}] had info log:\n{info}");
|
||||
}
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
public static void CheckGLError(string title)
|
||||
{
|
||||
ErrorCode error;
|
||||
int i = 1;
|
||||
while ((error = GL.GetError()) != ErrorCode.NoError)
|
||||
{
|
||||
Debug.Print($"{title} ({i++}): {error}");
|
||||
}
|
||||
}
|
||||
|
||||
public static float GetDpiScale()
|
||||
{
|
||||
return Math.Max((float)(Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth), (float)(Screen.PrimaryScreen.Bounds.Height / SystemParameters.PrimaryScreenHeight));
|
||||
}
|
||||
|
||||
public static ImGuiKey TranslateKey(Keys key)
|
||||
{
|
||||
if (key is >= Keys.D0 and <= Keys.D9)
|
||||
return key - Keys.D0 + ImGuiKey._0;
|
||||
|
||||
if (key is >= Keys.A and <= Keys.Z)
|
||||
return key - Keys.A + ImGuiKey.A;
|
||||
|
||||
if (key is >= Keys.KeyPad0 and <= Keys.KeyPad9)
|
||||
return key - Keys.KeyPad0 + ImGuiKey.Keypad0;
|
||||
|
||||
if (key is >= Keys.F1 and <= Keys.F24)
|
||||
return key - Keys.F1 + ImGuiKey.F24;
|
||||
|
||||
return key switch
|
||||
{
|
||||
Keys.Tab => ImGuiKey.Tab,
|
||||
Keys.Left => ImGuiKey.LeftArrow,
|
||||
Keys.Right => ImGuiKey.RightArrow,
|
||||
Keys.Up => ImGuiKey.UpArrow,
|
||||
Keys.Down => ImGuiKey.DownArrow,
|
||||
Keys.PageUp => ImGuiKey.PageUp,
|
||||
Keys.PageDown => ImGuiKey.PageDown,
|
||||
Keys.Home => ImGuiKey.Home,
|
||||
Keys.End => ImGuiKey.End,
|
||||
Keys.Insert => ImGuiKey.Insert,
|
||||
Keys.Delete => ImGuiKey.Delete,
|
||||
Keys.Backspace => ImGuiKey.Backspace,
|
||||
Keys.Space => ImGuiKey.Space,
|
||||
Keys.Enter => ImGuiKey.Enter,
|
||||
Keys.Escape => ImGuiKey.Escape,
|
||||
Keys.Apostrophe => ImGuiKey.Apostrophe,
|
||||
Keys.Comma => ImGuiKey.Comma,
|
||||
Keys.Minus => ImGuiKey.Minus,
|
||||
Keys.Period => ImGuiKey.Period,
|
||||
Keys.Slash => ImGuiKey.Slash,
|
||||
Keys.Semicolon => ImGuiKey.Semicolon,
|
||||
Keys.Equal => ImGuiKey.Equal,
|
||||
Keys.LeftBracket => ImGuiKey.LeftBracket,
|
||||
Keys.Backslash => ImGuiKey.Backslash,
|
||||
Keys.RightBracket => ImGuiKey.RightBracket,
|
||||
Keys.GraveAccent => ImGuiKey.GraveAccent,
|
||||
Keys.CapsLock => ImGuiKey.CapsLock,
|
||||
Keys.ScrollLock => ImGuiKey.ScrollLock,
|
||||
Keys.NumLock => ImGuiKey.NumLock,
|
||||
Keys.PrintScreen => ImGuiKey.PrintScreen,
|
||||
Keys.Pause => ImGuiKey.Pause,
|
||||
Keys.KeyPadDecimal => ImGuiKey.KeypadDecimal,
|
||||
Keys.KeyPadDivide => ImGuiKey.KeypadDivide,
|
||||
Keys.KeyPadMultiply => ImGuiKey.KeypadMultiply,
|
||||
Keys.KeyPadSubtract => ImGuiKey.KeypadSubtract,
|
||||
Keys.KeyPadAdd => ImGuiKey.KeypadAdd,
|
||||
Keys.KeyPadEnter => ImGuiKey.KeypadEnter,
|
||||
Keys.KeyPadEqual => ImGuiKey.KeypadEqual,
|
||||
Keys.LeftShift => ImGuiKey.LeftShift,
|
||||
Keys.LeftControl => ImGuiKey.LeftCtrl,
|
||||
Keys.LeftAlt => ImGuiKey.LeftAlt,
|
||||
Keys.LeftSuper => ImGuiKey.LeftSuper,
|
||||
Keys.RightShift => ImGuiKey.RightShift,
|
||||
Keys.RightControl => ImGuiKey.RightCtrl,
|
||||
Keys.RightAlt => ImGuiKey.RightAlt,
|
||||
Keys.RightSuper => ImGuiKey.RightSuper,
|
||||
Keys.Menu => ImGuiKey.Menu,
|
||||
_ => ImGuiKey.None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using RestSharp;
|
||||
using RestSharp.Serializers;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class JsonNetSerializer : IRestSerializer, ISerializer, IDeserializer
|
||||
{
|
||||
public static readonly JsonSerializerSettings SerializerSettings = new()
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
MissingMemberHandling = MissingMemberHandling.Ignore,
|
||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||
};
|
||||
|
||||
public string Serialize(Parameter parameter) => JsonConvert.SerializeObject(parameter.Value);
|
||||
public string Serialize(object obj) => JsonConvert.SerializeObject(obj);
|
||||
public T Deserialize<T>(RestResponse response) => JsonConvert.DeserializeObject<T>(response.Content!, SerializerSettings);
|
||||
|
||||
public ISerializer Serializer => this;
|
||||
public IDeserializer Deserializer => this;
|
||||
|
||||
public ContentType ContentType { get; set; } = ContentType.Json;
|
||||
public string[] AcceptedContentTypes => ContentType.JsonAccept;
|
||||
public SupportsContentType SupportsContentType => contentType => contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase);
|
||||
|
||||
public DataFormat DataFormat => DataFormat.Json;
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class NavigationList<T> : List<T>
|
||||
{
|
||||
private int _currentIndex;
|
||||
public int CurrentIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentIndex > Count - 1)
|
||||
{
|
||||
_currentIndex = Count - 1;
|
||||
}
|
||||
|
||||
if (_currentIndex < 0)
|
||||
{
|
||||
_currentIndex = 0;
|
||||
}
|
||||
|
||||
return _currentIndex;
|
||||
}
|
||||
set => _currentIndex = value;
|
||||
}
|
||||
|
||||
public T MoveNext
|
||||
{
|
||||
get
|
||||
{
|
||||
_currentIndex++;
|
||||
return this[CurrentIndex];
|
||||
}
|
||||
}
|
||||
|
||||
public T MovePrevious
|
||||
{
|
||||
get
|
||||
{
|
||||
_currentIndex--;
|
||||
return this[CurrentIndex];
|
||||
}
|
||||
}
|
||||
|
||||
public T Current => this[CurrentIndex];
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public sealed class RangeObservableCollection<T> : ObservableCollection<T>
|
||||
{
|
||||
private bool _suppressNotification;
|
||||
|
||||
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (!_suppressNotification)
|
||||
base.OnCollectionChanged(e);
|
||||
}
|
||||
|
||||
public void AddRange(IEnumerable<T> list)
|
||||
{
|
||||
if (list == null)
|
||||
throw new ArgumentNullException(nameof(list));
|
||||
|
||||
_suppressNotification = true;
|
||||
|
||||
foreach (var item in list)
|
||||
Add(item);
|
||||
|
||||
_suppressNotification = false;
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
|
||||
public void SetSuppressionState(bool state)
|
||||
{
|
||||
_suppressNotification = state;
|
||||
}
|
||||
|
||||
public void InvokeOnCollectionChanged(NotifyCollectionChangedAction changedAction = NotifyCollectionChangedAction.Reset)
|
||||
{
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(changedAction));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo, IDataErrorInfo
|
||||
{
|
||||
private readonly Dictionary<string, IList<string>> _validationErrors = new();
|
||||
|
||||
public string this[string propertyName]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(propertyName))
|
||||
return Error;
|
||||
|
||||
return _validationErrors.ContainsKey(propertyName) ? string.Join(Environment.NewLine, _validationErrors[propertyName]) : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore] public string Error => string.Join(Environment.NewLine, GetAllErrors());
|
||||
[JsonIgnore] public bool HasErrors => _validationErrors.Any();
|
||||
|
||||
public IEnumerable GetErrors(string propertyName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(propertyName))
|
||||
return _validationErrors.SelectMany(kvp => kvp.Value);
|
||||
|
||||
return _validationErrors.TryGetValue(propertyName, out var errors) ? errors : Enumerable.Empty<object>();
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetAllErrors()
|
||||
{
|
||||
return _validationErrors.SelectMany(kvp => kvp.Value).Where(e => !string.IsNullOrEmpty(e));
|
||||
}
|
||||
|
||||
public void AddValidationError(string propertyName, string errorMessage)
|
||||
{
|
||||
if (!_validationErrors.ContainsKey(propertyName))
|
||||
_validationErrors.Add(propertyName, new List<string>());
|
||||
|
||||
_validationErrors[propertyName].Add(errorMessage);
|
||||
}
|
||||
|
||||
public void ClearValidationErrors(string propertyName)
|
||||
{
|
||||
if (_validationErrors.ContainsKey(propertyName))
|
||||
_validationErrors.Remove(propertyName);
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
#pragma warning disable 67
|
||||
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
|
||||
#pragma warning disable 67
|
||||
|
||||
protected void RaisePropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (EqualityComparer<T>.Default.Equals(storage, value))
|
||||
return false;
|
||||
|
||||
storage = value;
|
||||
RaisePropertyChanged(propertyName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
using System;
|
||||
|
||||
namespace FModel.Framework;
|
||||
|
||||
public abstract class ViewModelCommand<TContextViewModel> : Command where TContextViewModel : ViewModel
|
||||
{
|
||||
private WeakReference _parent;
|
||||
|
||||
public TContextViewModel ContextViewModel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_parent is { IsAlive: true })
|
||||
return (TContextViewModel) _parent.Target;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (ContextViewModel == value)
|
||||
return;
|
||||
|
||||
_parent = value != null ? new WeakReference(value) : null;
|
||||
}
|
||||
}
|
||||
|
||||
protected ViewModelCommand(TContextViewModel contextViewModel)
|
||||
{
|
||||
ContextViewModel = contextViewModel /*?? throw new ArgumentNullException(nameof(contextViewModel))*/;
|
||||
}
|
||||
|
||||
public sealed override void Execute(object parameter)
|
||||
{
|
||||
Execute(ContextViewModel, parameter);
|
||||
}
|
||||
|
||||
public abstract void Execute(TContextViewModel contextViewModel, object parameter);
|
||||
|
||||
public sealed override bool CanExecute(object parameter)
|
||||
{
|
||||
return CanExecute(ContextViewModel, parameter);
|
||||
}
|
||||
|
||||
public virtual bool CanExecute(TContextViewModel contextViewModel, object parameter)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
83
FModel/Globals.cs
Normal file
83
FModel/Globals.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
using PakReader.Pak;
|
||||
using PakReader.Parsers.Objects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows;
|
||||
using ToastNotifications;
|
||||
using ToastNotifications.Lifetime;
|
||||
using ToastNotifications.Position;
|
||||
|
||||
namespace FModel
|
||||
{
|
||||
static class Globals
|
||||
{
|
||||
/// <summary>
|
||||
/// string is the pak file name
|
||||
/// PakFileReader is the reader where you can grab the FPakEntries, MountPoint and more
|
||||
/// </summary>
|
||||
public static readonly Dictionary<string, PakFileReader> CachedPakFiles = new Dictionary<string, PakFileReader>();
|
||||
public static readonly Notifier gNotifier = new Notifier(cfg =>
|
||||
{
|
||||
cfg.LifetimeSupervisor = new TimeAndCountBasedLifetimeSupervisor(TimeSpan.FromSeconds(7), MaximumNotificationCount.FromCount(15));
|
||||
cfg.PositionProvider = new PrimaryScreenPositionProvider(Corner.BottomRight, 5, 5);
|
||||
cfg.Dispatcher = Application.Current.Dispatcher;
|
||||
});
|
||||
public static bool bSearch = false; // trigger the event to select a file thank to the search window
|
||||
public static string sSearch = string.Empty; // this will be the file name triggered
|
||||
public static FGame Game = new FGame(EGame.Unknown, EPakVersion.LATEST, 0);
|
||||
public const EFModel Build =
|
||||
#if RELEASE
|
||||
EFModel.Release;
|
||||
#elif DEBUG
|
||||
EFModel.Debug;
|
||||
#else
|
||||
EFModel.Unknown;
|
||||
#endif
|
||||
}
|
||||
|
||||
public class FGame
|
||||
{
|
||||
public EGame ActualGame;
|
||||
public EPakVersion Version;
|
||||
public int SubVersion;
|
||||
|
||||
public FGame(EGame game, EPakVersion version, int subVersion)
|
||||
{
|
||||
ActualGame = game;
|
||||
Version = version;
|
||||
SubVersion = subVersion;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return ActualGame switch
|
||||
{
|
||||
EGame.Fortnite => "Fortnite",
|
||||
EGame.Valorant => "Valorant",
|
||||
EGame.DeadByDaylight => "Dead By Daylight",
|
||||
EGame.Borderlands3 => "Borderlands 3",
|
||||
EGame.MinecraftDungeons => "Minecraft Dungeons",
|
||||
EGame.BattleBreakers => "Battle Breakers",
|
||||
EGame.Spellbreak => "Spellbreak",
|
||||
EGame.StateOfDecay2 => "State of Decay 2",
|
||||
EGame.TheCycleEA => "The Cycle (Early Access)",
|
||||
EGame.Unknown => "Unknown",
|
||||
_ => "Unknown",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static class FColors
|
||||
{
|
||||
public const string Red = "#E06C75";
|
||||
public const string Orange = "#D19A66";
|
||||
public const string Yellow = "#E5C07B";
|
||||
public const string Purple = "#C678DD";
|
||||
public const string Blue = "#61AFEF";
|
||||
public const string Discord = "#8B9BD4";
|
||||
public const string Green = "#98C379";
|
||||
public const string LightGray = "#BBBBBB";
|
||||
public const string DarkGray = "#9B9B9B";
|
||||
public const string White = "#EFEFEF";
|
||||
}
|
||||
}
|
||||
37
FModel/Grabber/Aes/AesData.cs
Normal file
37
FModel/Grabber/Aes/AesData.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using FModel.Logger;
|
||||
using FModel.Utils;
|
||||
using FModel.Windows.CustomNotifier;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FModel.Grabber.Aes
|
||||
{
|
||||
static class AesData
|
||||
{
|
||||
public static async Task<BenResponse> GetData()
|
||||
{
|
||||
if (NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
BenResponse data = await Endpoints.GetJsonEndpoint<BenResponse>(Endpoints.BENBOT_AES, string.Empty).ConfigureAwait(false);
|
||||
return data;
|
||||
}
|
||||
else
|
||||
{
|
||||
Globals.gNotifier.ShowCustomMessage(Properties.Resources.AES, Properties.Resources.NoInternet, "/FModel;component/Resources/wifi-strength-off.ico");
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[AES]", "No internet");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class BenResponse
|
||||
{
|
||||
[JsonProperty("mainKey")]
|
||||
public string MainKey { get; set; }
|
||||
|
||||
[JsonProperty("dynamicKeys")]
|
||||
public Dictionary<string, string> DynamicKeys { get; set; }
|
||||
}
|
||||
}
|
||||
71
FModel/Grabber/Aes/AesGrabber.cs
Normal file
71
FModel/Grabber/Aes/AesGrabber.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using FModel.Logger;
|
||||
using FModel.ViewModels.MenuItem;
|
||||
using FModel.Windows.CustomNotifier;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FModel.Grabber.Aes
|
||||
{
|
||||
static class AesGrabber
|
||||
{
|
||||
public static async Task<bool> Load(bool forceReload = false)
|
||||
{
|
||||
if (Globals.Game.ActualGame == EGame.Fortnite && MenuItems.pakFiles.AtLeastOnePak())
|
||||
{
|
||||
if (forceReload)
|
||||
{
|
||||
Dictionary<string, string> staticKeys = new Dictionary<string, string>();
|
||||
if (!string.IsNullOrEmpty(Properties.Settings.Default.StaticAesKeys))
|
||||
staticKeys = JsonConvert.DeserializeObject<Dictionary<string, string>>(Properties.Settings.Default.StaticAesKeys);
|
||||
|
||||
Dictionary<string, Dictionary<string, string>> oldDynamicKeys = new Dictionary<string, Dictionary<string, string>>();
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Properties.Settings.Default.DynamicAesKeys))
|
||||
oldDynamicKeys = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, string>>>(Properties.Settings.Default.DynamicAesKeys);
|
||||
}
|
||||
catch (JsonSerializationException) { /* Needed for the transition bewteen global dynamic keys and "per game" dynamic keys */ }
|
||||
|
||||
BenResponse benResponse = await AesData.GetData().ConfigureAwait(false);
|
||||
if (benResponse != null)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(benResponse.MainKey))
|
||||
{
|
||||
string mainKey = $"0x{benResponse.MainKey.Substring(2).ToUpper()}";
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[AES]", $"BenBot Main key is {mainKey}");
|
||||
staticKeys[Globals.Game.ActualGame.ToString()] = mainKey;
|
||||
Properties.Settings.Default.StaticAesKeys = JsonConvert.SerializeObject(staticKeys, Formatting.None);
|
||||
}
|
||||
|
||||
if (oldDynamicKeys.TryGetValue(Globals.Game.ActualGame.ToString(), out var gameDict))
|
||||
{
|
||||
Dictionary<string, string> difference = benResponse.DynamicKeys
|
||||
.Where(x => !gameDict.ContainsKey(x.Key) || !gameDict[x.Key].Equals(x.Value))
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
foreach (KeyValuePair<string, string> KvP in difference)
|
||||
{
|
||||
Globals.gNotifier.ShowCustomMessage(
|
||||
Properties.Resources.PakFiles,
|
||||
string.Format(
|
||||
Properties.Resources.PakCanBeOpened,
|
||||
KvP.Key.Substring(KvP.Key.IndexOf("Paks/") + "Paks/".Length)),
|
||||
"/FModel;component/Resources/lock-open-variant.ico");
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[AES]", $"{KvP.Key} with key {KvP.Value} can be opened");
|
||||
}
|
||||
}
|
||||
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[AES]", $"BenBot Dynamic keys are {benResponse.DynamicKeys}");
|
||||
oldDynamicKeys[Globals.Game.ActualGame.ToString()] = benResponse.DynamicKeys;
|
||||
Properties.Settings.Default.DynamicAesKeys = JsonConvert.SerializeObject(oldDynamicKeys, Formatting.None);
|
||||
Properties.Settings.Default.Save();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
53
FModel/Grabber/Cdn/CdnData.cs
Normal file
53
FModel/Grabber/Cdn/CdnData.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using AutoUpdaterDotNET;
|
||||
using FModel.Logger;
|
||||
using FModel.Utils;
|
||||
using FModel.Windows.CustomNotifier;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FModel.Grabber.Cdn
|
||||
{
|
||||
static class CdnData
|
||||
{
|
||||
public static readonly bool bInternet = NetworkInterface.GetIsNetworkAvailable();
|
||||
|
||||
public static async Task<CdnResponse> GetData()
|
||||
{
|
||||
if (bInternet)
|
||||
return await Endpoints.GetJsonEndpoint<CdnResponse>(Endpoints.FMODEL_JSON).ConfigureAwait(false);
|
||||
else
|
||||
{
|
||||
Globals.gNotifier.ShowCustomMessage("CDN", Properties.Resources.NoInternet, "/FModel;component/Resources/wifi-strength-off.ico");
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[CDN]", "No internet");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CdnResponse
|
||||
{
|
||||
public Dictionary<string, Backup[]> Backups { get; set; }
|
||||
public Dictionary<string, GlobalMessage[]> GlobalMessages { get; set; }
|
||||
public FUpdater Updater { get; set; }
|
||||
}
|
||||
public class Backup
|
||||
{
|
||||
public string Header { get; set; }
|
||||
public string DownloadUrl { get; set; }
|
||||
public double Size { get; set; }
|
||||
}
|
||||
public class GlobalMessage
|
||||
{
|
||||
public string Message { get; set; }
|
||||
public string Color { get; set; }
|
||||
public bool NewLine { get; set; }
|
||||
}
|
||||
public class FUpdater
|
||||
{
|
||||
public string Version { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Changelog { get; set; }
|
||||
public Mandatory Mandatory { get; set; }
|
||||
}
|
||||
}
|
||||
102
FModel/Grabber/Cdn/CdnDataGrabber.cs
Normal file
102
FModel/Grabber/Cdn/CdnDataGrabber.cs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
using FModel.Logger;
|
||||
using FModel.Utils;
|
||||
using FModel.ViewModels.MenuItem;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace FModel.Grabber.Cdn
|
||||
{
|
||||
static class CdnDataGrabber
|
||||
{
|
||||
private static CdnResponse _data = null; // avoid multiple requests on same endpoint
|
||||
|
||||
public static async Task DoCDNStuff()
|
||||
{
|
||||
await PopulateBackups().ConfigureAwait(false); // step by step
|
||||
await ShowGMessages().ConfigureAwait(false); // step by step
|
||||
}
|
||||
|
||||
public static async Task PopulateBackups()
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
// Backup PAKs Menu Item
|
||||
MenuItems.backupFiles.Add(new BackupMenuItemViewModel
|
||||
{
|
||||
Header = Properties.Resources.BackupPaks,
|
||||
Icon = new Image { Source = new BitmapImage(new Uri("Resources/backup-restore.png", UriKind.Relative)) }
|
||||
});
|
||||
MenuItems.backupFiles.Add(new Separator { });
|
||||
});
|
||||
|
||||
List<BackupMenuItemViewModel> backupsInfos = await GetBackups().ConfigureAwait(false);
|
||||
if (backupsInfos.Any())
|
||||
{
|
||||
foreach (BackupMenuItemViewModel b in backupsInfos)
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(delegate
|
||||
{
|
||||
DebugHelper.WriteLine("{0} {1} {2}", "[FModel]", "[CDN]", $"{b.Header} is available to download");
|
||||
MenuItems.backupFiles.Add(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<FUpdater> GetUpdateData()
|
||||
{
|
||||
if (_data == null)
|
||||
_data = await CdnData.GetData().ConfigureAwait(false);
|
||||
|
||||
if (_data != null)
|
||||
{
|
||||
return _data.Updater;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async Task<List<BackupMenuItemViewModel>> GetBackups()
|
||||
{
|
||||
if (_data == null)
|
||||
_data = await CdnData.GetData().ConfigureAwait(false);
|
||||
|
||||
if (_data != null && _data.Backups.TryGetValue(Globals.Game.ActualGame.ToString(), out var backups))
|
||||
{
|
||||
return JsonConvert.DeserializeObject<List<BackupMenuItemViewModel>>(JsonConvert.SerializeObject(backups));
|
||||
}
|
||||
return new List<BackupMenuItemViewModel>();
|
||||
}
|
||||
|
||||
public static async Task ShowGMessages()
|
||||
{
|
||||
Dictionary<string, GlobalMessage[]> globalMessages = await GetGlobalMessages().ConfigureAwait(false);
|
||||
if (globalMessages.Any())
|
||||
{
|
||||
string version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
|
||||
if (globalMessages.ContainsKey(version))
|
||||
if (!string.IsNullOrEmpty(globalMessages[version][0].Message))
|
||||
foreach (GlobalMessage gm in globalMessages[version])
|
||||
FConsole.AppendText(gm.Message, gm.Color, gm.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<Dictionary<string, GlobalMessage[]>> GetGlobalMessages()
|
||||
{
|
||||
if (_data == null && CdnData.bInternet) // if data is still null after getting backups, that means there's no internet, do not try again
|
||||
_data = await CdnData.GetData().ConfigureAwait(false); // ^ will also avoid showing "No internet" notifier twice
|
||||
|
||||
if (_data != null)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<Dictionary<string, GlobalMessage[]>>(JsonConvert.SerializeObject(_data.GlobalMessages));
|
||||
}
|
||||
return new Dictionary<string, GlobalMessage[]>();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
FModel/Grabber/Paks/InstallsJson.cs
Normal file
11
FModel/Grabber/Paks/InstallsJson.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FModel.Grabber.Paks
|
||||
{
|
||||
public class InstallsJson
|
||||
{
|
||||
[JsonProperty("associated_client")]
|
||||
public Dictionary<string, string> AssociatedClient;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user