diff --git a/BuildTools/README.md b/BuildTools/README.md new file mode 100644 index 00000000..62e973c2 --- /dev/null +++ b/BuildTools/README.md @@ -0,0 +1,33 @@ +BuildTools +========== + +## Description + +This repository contains tools used for building projects stored at https://github.com/gildor2/. It contains a stripped binary set from MSys2 (bash, perl etc, +exe and dll files in bin directory) and a few custom scripts. If you already have MSys2 or Git for Windows installed and referenced in PATH environment variable, +you'd probably won't need exe and dll files from bin directory. + +A tool which should be mentioned separately is [Jom](https://wiki.qt.io/Jom) - a Microsoft nmake replacement with support for running multiple build tasks in +parallel. Originally it was a part of Qt library, but later became a separate tool. This repository uses jom to speedup Windows builds a few times, it is selected +by vc32tools automatically when available. + +## Tools + +- [vc32tools](bin/vc32tools) - a helper script which can find and launch Visual C++ compiler +- [vcfilt](bin/vcfilt) - a helper script which colorizes Visual C++ output + +## Usage + +#### vc32tools + +Basically you can run the script without arguments to see the command line. Please note that some parameters are order-dependent: if you'll specify "--64" +after "--version=...", it won't take any effect. The same applies to commands, like if you'll provide "--version" after "--make", it won't be processed. + +#### vcfilt + +It is used automatically from "--make" or "--compile" commands. If you'd like to use it with another build system, check vc32tools source to see how it +use vcfilt. + +## License + +All binary files from MSys2 directory are covered with GNU Public License. Tools represented in this repository are [public domain](https://unlicense.org/). diff --git a/BuildTools/bin/bash.exe b/BuildTools/bin/bash.exe new file mode 100644 index 00000000..cee8a2bf Binary files /dev/null and b/BuildTools/bin/bash.exe differ diff --git a/BuildTools/bin/cat.exe b/BuildTools/bin/cat.exe new file mode 100644 index 00000000..04da19bd Binary files /dev/null and b/BuildTools/bin/cat.exe differ diff --git a/BuildTools/bin/cp.exe b/BuildTools/bin/cp.exe new file mode 100644 index 00000000..3ee8426f Binary files /dev/null and b/BuildTools/bin/cp.exe differ diff --git a/BuildTools/bin/diff.exe b/BuildTools/bin/diff.exe new file mode 100644 index 00000000..40e91432 Binary files /dev/null and b/BuildTools/bin/diff.exe differ diff --git a/BuildTools/bin/find.exe b/BuildTools/bin/find.exe new file mode 100644 index 00000000..94e6ab1f Binary files /dev/null and b/BuildTools/bin/find.exe differ diff --git a/BuildTools/bin/jom.exe b/BuildTools/bin/jom.exe new file mode 100644 index 00000000..2506b4ed Binary files /dev/null and b/BuildTools/bin/jom.exe differ diff --git a/BuildTools/bin/less.exe b/BuildTools/bin/less.exe new file mode 100644 index 00000000..4ba02ee2 Binary files /dev/null and b/BuildTools/bin/less.exe differ diff --git a/BuildTools/bin/ls.exe b/BuildTools/bin/ls.exe new file mode 100644 index 00000000..2f358308 Binary files /dev/null and b/BuildTools/bin/ls.exe differ diff --git a/BuildTools/bin/mkdir.exe b/BuildTools/bin/mkdir.exe new file mode 100644 index 00000000..b472731b Binary files /dev/null and b/BuildTools/bin/mkdir.exe differ diff --git a/BuildTools/bin/msys-1.0.dll b/BuildTools/bin/msys-1.0.dll new file mode 100644 index 00000000..f2e50358 Binary files /dev/null and b/BuildTools/bin/msys-1.0.dll differ diff --git a/BuildTools/bin/msys-crypt-0.dll b/BuildTools/bin/msys-crypt-0.dll new file mode 100644 index 00000000..5c6f3cb8 Binary files /dev/null and b/BuildTools/bin/msys-crypt-0.dll differ diff --git a/BuildTools/bin/msys-iconv-2.dll b/BuildTools/bin/msys-iconv-2.dll new file mode 100644 index 00000000..622c181f Binary files /dev/null and b/BuildTools/bin/msys-iconv-2.dll differ diff --git a/BuildTools/bin/msys-intl-8.dll b/BuildTools/bin/msys-intl-8.dll new file mode 100644 index 00000000..892acba7 Binary files /dev/null and b/BuildTools/bin/msys-intl-8.dll differ diff --git a/BuildTools/bin/msys-perl5_8.dll b/BuildTools/bin/msys-perl5_8.dll new file mode 100644 index 00000000..b43f91ad Binary files /dev/null and b/BuildTools/bin/msys-perl5_8.dll differ diff --git a/BuildTools/bin/msys-regex-1.dll b/BuildTools/bin/msys-regex-1.dll new file mode 100644 index 00000000..379c6b39 Binary files /dev/null and b/BuildTools/bin/msys-regex-1.dll differ diff --git a/BuildTools/bin/msys-termcap-0.dll b/BuildTools/bin/msys-termcap-0.dll new file mode 100644 index 00000000..8f5ac266 Binary files /dev/null and b/BuildTools/bin/msys-termcap-0.dll differ diff --git a/BuildTools/bin/perl.exe b/BuildTools/bin/perl.exe new file mode 100644 index 00000000..b1e1c73f Binary files /dev/null and b/BuildTools/bin/perl.exe differ diff --git a/BuildTools/bin/rm.exe b/BuildTools/bin/rm.exe new file mode 100644 index 00000000..9f6de62a Binary files /dev/null and b/BuildTools/bin/rm.exe differ diff --git a/BuildTools/bin/rmdir.exe b/BuildTools/bin/rmdir.exe new file mode 100644 index 00000000..4f80153b Binary files /dev/null and b/BuildTools/bin/rmdir.exe differ diff --git a/BuildTools/bin/sh.exe b/BuildTools/bin/sh.exe new file mode 100644 index 00000000..cee8a2bf Binary files /dev/null and b/BuildTools/bin/sh.exe differ diff --git a/BuildTools/bin/sleep.exe b/BuildTools/bin/sleep.exe new file mode 100644 index 00000000..dfecc2d8 Binary files /dev/null and b/BuildTools/bin/sleep.exe differ diff --git a/BuildTools/bin/sort.exe b/BuildTools/bin/sort.exe new file mode 100644 index 00000000..c92bb0dd Binary files /dev/null and b/BuildTools/bin/sort.exe differ diff --git a/BuildTools/bin/tee.exe b/BuildTools/bin/tee.exe new file mode 100644 index 00000000..ba553ff4 Binary files /dev/null and b/BuildTools/bin/tee.exe differ diff --git a/BuildTools/bin/vc32tools b/BuildTools/bin/vc32tools new file mode 100644 index 00000000..4f16d666 --- /dev/null +++ b/BuildTools/bin/vc32tools @@ -0,0 +1,485 @@ +#!/bin/bash + +# Visual C++ compiler (CL) helper script. +# The main purpose of this is to locate the compiler, setup environment and execute it with provided parameters. +# It has a possibility to build makefiles using NMAKE or JOM backend, or compile (and possibly link) separate +# C/C++ files for small applications. To get list of possible options run vc32tools without arguments. + +# Created by Konstantin Nosov (Gildor) +# https://github.com/gildor2/BuildTools +# Public Domain (https://unlicense.org/) + + +# Some references for VS2017+ compiler location. +# Article: https://devblogs.microsoft.com/cppblog/finding-the-visual-c-compiler-tools-in-visual-studio-2017/. +# Vswhere does not enumerate toolsets: https://github.com/Microsoft/vswhere/issues/151 +# Useful batch files: +# "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" +# - top-level batch, calls other, like vsdevcmd.bat, core\vsdevcmd_start.bat, parse_cmd.bat +# "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\vsdevcmd\ext\vcvars.bat" +# - final batch, selected toolset version is in __VCVARS_VERSION env variable. + + +# Color constants +norm="\\033[0m" +hgl="\\033[1" +force_nmake="" + +# Usage: CheckVC [params...] +# $1 = path - typical path to Visual Studio installation, not used since VC2017 +# $2 = vc_version - classic version number +# $3 = vc_year - "year" version +# $4 = toolset - toolset version, used when multiple C++ toolsets are installed for single VS +# Output: +# $workpath - base path to VS installation +# $found_vc - 'classic' version of VS which is located +# $found_vc_year - 'year' version +# $vs_compiler - path to compiler, used for VS2017+ + +function CheckVC() +{ + platform="32 bit" + [ "$amd64" ] && platform="64 bit" + + # special case for VS2017+ + # see https://github.com/Microsoft/vswhere/wiki/Find-VC for details (PowerShell case) + if [ $3 -ge 2017 ]; then + vswhere="$progs/Microsoft Visual Studio/Installer/vswhere.exe" + [ -f "$vswhere" ] || return + version_filter="-version [$2.0,$(($2+1)).0)" + display_name=`"$vswhere" $version_filter -property displayName` + base_path=`"$vswhere" $version_filter -property installationPath` + [ "$base_path" ] || return + # get version file for specified toolset + version_file="$base_path/VC/Auxiliary/Build/Microsoft.VCToolsVersion.v$4.default.txt" + if ! [ -f "$version_file" ]; then + # get any toolset (most likely that will select toolset for current compiler) + version_file="$base_path/VC/Auxiliary/Build/Microsoft.VCToolsVersion.default.txt" + [ -f "$version_file" ] || return + fi + vs_subver=`cat "$version_file"` + vs_compiler="$base_path/VC/Tools/MSVC/$vs_subver" + [ -d "$vs_compiler" ] || return + workpath="$base_path/VC" + found_vc=$2 + found_vc_year=$3 + echo "$display_name toolset $4 ($vs_subver) [$platform] found at \"$base_path\" ..." + else + # pre-VS2017 path + # verify presence + vspath="$1" + if [ -f "$vspath/bin/nmake.exe" ]; then + if [ "$amd64" ] && ! [ -f "$vspath/bin/amd64/nmake.exe" ]; then + return + fi + workpath="$vspath" + found_vc=$2 + found_vc_year=$3 + echo "Using Visual C++ $2 ($3) [$platform] found at \"$vspath\" ..." + fi + fi +} + + +function CheckSDK() +{ + if [ "$sdkpath" ]; then + return # already found + fi + # verify presence + if [ -d "$1" ] && [ -f "$1/bin/rc.exe" ]; then +# sdkpath="/cygdrive/${1//:/}" # d:/path -> /cygdrive/d/path - CygWin +# sdkpath="/${1//:/}" # d:/path -> /d/path - MSys + sdkpath="$1" # no path replacement + echo "Found Win32 SDK at \"$1\" ..." + fi +} + + +function AddPath() +{ + # Unix has multiple items separated with ":" which is used as drive separator on Windows systems +# local p="/cygdrive/${1//:/}" # d:/path -> /cygdrive/d/path - CygWin + local p="/${1//:/}" # d:/path -> /d/path - MSys + # Add new path before previously defined ones, so for example if older path has "link.exe" (part of Git), + # we'll use one from Visual Studio (i.e. the correct one) + PATH="$p:$PATH" +} + + +# Parameters: +# $1 = version, which is either version (6, 7, ...), year (2008, 2010, ...) or "latest" +function LocateSuitableVC() +{ + local VC=( + # Format: + "$progs/microsoft visual studio/vc98" 6 0 0 # 6.0 + "$progs/microsoft visual studio .net/vc7" 7 2002 0 # 7.0 + "$progs/microsoft visual studio .net 2003/vc7" 7 2003 0 # 7.1 + "$progs/microsoft visual studio 8/vc" 8 2005 0 # 8.0 + "$progs/microsoft visual studio 9.0/vc" 9 2008 90 # 9.0 + "$progs/microsoft visual studio 10.0/vc" 10 2010 100 # 10.0 + "$progs/microsoft visual studio 11.0/vc" 11 2012 0 # 11.0 + "$progs/microsoft visual studio 12.0/vc" 12 2013 120 # 12.0 + "$progs/microsoft visual studio 14.0/vc" 14 2015 140 # 14.0 + "" 15 2017 141 # 15.0 + "" 16 2019 142 # 16.0 + "" 17 2022 143 # 17.0 + ) + + if [ "$1" == "latest" ]; then + # Find newest VC, should iterate array in reverse order + local i=${#VC[@]} + while [ $i -gt 0 ]; do + ((i=$i-4)) + local vc_path="${VC[$i]}" + local vc_num="${VC[$i+1]}" + local vc_year="${VC[$i+2]}" + local vc_tool="${VC[$i+3]}" +# echo "($vc_path) $vc_num $vc_year toolset=$vc_tool" + + CheckVC "$vc_path" $vc_num $vc_year $vc_tool + [ "$workpath" ] && break + done + else + local i=0 + toolset=0 + while [ $i -lt ${#VC[@]} ]; do + local vc_path="${VC[$i]}" + local vc_num="${VC[$i+1]}" + local vc_year="${VC[$i+2]}" + local vc_tool="${VC[$i+3]}" + ((i=$i+4)) +# echo "($vc_path) $vc_num $vc_year toolset=$vc_tool" + + if [ "$1" ]; then + # "continue" if version is not large enough + if [ "$1" -gt 2000 ]; then + # this is year-based version + [ "$1" -gt "$vc_year" ] && continue + else + [ "$1" -gt "$vc_num" ] && continue + fi + fi + [ $toolset == 0 ] && toolset=$vc_tool + + CheckVC "$vc_path" $vc_num $vc_year $toolset + [ "$workpath" ] && break + done + fi +} + + +function PrepareVC() +{ + if [ "$workpath" ]; then + return # action already performed + fi + + # find "Program Files" dir + # note: on 64-bit systems "PROGRAMFILES" will contain path to (x86) dir + if [ "$PROGRAMFILES" ]; then + progs="${PROGRAMFILES//\\//}" # get from environment with slash replacement + else + progs="c:/program files" + fi + # When running 64-bit bash on Windows, it will use 64-bit Program Files by default. However, + # Visual Studio is always located in x86 directory, so switch to using it. + if [ -d "$progs (x86)" ]; then + progs="$progs (x86)" + fi + + # ---------- Find Visual Studio ---------- + + LocateSuitableVC $vc_ver + + if [ ! "$workpath" ]; then + echo -e "${hgl};31mERROR: Visual C++ was not found.${norm}" + exit 1 + fi + + # compute path to additional dlls + case $found_vc in + 6) ide="$workpath/../common/msdev98/bin" + ;; + *) ide="$workpath/../common7/IDE" + ;; + esac + + # setup environment variables + if [ $found_vc -ge 15 ]; then + if [ "$amd64" ]; then + AddPath "$vs_compiler/bin/Hostx64/x64" + LIB="$vs_compiler/lib/x64" + else + # Note: AddPath will prepend PATH, so we're adding x64 before x86, however x86 will be first for lookup + AddPath "$vs_compiler/bin/Hostx64/x64" # pick dlls from another target's toolset for mspdbcore.dll + AddPath "$vs_compiler/bin/Hostx64/x86" + LIB="$vs_compiler/lib/x86" + fi + INCLUDE="$vs_compiler/include" + else + # pre-VS2017 paths + if [ "$amd64" ]; then + AddPath "$workpath/bin/amd64" + else + AddPath "$workpath/bin" + AddPath "$ide" + fi + INCLUDE="$workpath/INCLUDE" + INCLUDE="$INCLUDE;$workpath/MFC/INCLUDE;$workpath/PlatformSDK/Include;$workpath/PlatformSDK/Include/prerelease" #?? may be not needed to include this + #?? Again, "PlatformSDK" is below - it seems that was for VS 2003 + if [ -z "$amd64" ]; then + LIB="$workpath/LIB;$workpath/PlatformSDK/LIB" + else + LIB="$workpath/LIB/amd64;$workpath/PlatformSDK/LIB/x64" + fi + fi + + # ---------- Find Platform SDK ---------- + + CheckSDK "$progs/Microsoft SDKs/Windows/v7.1" + CheckSDK "$progs/Microsoft SDKs/Windows/v7.0A" + CheckSDK "C:/Program Files/Microsoft SDKs/Windows/v6.0A" # NOTE: not in "Program Files (x86)" + + if [ "$sdkpath" ]; then + INCLUDE="$INCLUDE;$sdkpath/Include" + if [ -z "$amd64" ]; then + LIB="$LIB;$sdkpath/Lib" + else + LIB="$LIB;$sdkpath/Lib/x64" + fi + AddPath "$sdkpath/bin" # VC9+ has no RC.exe in its bin directory, using it from the SDK + fi + + # ---------- CRT for modern C++ compilers ---------- + + if [ -z "$sdkpath" ] || [ $found_vc -ge 14 ]; then + # VS starting with 2015 version doesn't have CRT, it uses UCRT from Windows Kits/10 + win10sdk="$progs/Windows Kits/10" + if [ -d "$win10sdk" ]; then +# olddir="$PWD" +# cd "$win10sdk/Include" + win10sdk_ver="0.0.0.0" +# for d in "$win10sdk/Include/*"; do - changing dir + return to olddir will not work correctly for symlink'ed current directory + for d in `ls "$win10sdk/Include"`; do # we're using 'ls ...' here because simple "path_with_spaces/*" will not work + if [[ $d > $win10sdk_ver ]]; then + win10sdk_ver=$d + fi + done +# cd "$olddir" + INCLUDE="$INCLUDE;$win10sdk/Include/$win10sdk_ver/ucrt;$win10sdk/Include/$win10sdk_ver/um;$win10sdk/Include/$win10sdk_ver/shared" + if [ -z "$amd64" ]; then + lib_subdir=x86 + else + lib_subdir=x64 + fi + LIB="$LIB;$win10sdk/Lib/$win10sdk_ver/ucrt/$lib_subdir;$win10sdk/Lib/$win10sdk_ver/um/$lib_subdir" + # include SDK bin directory (required for rc.exe) + AddPath "$win10sdk/bin/$lib_subdir" + AddPath "$win10sdk/bin/$win10sdk_ver/$lib_subdir" + else + echo "ERROR: $win10sdk not found" + exit 1 + fi + fi + +# echo " -- PATH: $workpath : $sdkpath" +# echo " -- INC: $INCLUDE" +# echo " -- LIB: $LIB" + export INCLUDE LIB +} + + +# Generate sound event using Visual Studio settings +function VSEvent() +{ + Sound=$1 + # We're using PowerShell to access registry and play sounds. + # Note: using '&' at the end to issue sound asynchronously, so script will not wait till sound completed. + { + cat < LNK: <$linkopt>" +# linker option "-merge:.rdata=.data" will merge sections, but produce warnings + vcfilt cl -nologo -O1 -MD $cppopt $filename -link -filealign:512 $linkopt + return ${PIPESTATUS[0]} # use return instead of exit +} + + +function Link() +{ + PrepareVC + link $* +} + +function Usage() +{ +cat<] [--make ] [--check] [--help] + +Compiler secection options: + --version= minimal Visual C++ version to use (version option must go first) + --64 use 64-bit compiler (by default 32-bit compiler is used) + + is VC year number or its version. Example: For VS2015 use --version=2015 or 14. + Use "--version=latest" to use most recent version available on the system. When not specified, + the first available (i.e. oldest) compiler version will be used. + +Compiling source code: + --compile compile c/cpp source file(s) + +Extra options for compile: + file(s) to compile + --debug create pdb file + /link pass all following options directly to linker + +Executing makefile: + --make build specified makefile + --nmake force NMAKE build, do not use JOM (should go before --make) + +Calling linker: + --link link source files + +Other options: + --check verify availability of VC++ + --help display compiler help pages +EOF +} + + +# NOTE: this will try to parse command even when called via "source ", i.e. included +while [ "1" ]; do + + case "$1" in + --version=*) # specify preferred compiler version + vc_ver=${1:10} + shift + continue # process other switches + ;; + --64) + amd64=1 + shift + continue + ;; + --nmake) + force_nmake=1 + shift + continue + ;; + --compile) + shift + Compile $* + exit + ;; + --link) + shift + Link $* + exit + ;; + --make) + shift + Make $* + exit + ;; + --check) + PrepareVC + break + ;; + --help) + PrepareVC + cl -? + exit 0 + ;; + "") # no commands + Usage + exit 0 + ;; + *) + echo "Invalid vc32tools option $1" + exit 1 + ;; + esac + +done diff --git a/BuildTools/bin/vcfilt b/BuildTools/bin/vcfilt new file mode 100644 index 00000000..a0ccca5f --- /dev/null +++ b/BuildTools/bin/vcfilt @@ -0,0 +1,90 @@ +#!/usr/bin/perl -w + +# Visual C++ compiler (CL) output colorizer. +# It is intended to highlight error and warning messages from compiler to make them more noticeable. +# The script works as output filter, however it starts compiler itself, so usual use is +# `perl vcfilt `. + +# Created by Konstantin Nosov (Gildor) +# https://github.com/gildor2/BuildTools +# Public Domain (https://unlicense.org/) + +# Changes: +# 20.09.2014 +# - removed 'nice' call - probably not needed anymore (previously used to give priority to this +# filter process over compiler, so there was no lags in output) +# 31.01.2012 +# - do not use colorizing when not attached to terminal (redirected to a file) +# 14.10.2011 +# - fixed strange bug with Win7 + new CygWin - pipe were not created when arguments are quoted + + +sub color { + my ($line, $color) = @_; + return "\033[1;${color}m".$line."\033[0m"; +} + +$log = 0; +if (exists $ENV{"logfile"}) { + $log = 1; + open (LOG, ">>".$ENV{"logfile"}) or $log = 0; +} + +#setpriority(PRIO_PROCESS, $PID, -20); -- unimplemented + +# wisely combine command line, adding quotes when needed +# other (smaller) variants: +# $cmdline = "\"".join("\" \"", @ARGV)."\""; +# - this could raise to error "Can't pipe to ..." because executable name is quoted +# $cmdline = join(" ", @ARGV); +# - combine without quotes, will not process spaces at all +$cmdline = $ARGV[0]; +shift @ARGV; +foreach $arg (@ARGV) +{ + if ($arg =~ /\s/) { + $cmdline .= " \"$arg\""; + } else { + $cmdline .= " $arg"; + } +} +die "Usage: vcfilt \n" if !defined($cmdline); + + +# set priority of executed tool lower, then this filter' priority +#!!$cmdline = "nice -n 5 $cmdline"; + +# create process with piped output +#-- open(IN, "-|", "2>&1 $cmdline") or die "Can't pipe to @ARGV[0]\n"; +open(IN, "$cmdline 2>&1|") or die "Can't pipe to $ARGV[0]\n"; +$isConsole = (-t STDOUT); + +while ($line = ) +{ + if (!$isConsole) { + print($line); + print(LOG $line) if $log; + next; + } + my $line2 = $line; + if ($line =~ /Microsoft|Copyright/) { + $line2 = color ($line, 30); + } elsif ($line =~ /: fatal/) { + $line2 = color ($line, 35); + } elsif ($line =~ /: error/) { + $line2 = color ($line, 31); + } elsif ($line =~ /: warning/) { + $line2 = color ($line, 33); + } elsif ($line =~ /: attention/) { #!! temporary + $line2 = color ($line, 34); + } + print($line2); + print(LOG $line) if $log; +} +close(IN); +$ret = $?; + +close(LOG) if $log; + +# return code => out; note: code 256 could be converted to 0 (8 bit value!) so convert it to 0 or 1 +exit($ret != 0); diff --git a/Exporters/ExportMaterial.cpp b/Exporters/ExportMaterial.cpp index 527d6a26..e4325266 100644 --- a/Exporters/ExportMaterial.cpp +++ b/Exporters/ExportMaterial.cpp @@ -4,10 +4,71 @@ #include "UnObject.h" #include "UnrealMaterial/UnMaterial.h" #include "UnrealMaterial/UnMaterial3.h" - +#include "UnrealMaterial/UnMaterialExpression.h" +#include "unrealPackage/UnPackage.h" #include "Exporters.h" + +bool IsValidCString(const char* str, size_t maxLen = 1024) +{ + if (!str) return false; + + __try { + for (size_t i = 0; i < maxLen; ++i) + { + if (str[i] == '\0') return true; // found null terminator + } + return false; // too long or unterminated + } + __except (EXCEPTION_EXECUTE_HANDLER) { + return false; // access violation or bad pointer + } +} + +//Hack. get texture type by addition in texture name +FString GetTextureType(const UUnrealMaterial* Tex) +{ + struct TextureSuffix { + const char* suffix; + const char* type; + }; + + TextureSuffix suffixMap[] = + { + { "_D", "Diffuse" }, + { "_D2", "Diffuse" }, + //{ "_pack", "Diffuse"}, // ... In custom materials, but used common + { "_N", "Normal" }, + { "_MASK", "Mask" }, + { "_Cube", "Cubemap" }, + { "_crl", "Detail"}, // ??? + }; + + if (IsValidCString(Tex->Name) == false) + { + printf("Invalid texture name / pointer \n"); + return ""; + } + + const char* name = Tex->Name; + int nameLen = strlen(name); + + for (int i = 0; i < ARRAY_COUNT(suffixMap); i++) + { + const char* suffix = suffixMap[i].suffix; + int suffixLen = strlen(suffix); + + if (nameLen >= suffixLen && stricmp(name + nameLen - suffixLen, suffix) == 0) + { + printf("Type: %s for texture %s\n", suffixMap[i].type, name); + return FString(suffixMap[i].type); + } + } + + return ""; +} + void ExportMaterial(const UUnrealMaterial* Mat) { guard(ExportMaterial); @@ -29,25 +90,56 @@ void ExportMaterial(const UUnrealMaterial* Mat) } //todo: handle Mat->IsTexture(), Mat->IsTextureCube() to select exporter code - //todo: remove separate texture handling from Main.cpp exporter registraction + //todo: remove separate texture handling from Main.cpp exporter registration + + if (Mat->IsA("MaterialInstanceConstant")) + { + const UMaterialInstance* MIC = (const UMaterialInstance*)Mat; + // Access Violation 0x00000000 occurs if Parent is NULL or invalid + if (!MIC->Parent) { + appPrintf("Skipping %s: NULL Parent material detected to prevent crash.\n", *Mat->Name); + return; + } + } TArray AllTextures; + CMaterialParams Params; + printf("Will append referenced textures\n"); Mat->AppendReferencedTextures(AllTextures, false); + printf("Appended Referenced Textures\n"); - CMaterialParams Params; - Mat->GetParams(Params); - if ((Params.IsNull() || Params.Diffuse == Mat) && AllTextures.Num() == 0) + for (int i = 0; i < AllTextures.Num(); i++) { - // empty/unknown material, or material itself is a texture - appPrintf("Ignoring %s'%s' due to empty parameters\n", Mat->GetClassName(), Mat->Name); - return; + UObject* Tex = AllTextures[i]; // Use UObject* if you're dumping textures + if (Tex) + { + char FullName[256]; // Allocate a buffer + Tex->GetFullName(FullName, sizeof(FullName), true, true, true); + appPrintf("Referenced_U texture [%d]: %s\n", i, FullName); + } } + + FArchive* Ar = CreateExportArchive(Mat, EFileArchiveOptions::TextFile, "%s.mat", Mat->Name); if (!Ar) return; TArray ToExport; + if (Mat->IsA("Material3")) + { + const UMaterial3* Mat3 = static_cast(Mat); + for (int i = 0; i < Mat3->ReferencedTextures.Num(); i++) + { + UTexture3* Tex = Mat3->ReferencedTextures[i]; + if (Tex) + { + appPrintf("Exporting raw texture [%d]: %s\n", i, Tex->Name); + ToExport.AddUnique(Tex); + } + } + } + #define PROC(Arg) \ if (Params.Arg) \ { \ @@ -56,6 +148,7 @@ void ExportMaterial(const UUnrealMaterial* Mat) } PROC(Diffuse); + //PROC(DiffuseColor); PROC(Normal); PROC(Specular); PROC(SpecPower); @@ -64,47 +157,215 @@ void ExportMaterial(const UUnrealMaterial* Mat) PROC(Cube); PROC(Mask); + //Testing these: + PROC(Detail); + PROC(AO); + PROC(GlowMap); + PROC(PaintMask); + PROC(TeamColor); + PROC(ColorLookup); + PROC(DecalTexture); + PROC(TilingPattern); + PROC(HexMask); + PROC(Environment); + PROC(Reflection); + PROC(Overlay); + PROC(Noise); + PROC(Roughness); + PROC(Metallic); + // Dump material properties to a separate file FArchive* PropAr = CreateExportArchive(Mat, EFileArchiveOptions::TextFile, "%s.props.txt", Mat->Name); if (PropAr) { Mat->GetTypeinfo()->SaveProps(Mat, *PropAr); + + delete PropAr; } - // Export other textures + TArray> ExportTexMap; + + // HACK - First iterration, to gather all type-texture map. The type is assumed based on texture name, so there is no guarantee it is correct. int numOtherTextures = 0; + + printf("Processing textures start\n"); for (int i = 0; i < AllTextures.Num(); i++) { UUnrealMaterial* Tex = AllTextures[i]; - if (ToExport.FindItem(Tex) < 0) + + // 1. Strict validation + if (!Tex || !Tex->Name) continue; + + // 2. Immediate duplication to keep memory safe + const char* TexPath = appStrdup(Tex->Name); + + FString TextureType = GetTextureType(Tex); + const char* FinalParamName = NULL; + + if (TextureType.Len() > 0) + { + // 3. Duplicate IMMEDIATELY before FString can fluctuate + FinalParamName = appStrdup(*TextureType); + } + else + { + // 4. Use a larger buffer and safe formatting + char OtherBuf[128]; + appSprintf(ARRAY_ARG(OtherBuf), "Other[%d]", numOtherTextures++); + FinalParamName = appStrdup(OtherBuf); + } + + // 5. Final safety check before adding to the Map + if (FinalParamName && TexPath) { - Ar->Printf("Other[%d]=%s\n", numOtherTextures++, Tex->Name); - ToExport.Add(Tex); + ExportTexMap.Add({ FinalParamName, TexPath }); } } + printf("Processing textures done\n"); + + if (Mat->IsA("Material3")) + { + // Second iteration: export named texture parameters from material expressions and overwrite the previous list. + // There can be a mistake if few parameters are using the same texture path... + // Second iteration: export named texture parameters + const UMaterial3* Material = static_cast(Mat); + for (int i = 0; i < Material->Expressions.Num(); i++) + { + printf("Processing expression %d\n", i); + UObject* Expr = Material->Expressions[i]; + printf("Got expression pointer\n"); + if (Expr == nullptr) continue; + + // CRITICAL: Only process if it is actually a Texture Sample Parameter + if (!Expr->IsA("MaterialExpressionTextureSampleParameter")) + { + continue; + } + printf("Before cast"); + auto* TexSampleParameter = static_cast(Expr); + printf("After cast"); + + if (!TexSampleParameter->Texture) continue; + + + const char* paramName = nullptr; + const char* texPath = nullptr; + printf("Found texture sample parameter expression\n"); + auto ExpTex = TexSampleParameter->Texture; + if (ExpTex == nullptr) + continue; + if (TexSampleParameter->Texture == nullptr) + continue; + + paramName = TexSampleParameter->ParameterName ? TexSampleParameter->ParameterName : ""; + printf(" Parameter name: %s\n", paramName); + + texPath = nullptr; + if (ExpTex && IsValidCString(ExpTex->Name)) + { + printf(" Texture path: \n", ExpTex->Name); + texPath = appStrdup(ExpTex->Name); + } + else if (IsValidCString(paramName) && paramName != "None") + { + printf(" Texture is null, but parameter is: %s \n", paramName); + texPath = "None"; + } + else + { + printf(" Texture name is null\n"); + texPath = appStrdup("Unknown"); + continue; + } + + + printf(" Texture path: %s\n", texPath); + bool KeyChanged = false; + for (int x = 0; x < ExportTexMap.Num(); x++) + { + printf(" Map %d: %s=%s\n", x, ExportTexMap[x].Key, ExportTexMap[x].Value); + if (ExportTexMap[x].Key == nullptr || ExportTexMap[x].Value == nullptr || paramName == nullptr || ExpTex->Name == nullptr) + { + continue; + } + //appPrintf("texPath ptr=%p ExportTexMap[%d].Value ptr=%p\n", (void*)texPath, x, (void*)ExportTexMap[x].Value); + + printf(" Comparing %s to %s\n", ExportTexMap[x].Value, texPath); + if (!paramName || !texPath) continue; + if (strlen(paramName) > 4096 || strlen(texPath) > 4096) continue; + + //printf("%d Comparing parameter %s=%s to %s=%s\n", ExportTexMap.Num(), ExportTexMap[x].Key, ExportTexMap[x].Value, paramName, texPath); + + if (strcmp(ExportTexMap[x].Value, texPath) == 0) + { + appPrintf("Changing texture parameter %s=%s to %s=%s\n", ExportTexMap[x].Key, ExportTexMap[x].Value, paramName, texPath); + ExportTexMap[x].Key = paramName; + KeyChanged = true; + break; + } + } + + if (KeyChanged == false) + { + printf("Added %s=%s\n", paramName, texPath); + ExportTexMap.Add({ appStrdup(paramName), appStrdup(texPath) }); + } + + } + } + + printf("Exporting texture parameters to archive\n"); + for (int i = 0; i < ExportTexMap.Num(); i++) + { + printf("Adding to texture to archive\n"); + const char* safeKey = ExportTexMap[i].Key != nullptr ? ExportTexMap[i].Key : "Null"; + const char* safeValue = ExportTexMap[i].Value != nullptr ? ExportTexMap[i].Value : "Null"; // value can be still null + printf("Exporting texture parameter %s=%s\n", safeKey, safeValue); + + if (safeKey != "None") + Ar->Printf("%s=%s\n", safeKey, safeValue); + } + + printf("Adding all textures to ToExport list\n"); + for (int i = 0; i < AllTextures.Num(); i++) + { + UUnrealMaterial* ExpMat = AllTextures[i]; + if (ExpMat == NULL) continue; + ToExport.AddUnique(ExpMat); + } + + delete Ar; // close .mat file // We have done with current object, now let's export referenced objects. - for (UObject* Obj : ToExport) { if (Obj != Mat) // UTextureCube::GetParams() adds self to "Cube" field ExportObject(Obj); } + printf("Material export done: %s\n", Mat->Name); // For MaterialInstanceConstant, export its parent too if (Mat->IsA("MaterialInstanceConstant")) { + printf("Exporting parent material\n"); const UMaterialInstanceConstant* Inst = static_cast(Mat); - if (Inst->Parent) + printf("Parent material: %s\n", Inst->Parent ? Inst->Parent->Name : "NULL"); + if (Inst != nullptr && Inst->Parent) { - ExportMaterial(Inst->Parent); + //ExportMaterial(Inst->Parent); + //ToExport.AddUnique(Inst->Parent); } + printf("Parent material export done\n"); } + #endif // RENDERING unguardf("%s'%s'", Mat->GetClassName(), Mat->Name); } + + + diff --git a/TestRun.bat b/TestRun.bat new file mode 100644 index 00000000..33d0df1c --- /dev/null +++ b/TestRun.bat @@ -0,0 +1 @@ +python TestRun.py \ No newline at end of file diff --git a/TestRun.py b/TestRun.py new file mode 100644 index 00000000..6b847b8d --- /dev/null +++ b/TestRun.py @@ -0,0 +1,27 @@ +import subprocess +import os + +# Paths +umodel_path = "C:/Users/lauri/Documents/Github/UEViewer/umodel.exe" +game_path = "C:/Program Files/Epic Games/rocketleague" +package_path = os.path.join(game_path, "TAGame/CookedPCConsole/Stadium_P.upk") +output_path = "C:/Users/lauri/Desktop/RocketLeague/Automation" + +# Command +command = [ + umodel_path, + "-export", + "-game=rocketleague", + f"-path={game_path}", + f"-out={output_path}", + package_path +] + +# Run UModel +try: + subprocess.run(command, check=True) + print("UModel export completed successfully.") +except subprocess.CalledProcessError as e: + print(f"UModel failed with error code {e.returncode}") +except FileNotFoundError: + print("UModel executable not found. Check the path.") \ No newline at end of file diff --git a/Tools/genmake b/Tools/genmake index 9b44f782..8e618d15 100755 --- a/Tools/genmake +++ b/Tools/genmake @@ -568,14 +568,17 @@ sub EmitCompilerDefs { $defs2 .= " -d _WIN64 -d WIN64"; } # Compiler - print "CPP = cl.exe -nologo -c $defs1 -D _WINDOWS${winxp}\n"; + #print "CPP = cl.exe -nologo -c $defs1 -D _WINDOWS${winxp}\n"; + print "CPP = cl.exe -nologo -c /MT $defs1 -D _WINDOWS${winxp}\n"; # Linker my $machine = ($PLATFORM eq "win64") ? "X64" : "X86"; print "LINK = link.exe -nologo -filealign:512 -incremental:no -machine:$machine\n"; # Library manager print "AR = link.exe -lib -nologo\n"; # cannot use "LIB" name # Resource compiler - print "RC = rc.exe -nologo -dNDEBUG -l 0x409${winxp}${defs2}\n" + + print "RC = rc.exe -nologo -dNDEBUG -l 0x409${winxp}${defs2}\n"; + print "LIBS = ucrt.lib legacy_stdio_definitions.lib msvcrt.lib kernel32.lib user32.lib advapi32.lib shell32.lib\n"; } elsif ($COMPILER eq "GnuC") { my $platf = "gcc"; # platform override @@ -1274,6 +1277,8 @@ sub FlushTargetText { return $prefix."\t\$(AR) -out:\"$n\"$fileList"; } $line = "\t\$(LINK) -out:\"$n\""; + $line .= " \$(LIBS)"; + $line .= " /NODEFAULTLIB:LIBCMT"; $line .= GenerateOptions ($libpath, "-libpath:") if $libpath ne ""; $line .= " $stdlibs" if $stdlibs ne ""; $line .= " $libs" if $libs ne ""; diff --git a/Unreal/UnObject.cpp b/Unreal/UnObject.cpp index e0585881..3ae40d09 100644 --- a/Unreal/UnObject.cpp +++ b/Unreal/UnObject.cpp @@ -71,9 +71,16 @@ void UObject::GetFullName(char *buf, int bufSize, bool IncludeObjectName, bool I guard(UObject::GetFullName); if (!Package) { + // Simulate package name for dummy textures + if (PackageIndex == INDEX_NONE && Name) + { + appSprintf(buf, bufSize, "Texture2D'%s.%s'", "MissingPackage_U", Name); + return; + } buf[0] = 0; return; } + #if UNREAL4 if (Package != NULL && Package->Game >= GAME_UE4_BASE) { @@ -92,7 +99,24 @@ void UObject::GetFullName(char *buf, int bufSize, bool IncludeObjectName, bool I #endif // UNREAL4 if (PackageIndex == INDEX_NONE) { - // This is a dummy generated export + if (Package) + { + if (Package->GetFilename().Len() > 0) + { + appSprintf(buf, bufSize, *Package->GetFilename(), ".%s", Package->Name, Name ? Name : "Unnamed"); + return; + } + return; + } + // Handle dummy textures with missing package + if (!Package) + { + // Use fallback name if available + appSprintf(buf, bufSize, "MissingPackageZ.%s", Name ? Name : "Unnamed"); + return; + } + + // Normal dummy export with known package appSprintf(buf, bufSize, "%s.%s", Package->Name, Name); return; } diff --git a/Unreal/UnRenderer.cpp b/Unreal/UnRenderer.cpp index 49e640cb..01c3f9d7 100644 --- a/Unreal/UnRenderer.cpp +++ b/Unreal/UnRenderer.cpp @@ -1743,8 +1743,8 @@ void UUnreal3Material::GetParams(CMaterialParams &Params) const Params.Normal = Tex; else if (!stricmp(Name + len - 2, "_m")) Params.Mask = Tex; -// else -// appPrintf("Tex: %s\n", Name); + else + appPrintf("Tex: %s\n", Name); } Params.SpecularMaskChannel = TC_G; @@ -1977,6 +1977,12 @@ void UMaterial3::GetParams(CMaterialParams &Params) const Super::GetParams(Params); int DiffWeight = 0, NormWeight = 0, SpecWeight = 0, SpecPowWeight = 0, OpWeight = 0, EmWeight = 0, CubeWeight = 0; + + int DetailWeight = 0, AOWEIGHT = 0, GlowMapWeight = 0, PaintMaskWeight = 0; + int TeamColorWeight = 0, ColorLookupWeight = 0, DecalTextureWeight = 0, TilingPatternWeight = 0; + int HexMaskWeight = 0, EnvironmentWeight = 0, ReflectionWeight = 0, OverlayWeight = 0; + int NoiseWeight = 0, RoughnessWeight = 0, MetallicWeight = 0; + #define DIFFUSE(check,weight) \ if (weight > DiffWeight && check) \ { \ @@ -2040,6 +2046,110 @@ void UMaterial3::GetParams(CMaterialParams &Params) const Params.EmissiveColor = Color; \ EmcWeight = weight; \ } +#define DETAIL(check,weight) \ + if (weight > DetailWeight && check) \ + { \ + Params.Detail = Tex; \ + DetailWeight = weight; \ + } + +#define AO(check,weight) \ + if (weight > AOWEIGHT && check) \ + { \ + Params.AO = Tex; \ + AOWEIGHT = weight; \ + } + +#define GLOWMAP(check,weight) \ + if (weight > GlowMapWeight && check) \ + { \ + Params.GlowMap = Tex; \ + GlowMapWeight = weight; \ + } + +#define PAINTMASK(check,weight) \ + if (weight > PaintMaskWeight && check) \ + { \ + Params.PaintMask = Tex; \ + PaintMaskWeight = weight; \ + } + +#define TEAMCOLOR(check,weight) \ + if (weight > TeamColorWeight && check) \ + { \ + Params.TeamColor = Tex; \ + TeamColorWeight = weight; \ + } + +#define COLORLOOKUP(check,weight) \ + if (weight > ColorLookupWeight && check) \ + { \ + Params.ColorLookup = Tex; \ + ColorLookupWeight = weight; \ + } + +#define DECALTEXTURE(check,weight) \ + if (weight > DecalTextureWeight && check) \ + { \ + Params.DecalTexture = Tex; \ + DecalTextureWeight = weight; \ + } + +#define TILINGPATTERN(check,weight) \ + if (weight > TilingPatternWeight && check) \ + { \ + Params.TilingPattern = Tex; \ + TilingPatternWeight = weight; \ + } + +#define HEXMASK(check,weight) \ + if (weight > HexMaskWeight && check) \ + { \ + Params.HexMask = Tex; \ + HexMaskWeight = weight; \ + } + +#define ENVIRONMENT(check,weight) \ + if (weight > EnvironmentWeight && check) \ + { \ + Params.Environment = Tex; \ + EnvironmentWeight = weight; \ + } + +#define REFLECTION(check,weight) \ + if (weight > ReflectionWeight && check) \ + { \ + Params.Reflection = Tex; \ + ReflectionWeight = weight; \ + } + +#define OVERLAY(check,weight) \ + if (weight > OverlayWeight && check) \ + { \ + Params.Overlay = Tex; \ + OverlayWeight = weight; \ + } + +#define NOISE(check,weight) \ + if (weight > NoiseWeight && check) \ + { \ + Params.Noise = Tex; \ + NoiseWeight = weight; \ + } + +#define ROUGHNESS(check,weight) \ + if (weight > RoughnessWeight && check) \ + { \ + Params.Roughness = Tex; \ + RoughnessWeight = weight; \ + } + +#define METALLIC(check,weight) \ + if (weight > MetallicWeight && check) \ + { \ + Params.Metallic = Tex; \ + MetallicWeight = weight; \ + } int ArGame = GetGame(); @@ -2056,8 +2166,8 @@ void UMaterial3::GetParams(CMaterialParams &Params) const //!! - separate code (common for UMaterial3 + UMaterialInstanceConstant) //!! - may implement with tables + macros //!! - catch normalmap, specular and emissive textures - if (strstr(Name, "noise")) continue; - if (strstr(Name, "detail")) continue; + //if (strstr(Name, "noise")) continue; + //if (strstr(Name, "detail")) continue; DIFFUSE(strstr(Name, "diff"), 100); NORMAL (strstr(Name, "norm"), 100); @@ -2065,6 +2175,27 @@ void UMaterial3::GetParams(CMaterialParams &Params) const DIFFUSE(strstr(Name, "_tex"), 60); DIFFUSE(!strcmp(Name + len - 2, "_d"), 20); OPACITY(strstr(Name, "_om"), 20); + // + /* + * + * PROC(Detail); + PROC(AO); + PROC(GlowMap); + PROC(PaintMask); + PROC(TeamColor); + PROC(ColorLookup); + PROC(DecalTexture); + PROC(TilingPattern); + PROC(HexMask); + PROC(Environment); + PROC(Reflection); + PROC(Overlay); + PROC(Noise); + PROC(Roughness); + PROC(Metallic); + * */ + // + // // CUBEMAP(strstr(Name, "cubemap"), 100); -- bad #if 0 if (!strcmp(Name + len - 3, "_di")) // The Last Remnant ... @@ -2144,7 +2275,6 @@ void UMaterial3::AppendReferencedTextures(TArray& OutTextures, guard(UMaterial3::AppendReferencedTextures); if (onlyRendered) { - // default implementation does that Super::AppendReferencedTextures(OutTextures, onlyRendered); } else @@ -2152,12 +2282,46 @@ void UMaterial3::AppendReferencedTextures(TArray& OutTextures, for (int i = 0; i < ReferencedTextures.Num(); i++) { if (ReferencedTextures[i]) - OutTextures.AddUnique(ReferencedTextures[i]); + OutTextures.Add(ReferencedTextures[i]); + } + + for (int i = 0; i < Expressions.Num(); i++) + { + UObject* Expr = Expressions[i]; + // Standard safety: check if Expr is valid and its class matches + if (Expr && Expr->IsA("Texture3")) + { + UTexture3* Tex = static_cast(Expr); + if (Tex) + OutTextures.AddUnique(Tex); + } + } + + // LOGGING LOOP: The most likely source of the 0x00000000 crash + for (int i = 0; i < Expressions.Num(); i++) + { + UObject* Expr = Expressions[i]; + // Replace IsValidCString with basic pointer and null-terminator check + if (Expr && Expr->Name && Expr->Name[0] != '\0') + { + appPrintf("Expr[%d]: %s (%s)\n", i, Expr->Name, Expr->GetClassName()); + } + else if (Expr) + { + appPrintf("Expr[%d]: (%s)\n", i, Expr->GetClassName()); + } } + + if (ReferencedTextures.Num() == 0) + appPrintf("Warning: Material %s has no ReferencedTextures\n", Name ? Name : "Unknown"); } unguard; } + + + + void UTexture2D::SetupGL() { guard(UTexture2D::SetupGL); @@ -2511,9 +2675,9 @@ void UMaterialInstanceConstant::GetParams(CMaterialParams &Params) const } for (i = 0; i < VectorParameterValues.Num(); i++) { - const FVectorParameterValue &P = VectorParameterValues[i]; - const char *Name = P.GetName(); - const FLinearColor &Color = P.ParameterValue; + const FVectorParameterValue& P = VectorParameterValues[i]; + const char* Name = P.GetName(); + const FLinearColor& Color = P.ParameterValue; EMISSIVE_COLOR(strstr(Name, "emissive"), 100); #if TRON if (ArGame == GAME_Tron) @@ -2530,10 +2694,10 @@ void UMaterialInstanceConstant::GetParams(CMaterialParams &Params) const Params.Mask = NULL; // some different meaning for this texture if (Params.Mask) { - Params.EmissiveChannel = TC_MA; - Params.SpecularMaskChannel = TC_G; + Params.EmissiveChannel = TC_MA; + Params.SpecularMaskChannel = TC_G; Params.SpecularPowerChannel = TC_B; - Params.CubemapMaskChannel = TC_R; + Params.CubemapMaskChannel = TC_R; } } #endif // TRON @@ -2543,7 +2707,7 @@ void UMaterialInstanceConstant::GetParams(CMaterialParams &Params) const { if (Params.Mask) { - Params.SpecularMaskChannel = TC_R; + Params.SpecularMaskChannel = TC_R; Params.SpecularPowerChannel = TC_G; // TC_B = skin mask } @@ -2555,7 +2719,7 @@ void UMaterialInstanceConstant::GetParams(CMaterialParams &Params) const { if (Params.Mask) { - Params.CubemapMaskChannel = TC_B; + Params.CubemapMaskChannel = TC_B; Params.SpecularPowerChannel = TC_G; // TC_R = skin } @@ -2572,23 +2736,55 @@ void UMaterialInstanceConstant::GetParams(CMaterialParams &Params) const void UMaterialInstanceConstant::AppendReferencedTextures(TArray& OutTextures, bool onlyRendered) const { guard(UMaterialInstanceConstant::AppendReferencedTextures); + if (onlyRendered) { - // default implementation does that + printf("UMaterialInstanceConstant::AppendReferencedTextures: onlyRendered=True, calling Super\n"); Super::AppendReferencedTextures(OutTextures, onlyRendered); } else { for (int i = 0; i < TextureParameterValues.Num(); i++) { - if (TextureParameterValues[i].ParameterValue) - OutTextures.AddUnique(TextureParameterValues[i].ParameterValue); + const auto& Param = TextureParameterValues[i]; + + if (!Param.ParameterValue) + { + appPrintf("Warning: TextureParameterValues[%d] in MaterialInstanceConstant %s has null ParameterValue\n", i, Name ? Name : "Unknown"); + continue; + } + + // VITAL FIX: Ensure the parameter name pointer is not null before printing + const char* pName = (Param.ParameterName) ? Param.ParameterName : "None"; + printf("MaterialInstanceConstant %s: Processing TextureParameterValues[%d] with name '%s'\n", Name ? Name : "Unknown", i, pName); + // Additional safety: Check if the texture pointer itself is valid + UUnrealMaterial* pTex = Param.ParameterValue; + + appPrintf("Param[%d]: %s = %s\n", i, pName, pTex ? pTex->Name : ""); + + if (pTex != nullptr) + { + OutTextures.AddUnique(pTex); + } + } + printf("Pre parent check"); + + // Prevent infinite recursion if Parent == this + if (Parent != nullptr && Parent != this) + { + if (Parent->GetClassName() != nullptr) + { + if (Parent->IsA("MaterialInstanceConstant") || Parent->IsA("Material3")) + { + Parent->AppendReferencedTextures(OutTextures, onlyRendered); + } + } } - if (Parent && Parent != this) Parent->AppendReferencedTextures(OutTextures, onlyRendered); } unguard; } + #endif // UNREAL3 diff --git a/Unreal/UnrealMaterial/UnMaterial.h b/Unreal/UnrealMaterial/UnMaterial.h index 18be6aec..4c9eeb5d 100644 --- a/Unreal/UnrealMaterial/UnMaterial.h +++ b/Unreal/UnrealMaterial/UnMaterial.h @@ -37,7 +37,22 @@ struct CMaterialParams PARAM(Opacity) \ PARAM(Emissive) \ PARAM(Cube) \ - PARAM(Mask) + PARAM(Mask) \ + PARAM(Detail) \ + PARAM(AO) \ + PARAM(GlowMap) \ + PARAM(PaintMask) \ + PARAM(TeamColor) \ + PARAM(ColorLookup) \ + PARAM(DecalTexture) \ + PARAM(TilingPattern) \ + PARAM(HexMask) \ + PARAM(Environment) \ + PARAM(Reflection) \ + PARAM(Overlay) \ + PARAM(Noise) \ + PARAM(Roughness) \ + PARAM(Metallic) bool IsNull() const { @@ -64,6 +79,24 @@ struct CMaterialParams UUnrealMaterial *Emissive; UUnrealMaterial *Cube; UUnrealMaterial *Mask; // multiple mask textures baked into a single one + //New + UUnrealMaterial* Detail; + UUnrealMaterial* AO; + UUnrealMaterial* GlowMap; + UUnrealMaterial* PaintMask; + UUnrealMaterial* TeamColor; + UUnrealMaterial* ColorLookup; + UUnrealMaterial* DecalTexture; + UUnrealMaterial* TilingPattern; + UUnrealMaterial* HexMask; + UUnrealMaterial* Environment; + UUnrealMaterial* Reflection; + UUnrealMaterial* Overlay; + UUnrealMaterial* Noise; + UUnrealMaterial* Roughness; + UUnrealMaterial* Metallic; + + // channels (used with Mask texture) ETextureCannel EmissiveChannel; ETextureCannel SpecularMaskChannel; diff --git a/Unreal/UnrealMaterial/UnMaterialExpression.h b/Unreal/UnrealMaterial/UnMaterialExpression.h index eadbb117..716966c6 100644 --- a/Unreal/UnrealMaterial/UnMaterialExpression.h +++ b/Unreal/UnrealMaterial/UnMaterialExpression.h @@ -122,6 +122,9 @@ class UMaterialExpressionVectorParameter : public UMaterialExpressionParameter REGISTER_CLASS(UMaterialExpressionScalarParameter) \ REGISTER_CLASS(UMaterialExpressionStaticBoolParameter) \ REGISTER_CLASS(UMaterialExpressionStaticSwitchParameter) \ - REGISTER_CLASS(UMaterialExpressionVectorParameter) + REGISTER_CLASS(UMaterialExpressionVectorParameter) + //REGISTER_CLASS(MaterialExpressionTextureSampleParameterCube) #endif // __UNMATERIAL_EXPRESSION_H__ + +//TODDO: MaterialExpressionTextureSampleParameterCube \ No newline at end of file diff --git a/Unreal/UnrealMaterial/UnTexture3.cpp b/Unreal/UnrealMaterial/UnTexture3.cpp index f37080aa..a03e060e 100644 --- a/Unreal/UnrealMaterial/UnTexture3.cpp +++ b/Unreal/UnrealMaterial/UnTexture3.cpp @@ -1146,6 +1146,10 @@ void UMaterial3::Serialize(FArchive &Ar) #endif Super::Serialize(Ar); + //printf("UMaterial3::Serialize: %s\n", Name); + //for() + + #if UNREAL4 if (Ar.Game >= GAME_UE4_BASE) { @@ -1193,25 +1197,32 @@ void UMaterial3::Serialize(FArchive &Ar) mask: int unkMask; // default 1 Ar << unkMask; - } - if (Ar.ArVer >= 656) + } if (Ar.ArVer >= 656) { guard(SerializeFMaterialResource); // Starting with version 656 UE3 has deprecated ReferencedTextures array. // This array is serialized inside FMaterialResource which is not needed // for us in other case. // FMaterialResource serialization is below + + TArray f10; - TMap f1C; + TMap TextureMap; int f58; FGuid f60; int f80; - Ar << f10 << f1C << f58 << f60 << f80; - if (Ar.ArVer >= 656) Ar << ReferencedTextures; // that is ... - // other fields are not interesting ... + //TArray sus; + + Ar << f10 << TextureMap << f58 << f60 << f80; + Ar << ReferencedTextures; + + + unguard; } DROP_REMAINING_DATA(Ar); //?? drop native data } + + #endif // UNREAL3 diff --git a/Unreal/UnrealPackage/UnPackage.cpp b/Unreal/UnrealPackage/UnPackage.cpp index 9c7ec1b2..91e834d8 100644 --- a/Unreal/UnrealPackage/UnPackage.cpp +++ b/Unreal/UnrealPackage/UnPackage.cpp @@ -13,6 +13,23 @@ /*----------------------------------------------------------------------------- Unreal package structures -----------------------------------------------------------------------------*/ +class UMissingTexture : public UObject +{ +public: + + FString TexturePath; + + static const CTypeInfo* StaticGetTypeinfo() + { + static const CTypeInfo type("UTexture2D", NULL, sizeof(UMissingTexture), NULL, 0, TYPE_None, NULL, NULL); + return &type; + } + virtual const CTypeInfo* GetTypeinfo() const override + { + return StaticGetTypeinfo(); + } + +}; bool FPackageFileSummary::Serialize(FArchive &Ar) { @@ -1141,10 +1158,29 @@ UObject* UnPackage::CreateImport(int index) if (ObjIndex == INDEX_NONE) { appPrintf("WARNING: Import(%s.%s) was not found\n", PackageName, *Imp.ObjectName); - Imp.Missing = true; - return NULL; + //Imp.Missing = true; + UObject* Dummy = new UMissingTexture(); + + Dummy->Name = Imp.ObjectName; + Dummy->PackageIndex = INDEX_NONE; + //Dummy->Package = NULL; + + //UnPackage* FakePackage = new UnPackage("MissingPackage.upk", NULL, true); + Dummy->Package = NULL; + + // Cast to access TexturePath + UMissingTexture* MissingTex = static_cast(Dummy); + char buf[256]; + appSprintf(ARRAY_ARG(buf), "%s.%s", PackageName, *Imp.ObjectName); + MissingTex->TexturePath = buf; + + appPrintf("Creating dummy texture for metadata: %s\n", *MissingTex->TexturePath); + return MissingTex; + } } + + #endif // UNREAL3 // at this point we have either Package == NULL (not found) or Package != NULL and ObjIndex is valid @@ -1365,3 +1401,4 @@ TArray MissingPackages; unguardf("%s", *File->GetRelativeName()); } + diff --git a/build.bat b/build.bat new file mode 100644 index 00000000..494eed22 --- /dev/null +++ b/build.bat @@ -0,0 +1,49 @@ +@echo off +setlocal enabledelayedexpansion + +:: 1. SET PATHS +set "SRC=umodel_64.exe" +set "DST=C:\p4v\laurynas_RocketLeague\Plugins\ProjectPlugins\MapImporter\Source\MapImporter\UModel\umodel_64.exe" +set "PATH=C:\Users\lauri\Documents\Github\UEViewer\BuildTools\bin;%PATH%" +set "VERSION_FILE=UmodelTool\Version.h" + +:: 2. Increment version (Ultra-Safe No-Script Version) +echo Incrementing version... +for /f "tokens=3" %%A in ('type "%VERSION_FILE%"') do set /a "VER=%%A+1" +echo #define GIT_REVISION %VER% > "%VERSION_FILE%" + +:: Verify visually +type "%VERSION_FILE%" + + +:: 3. BUILD +bash build.sh --64 +if %ERRORLEVEL% NEQ 0 ( + echo Build failed, aborting. + exit /b 1 +) + +:: 4. VERIFY SRC AND COPY +:: Using quotes around everything to prevent ". was unexpected" +if not exist "%SRC%" ( + echo ERROR: Compiled file "%SRC%" not found! + exit /b 1 +) + +if exist "%DST%" ( + echo Deleting old destination... + del /F /Q "%DST%" +) + +echo Copying "%SRC%" to "%DST%"... +copy /Y "%SRC%" "%DST%" >nul + +if %ERRORLEVEL% EQU 0 ( + echo Copy succeeded. +) else ( + echo ERROR: Copy failed. Check if the file is locked by another program. + exit /b 1 +) + +endlocal +exit /b 0 diff --git a/build.sh b/build.sh index af57fd33..646687fc 100755 --- a/build.sh +++ b/build.sh @@ -238,7 +238,7 @@ EOF #------------------------------------------------------------- [ "$project" ] || SetupDefaultProject -GetBuildNumber +# GetBuildNumber DetectBuildPlatform [ "${PLATFORM:0:3}" == "vc-" ] && DetectVisualStudioVersion diff --git a/libs/includewin32/SDL2/SDL_events.h b/libs/includewin32/SDL2/SDL_events.h index 282b9fb7..b1d79556 100644 --- a/libs/includewin32/SDL2/SDL_events.h +++ b/libs/includewin32/SDL2/SDL_events.h @@ -589,7 +589,7 @@ typedef union SDL_Event this structure, and GCC will use the alignment of the largest datatype within the union, which is 8 bytes. - So... we'll add padding to force the size to be 56 bytes for both. + So... we'll add p\ force the size to be 56 bytes for both. */ Uint8 padding[56]; } SDL_Event; diff --git a/umodel.exe b/umodel.exe index 9e202fb5..616faace 100644 Binary files a/umodel.exe and b/umodel.exe differ