PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Powershell-Skript, um den Perfomance-Engpass herausfinden


Platos
2023-12-08, 21:10:11
Also ich habe hier ein powershell-skript und wenn ich im MSI Afterburner anschaue, was das so an CPU last verursacht, ist das nicht gerade viel. Also keiner der CPU-Kerne ist auch nur in der nähe von 100% und laut HWINFO ist auch die SSD nicht wirklich gross ausgelastet. 20% maximal beim lesen oder schreiben.

Ja und da frage ich mich, wie ich das bottleneck finden kann?

Monger
2023-12-08, 21:46:22
Ich versteh noch nicht so ganz... woran stellst du überhaupt fest, dass das Skript lamgsam ist?

Monger
2023-12-08, 21:55:12
https://theniceweb.com/archives/471

Erfahrungsgemäß: Powershell Profiling ist ein großer Shice. Gibt bis heute keine wirklich gute Unterstützung dafür. Aber du müsstest nen beliebigen Dotnet Profiler dranhängen können, weil Powershell ist letztlich dot net. Leider ist der Callstack oft mies zu lesen.

Platos
2023-12-08, 22:26:02
Ich versteh noch nicht so ganz... woran stellst du überhaupt fest, dass das Skript lamgsam ist?

Ich habe nie gesagt, dass es langsam ist. Ich habe nur gesagt, dass weder die CPU noch die SSD vollständig ausgelastet sind (also bei der CPU ist auch kein einzige Kern ganz ausgelastet.).

Und es könnte halt schneller sein. Die Dateien, die ich durchsucht habe, sind halt bisschen viel. Da gehts halt paar Minuten...

Und daher Frage ich mich, welcher teil der Hardware ist der Flaschenhals.

Also das skript ist halt (für mich) ziemlich komplex. 25k zeichen und allein eine foreach schleife ist etwa 18k lang.

optimieren ist da echt schwer und ich kenne mich halt eig. eh nicht aus. Habe alles mithilfe von chatGPT geschrieben. Und wenn ich den frage, was perfomance spart, sagt er mir im einen atemzug x und im nächsten y.

Also ich will mir halt stundenlanges optimieren an cpu-lastigen szenarien sparen, wenn ich dann am ende merke, dass die ssd der flaschenhals ist... weisst du, was ich meine? Ich muss zuerst mal wissen, wo der flaschenhals ist (weil ich mich mit code nicht genug auskenne, so dass ich sagen können, "das ist das problem"). abgesehen davon weiss ich sowieso nicht wirklich, was ich optimieren sollte. also ist das sowieso schwierig. aber so könnte ich wenigstens etwas gezielter suchen.

Edit:

Also es ist hald so:

ich weiss (wegen der fortschrittsanzeige), dass die 2. foreach schleife langsam ist. Ich habe aber schon einige optimierungen vorgenommen, die haben aber praktisch nix geholfen. Also beispielsweise prüfe ich die auflösung von dateien, d.h ich sortiere sie in bestimmte kategorien ein. Vermutlich etwa 80% passen nicht in diese kategorie, deswegen habe ich eine if-anweisung eingeführt, die das gegenteil prüft. wenn das zutrifft (was in 80% der fällen sein sollte), überspringt es jegliche anderen prüfungen dieser art. das mache ich mit einer eingerückten else-klammer.

Als ich das gemacht habe, war der code genau gleich schnell in der 2. schleife.

Was ähnliches habe ich wo anders gemacht, wo ich auf bestiimte Buchstaben im Dateiname prüfe. Da habe ich mit so variablen gearbeitet, die auf true gesetzt wird und die anderen prüfungen werden nur durchgeführt, wenn die variable auf falsr ist (die wird dann am anfang jeder iteration auf false gesetzt).

Auch das hat nichts gebracht. Also keine sekunde. Ich stoppe das (geht immer so ca. 18 sekunden das skript bei einem test).

Und dann habe ich noch die prüfungen so angeordnet, dass die wahrscheinlichsten ganz oben sind. auch das hat nix gebracht.

Daher glaube ich, dass ich irgendwo massive flaschenhälse habe.

Ich habe dann mit solchen measure-commands die zeit versucht zu messen, die ein bestimmter codeblock innerhalb der schleife benötigt, aber das habe ich (und chatgpt) nicht hingekriegt. Bei meiner ersten, kleinen schleife ging das noch. Aber bei der zweiten nicht. da habe ich immer fehlermeldungen wegen else-bedingungen gekriegt. Ich habe die klammer aber richtig gesetzt bei diesen measure-codeblocks (man sieht die ja aufleuchten bei notepad++). ich glaube, die powershell hat wegen den klammern den measure-commands immer das gefühl, dass die else klammer fehlerhaft ist.

naja...

Kompliziert. Ich könnte natürlich den code hier posten, aber ist natürlich 25k zeichen lang, also... xD

Ps: Ja, 18sekunden sind natürlich nicht viel, aber das ist nur ein test mit wenigen dateien. wie gesagt, wenns mehr sind, gehts dann hald minuten.

aber eben... ich verstehe nicht, wo die ganze zeit flöten geht, nachdem ich die 2. schleife so optimiert habe (scheinbar).

#44
2023-12-09, 09:53:49
Dein Optimierungsversuch ist etwas, was man "premature optimization" nennt - voreilig. Du hast versucht durch Intution eine Abkürzung zu finden.
Das funktioniert nicht besonders gut. Man muss das System (hier: .Net Runtime und alles darunter) schon sehr gut verstehen, um so zu optimieren.
Wie du ja selbst festgestellt hast, sollte Optimierung auf Messung basieren. Das nur mal als Kontext.

Ich vermute stark, dass dein Skript nicht parallelisiert ist und du ein CPU-Bottleneck hast, welches dank dem Scheduler nicht klar über die Prozessorauslastung als solches erkennbar ist. (Vmtl. CPU Gesamtauslastung irgendwo knapp über 100/Anzahl Cores)

Schritt 1 wäre also dein Skript in der Powershell ISE (Entwicklungsumgebung für PS - sollte installiert sein; ein Terminal tut es aber auch) zu öffnen, die ISE über den Taskmanager fix einem Kern zuzuordnen und dann bei einem Lauf die Auslastung zu prüfen.

€: Ok, noch ein bisschen mehr Kontext: Compiler optimieren Code typischerweise. Nun ist .Net aber eine Laufzeitumgebung - die kann da sogar noch etwas mehr. Laufzeitumgebungen können zur Laufzeit deinen Code analysieren und optimieren. Darum (so meine Vermutung) bringt das manuelle sortieren deiner Statements nach Wahrscheinlichkeit sehr wenig. Denn das passiert im Hintergrund auch mit dem nicht von dir "optimierten" Code.
Wir hatten in diesem Unterforum mal einen Thread darüber, dass C(++) als lowlevel kompilierte Sprache ja so viel schneller wäre als bspw. Java mit seiner Laufzeit. Da kam es dann zu der kuriosen Situation, dass ein Java-Programm auf einem x64 Rechner am Ende am schnellsten war. Vmtl. weil es im Gegensatz zu x86 kompiliiertem C++ die zusätzlichen Register von x64 nutzen konnte.
Wie Eingangs gesagt: Optimierung braucht viel Wissen. Messen ist idr. einfacher. Dann weiß man auch, ob man etwas kaputt "optimiert" hat.

Monger
2023-12-09, 10:17:45
Ich habe nie gesagt, dass es langsam ist. Ich habe nur gesagt, dass weder die CPU noch die SSD vollständig ausgelastet sind (also bei der CPU ist auch kein einzige Kern ganz ausgelastet.).

Es gibt ja noch ganz andere Engpässe. Zugriffszeit, zum Beispiel. Viele kleine Dateien zugreifen dauert länger als eine große. Das merkst du aber an der SSD Auslastung kaum. Es gibt auch Windows-bedingte Limits bei I/O- und Thread-Handles, und noch hundert andere Indikatoren.

Aber ehrlich gesagt der dickste Hinweis auf ein Problem ist mMn dass du ein 25.000 Zeichen Skript (!!!) laufen lässt. Was zur Hölle macht das? Ich hab jahrelang Powershell für Arbeit geschrieben, unsere ganze Build Umgebung ist darin geschrieben, und hat trotzdem keine 25.000 Zeichen.

Monger
2023-12-09, 10:27:35
Powershell hat z.B. auch innere Limits. Große Arrays sind langsam, insbesondere wenn man über sie iteriert. Powershell räumt z.B. auch normalerweise zwischendrin keine Objekte ab. Das macht es, sobald die Session schließt.
Powershell arbeitet ja sehr viel mit Streams, da können sich alle möglichen Objekte ansammeln die irgendwann geflushed werden müssen.
Kurzum: Powershell ist keine auf Performance getrimmte Sprache. Sie dient vor allem als Glue Code zwischen C#, F# und Betriebssystem.

#44
2023-12-09, 10:41:12
Aber ehrlich gesagt der dickste Hinweis auf ein Problem ist mMn dass du ein 25.000 Zeichen Skript (!!!) laufen lässt. Was zur Hölle macht das?
Ich muss zugeben, dass ich da auch sehr neugierig wäre :biggrin:

PatkIllA
2023-12-09, 10:44:01
Läuft denn der SkriptTeil überhaupt durch den JIT und Optimierer? Das lohnt doch bei einem typischen Skript, was von oben nach unten durchläuft gar nicht.

Es gibt extra Profiler die anzeigen wieviel Prozent wo auf der Strecke bleiben. Das dann zu optimieren wird aber schwierig, wenn der Code von ChatGPT zusammengeschrieben wurde.
Wahrscheinlich wird es einfacher, wenn das Skript in Funktionen zerlegt wird und nciht eine Riesen Schleife ist

25k Zeichen oder Zeilen? Länge von Code gibt man üblicherweise in Zeilen an.

Dennis50300
2023-12-09, 10:52:45
Die eigentlich viel interessantere Frage ist doch, was soll der Skript eigentlich machen ?
Vielleicht gibt es das ja schon, in besser ?
Weil dann muss man nix neu schreiben und schon garnicht von ChatGPT, hat das mal tatsächlich jemand benutzt wie Strunzendämlich das eigentlich ist ?
Das ist zwar in der Laage dir benutzt wie Google, Ergebnisse zu liefern, diese zuvor natürlich zu finden, aber einfach zu blöde dir zum Ergebnis wo es das her hat eine klickbare URL zu liefern.

Also mal ehrlich, sowas lasse ich nicht Programmieren, der "Buffer Overflow" ist doch vorprogrammiert xD

Gruss

Cubitus
2023-12-09, 10:53:48
Powershell hat z.B. auch innere Limits. Große Arrays sind langsam, insbesondere wenn man über sie iteriert. Powershell räumt z.B. auch normalerweise zwischendrin keine Objekte ab. Das macht es, sobald die Session schließt.
Powershell arbeitet ja sehr viel mit Streams, da können sich alle möglichen Objekte ansammeln die irgendwann geflushed werden müssen.
Kurzum: Powershell ist keine auf Performance getrimmte Sprache. Sie dient vor allem als Glue Code zwischen C#, F# und Betriebssystem.

Genau bzw. ist eher Mittel zum zweck, gerade im administrativen Bereich geht halt mit PowerShell immer mehr als wie mit der Gui.

Aber mal ein größeres Powershell Script übers AD oder über den Sharepoint laufen zu lassen, ist mittel verheerend..

#44
2023-12-09, 10:57:30
Läuft denn der SkriptTeil überhaupt durch den JIT und Optimierer? Das lohnt doch bei einem typischen Skript, was von oben nach unten durchläuft gar nicht.
So weit ich das verstehe - grundsätzlich ja.

https://learn.microsoft.com/en-us/powershell/scripting/dev-cross-plat/performance/script-authoring-considerations?view=powershell-7.4#avoid-write-host
Der Artikel scheint mir auch ganz grundsätzlich für das Topic relevant.

Platos
2023-12-09, 13:37:42
Also danke schonmal für eure Hilfe. Ich kann gerne mal (versuchen) das Skript hier als Spoiler anzufügen. Aber bitte: Konstruktive Kritik. Sowas wie "Boah ist das aber schlecht" kann ich nicht gebrauchen gell ^^

Also, habe 4 Versionen des Skrtips in den zwei anderen Antworten hochgeladen. Danke schonmal für etwaige HIlfe

Die Funktion des Skripts:

Vor dem Skript wird noch ein anderes Tool genutzt, das ist wichtig für die Erklärung. Es geht hier um das Game Dragons Dogma und das (sog.) ARCtool entpackt dann einen ganzen Ordner mit all seinen Unterordner. D.h die .arc Files werden entpackt und dann die darin befindlichen .text files zu .dds umgewandelt. Für jede .tex file wird eine .txt file erstellt. Da drinn stehen informationen, nach dem mein skript die .dds Dateien sortiert.

Mein Skript sucht nun in einem Verzeichnis (nachdem das ARCtool alles entpackt hat) nach .txt files und durchsucht den inhalt der .txt files. Es sucht nach bestimmten Zeichenfolgen. Wenn diese vorhanden sind, weiss ich, dass es eine zugehörige .dds Datei gibt (gleicher Namen, gleicher Unterordner).

Dann werden die zugehörigen .dds Dateien untersucht, um sie zu sortieren. Sie werden untersucht auf Merkmale und dann in einem outputFolder in eine bestimmte Ordnerstruktur kopiert. Diese Struktur wird hald quasi anhand dieser "Untersuchungen" gebildet. Die Untersuchungen lassen sich alle in 2 Gruppen einteilen: Dateinamen-Untersuchung (Beispiel: Beinhaltet der Dateiname irgendwo "NM") und Untersuchung, die auf der Texturauflösung basiert (die ich aus der zugehörigen .txt datei bei "Width=" bzw. "Height=" auslese). Dann wird geprüft (errechnet??) ob, die Datei grösser oder kleiner als ne bestimmte Auflösung ist.

Am Ende wird die Datei dann noch auf Duplikate geprüft und DANN wird die Datei kopiert und dann wird in einer csv Datei der Pfad der .dds Datei vor und nach dem Kopieren vermerkt. Das dient dazu, dass ich später mit einem Skript 2 alles wieder zurück kopieren kann.

Zur den 25k Zeichen:
Das SKript hat sehr, sehr viel debugging (Das ich natürlich abgestellt habe bei meinen Zeitmessungen). Es besteht verm. zu 50% aus debugging. Das kommt daher, dass ich am Anfang dieses vorhabens wirklich genau NULL Kenntis hatte (und jetzt habe ich 0.1 Kenntnis). Also ich hätte nicht eine kleinste Zeile code schreiben können (und jetzt auch nru wenig). Deswegen habe ich ChatGPT in Worten gesagt, was ich will und er hat es mir dann meistens falsch umgesetzt. Da ich ja absolut nichts vom Code verstanden habe am Anfang, habe ich mir versucht immer jede Codezeile erklären zu lassen usw, aber er kann kein Kontext vermitteln (und versteht es auch nicht). Daher habe ich irgendwann herausgefunden, was debugging ist und dann habe ich mir von ihm bei Problemen zu allem möglichen Debugging-Funktionen einbauen lassen.

So: Bevor jetzt aber jemand kommt und sagt, das verursacht die ganzen Probleme: Ich habe dann irgendwann alles Debugging mit so $debug variablen am Anfang des skriptes definiert und die sind somit jetzt immer auf false gesetzt. Sollten also keine Perfomance fressen.

So und mittlerweile habe ich auch herausgefunden, dass diese += operationen scheisse sind. Genau so, wie eig. so ziemlich alle operationen, die ich so nutze :D
Daher mein Verlauf:

Zuerst hatte ich Arrays. ChatGPT hat mir dann so "generic Listen" empfohlen, da das viel besser sein soll. Irgendwann hat er dann aber gesagt, dass Hashtabellen bei vielen Daten viel besser seien, da Listen immer neu erstellt werden müssen bei nem neuen eintrag (oder so ähnlich). Das merke ich zwar auch, das Skript wird immer langsämer, je länger es läuft. Aber es ist auch schon so langsam.

Naja, ich kann jetzt nicht mein ganzer verbesserungsverlauf beschreiben, sonst gehts ein leben lang. Ich werde einfach in dieser oder einer zusätzlich antwort versuchen das skript zu posten.

Dein Optimierungsversuch ist etwas, was man "premature optimization" nennt - voreilig. Du hast versucht durch Intution eine Abkürzung zu finden.
Das funktioniert nicht besonders gut. Man muss das System (hier: .Net Runtime und alles darunter) schon sehr gut verstehen, um so zu optimieren.
Wie du ja selbst festgestellt hast, sollte Optimierung auf Messung basieren. Das nur mal als Kontext.

Ich vermute stark, dass dein Skript nicht parallelisiert ist und du ein CPU-Bottleneck hast, welches dank dem Scheduler nicht klar über die Prozessorauslastung als solches erkennbar ist. (Vmtl. CPU Gesamtauslastung irgendwo knapp über 100/Anzahl Cores)

Schritt 1 wäre also dein Skript in der Powershell ISE (Entwicklungsumgebung für PS - sollte installiert sein; ein Terminal tut es aber auch) zu öffnen, die ISE über den Taskmanager fix einem Kern zuzuordnen und dann bei einem Lauf die Auslastung zu prüfen.

€: Ok, noch ein bisschen mehr Kontext: Compiler optimieren Code typischerweise. Nun ist .Net aber eine Laufzeitumgebung - die kann da sogar noch etwas mehr. Laufzeitumgebungen können zur Laufzeit deinen Code analysieren und optimieren. Darum (so meine Vermutung) bringt das manuelle sortieren deiner Statements nach Wahrscheinlichkeit sehr wenig. Denn das passiert im Hintergrund auch mit dem nicht von dir "optimierten" Code.
Wir hatten in diesem Unterforum mal einen Thread darüber, dass C(++) als lowlevel kompilierte Sprache ja so viel schneller wäre als bspw. Java mit seiner Laufzeit. Da kam es dann zu der kuriosen Situation, dass ein Java-Programm auf einem x64 Rechner am Ende am schnellsten war. Vmtl. weil es im Gegensatz zu x86 kompiliiertem C++ die zusätzlichen Register von x64 nutzen konnte.
Wie Eingangs gesagt: Optimierung braucht viel Wissen. Messen ist idr. einfacher. Dann weiß man auch, ob man etwas kaputt "optimiert" hat.

Ja, ich habe nichts parallelisiert. Ich hatte mal die idee, aber es war irgendwie so komplex, ich habs dann aufgegeebn. Ich habe es sogar mit "nur" der ersten foreach schleife probiert (die ziemlich klein ist). aber auch das hat nicht funktioniert.

Und das ding ist: Das skript ist bewusst mit powershell geschrieben. Der Grund dafür ist, dass es ohne jegliche installation von irgendetwas laufen soll (also das skript). D.h funktionen, bei denen man irgendwelche pakete in der powershell isntallieren muss (oder gar powershell 7 nutzen muss), sind hier ein no-go. Sonst könnte ich gleich ein phyton skript nutzen (was ich nicht will, wie gesagt).

aber eben: Ich habs probiert und aufgegeben, da ich nicht weiter gekommen bin. Ich hab das mit so JobA und JobB versucht.



Es gibt ja noch ganz andere Engpässe. Zugriffszeit, zum Beispiel. Viele kleine Dateien zugreifen dauert länger als eine große. Das merkst du aber an der SSD Auslastung kaum. Es gibt auch Windows-bedingte Limits bei I/O- und Thread-Handles, und noch hundert andere Indikatoren.

Aber ehrlich gesagt der dickste Hinweis auf ein Problem ist mMn dass du ein 25.000 Zeichen Skript (!!!) laufen lässt. Was zur Hölle macht das? Ich hab jahrelang Powershell für Arbeit geschrieben, unsere ganze Build Umgebung ist darin geschrieben, und hat trotzdem keine 25.000 Zeichen.

Ja, also das kann schon gut möglich sein. Das Skript durchsucht texturfiles und die sind halt einzeln betrachtet klein und es gibt viele. Je nach Ordner durchaus mal 10 tausend.

Powershell hat z.B. auch innere Limits. Große Arrays sind langsam, insbesondere wenn man über sie iteriert. Powershell räumt z.B. auch normalerweise zwischendrin keine Objekte ab. Das macht es, sobald die Session schließt.
Powershell arbeitet ja sehr viel mit Streams, da können sich alle möglichen Objekte ansammeln die irgendwann geflushed werden müssen.
Kurzum: Powershell ist keine auf Performance getrimmte Sprache. Sie dient vor allem als Glue Code zwischen C#, F# und Betriebssystem.

Ja, ich nutze viele Arrays oder nutzte, wie auch immer. Das war eine der sogeannten "voreiligen Optimierungen". ChatGPT hat mir dann so generische Listen empfoheln. Hat nichts gebracht.

Die eigentlich viel interessantere Frage ist doch, was soll der Skript eigentlich machen ?
Vielleicht gibt es das ja schon, in besser ?
Weil dann muss man nix neu schreiben und schon garnicht von ChatGPT, hat das mal tatsächlich jemand benutzt wie Strunzendämlich das eigentlich ist ?
Das ist zwar in der Laage dir benutzt wie Google, Ergebnisse zu liefern, diese zuvor natürlich zu finden, aber einfach zu blöde dir zum Ergebnis wo es das her hat eine klickbare URL zu liefern.

Also mal ehrlich, sowas lasse ich nicht Programmieren, der "Buffer Overflow" ist doch vorprogrammiert xD

Gruss

Ja, CHatGPT ist strohdumm, deswegen habe ich ja auch so viel Zeit mit Debugging verbracht. Dennoch hätte ich ohne das "Katalogwissen" von ChatGPT nichts dergleichen machen können, da mir schlicht programmierkenntnise fehlen. Code erklären lassen (also wenigstens eine einzige Zeile), kann er aber noch relativ gut. Er checkt aber den Kontext nie.

Platos
2023-12-09, 13:41:13
Hier ist Version 14 meines Skripts:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = @()
$ddsFormatFiles = @()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles += @{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
}
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds") -Recurse
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$godFatherFolder += "_UltraLowRes"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$godFatherFolder += "_color"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Prüfen, ob die Breite und die Höhe größer als 2048 sind und gleich sind
if ([int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height) {
$resolution = "_HyperHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}

# Prüfen, ob die Breite größer als 2048 ist und die Höhe kleiner oder gleich 2048 ist
elseif ([int]$width -gt 2048 -and [int]$height -le 2048) {
$resolution = "_HyperHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Höhe größer als 2048 ist und die Breite kleiner oder gleich 2048 ist
elseif ([int]$height -gt 2048 -and [int]$width -le 2048) {
$resolution = "_HyperHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}


# Prüfen, ob die Breite und die Höhe größer als 1536 und kleiner oder gleich 2048 sind und gleich sind
if ([int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height) {
$resolution = "_UltraHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}

# Prüfen, ob die Breite größer als 1536 und kleiner oder gleich 2048 ist und die Höhe kleiner oder gleich 1536 ist
elseif ([int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536) {
$resolution = "_UltraHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Höhe größer als 1536 und kleiner oder gleich 2048 ist und die Breite kleiner oder gleich 1536 ist
elseif ([int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536) {
$resolution = "_UltraHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}

# Prüfen, ob die Breite und die Höhe genau 1536 sind
elseif ([int]$width -eq 1536 -and [int]$height -eq 1536) {
$resolution = "_SuperHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Breite genau 1536 ist und die Höhe kleiner als 1536 ist
elseif ([int]$width -eq 1536 -and [int]$height -lt 1536) {
$resolution = "_SuperHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Höhe genau 1536 ist und die Breite kleiner als 1536 ist
elseif ([int]$height -eq 1536 -and [int]$width -lt 1536) {
$resolution = "_SuperHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Breite und die Höhe größer als 1024 und kleiner als 1536 sind und gleich sind
elseif ([int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height) {
$resolution = "_HighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Breite größer als 1024 und kleiner als 1536 ist und die Höhe kleiner oder gleich 1024 ist
elseif ([int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024) {
$resolution = "_HighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Höhe größer als 1024 und kleiner als 1536 ist und die Breite kleiner oder gleich 1024 ist
elseif ([int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024) {
$resolution = "_HighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}
# Prüfen, ob die Breite und die Höhe kleiner oder gleich 64 sind, aber mindestens einer der Werte größer als 16 ist
elseif ([int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16)) {
$resolution = "_LowRes"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Prüfen, ob der Dateiname der .dds-Datei "DM" enthält
if ($ddsFile.Name -cmatch "DM.*dds$") {
# Prüfen, ob $parentFolder auf "NM" endet und ersetzen Sie es durch "DM", wenn es zutrifft
if ($parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
}
}

# Prüfen, ob der Dateiname der .dds-Datei "LM" enthält
if ($ddsFile.Name -cmatch "LM.*dds$") {
$fileNameCheck = "_LM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), LM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "AM" enthält
if ($ddsFile.Name -cmatch "AM.*dds$") {
$fileNameCheck = "_AM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), AM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "BM" enthält
if ($ddsFile.Name -cmatch "BM.*dds$") {
$fileNameCheck = "_BM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), BM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "HM" enthält
if ($ddsFile.Name -cmatch "HM.*dds$") {
$fileNameCheck = "_HM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), HM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "TM" enthält
if ($ddsFile.Name -cmatch "TM.*dds$") {
$fileNameCheck = "_TM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), TM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "CMM" enthält
if ($ddsFile.Name -cmatch "CMM.*dds$") {
$fileNameCheck = "_CMM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), CMM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}
# Prüfen, ob der Dateiname der .dds-Datei "CM" (aber nicht CMM) enthält
if ($ddsFile.Name -cmatch "_CM([^M].*|)\\.dds$") {
$fileNameCheck = "_CM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), CM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "GM" enthält
if ($ddsFile.Name -cmatch "GM.*dds$") {
$fileNameCheck = "_GM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), GM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "MM" (aber nicht CMM) enthält
if ($ddsFile.Name -cmatch "_MM.*dds$") {
$fileNameCheck = "_MM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), MM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}

# Prüfen, ob der Dateiname der .dds-Datei "NUKI" enthält
if ($ddsFile.Name -cmatch "NUKI.*dds$") {
$fileNameCheck = "_NUKI"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), NUKI gefunden, neuer fileNameCheck: ${fileNameCheck}" }
}
}

# Erstellen Sie den GodFatherFolder im Ausgabeordner
if (-not (Test-Path $godFatherFolder)) {
$testPathResult = Test-Path $godFatherFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${godFatherFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $godFatherFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${godFatherFolder}: ${newItemResult}" }
}

# Erstellen Sie den GrandParentFolder im Ausgabeordner
if (-not (Test-Path $grandParentFolder)) {
$testPathResult = Test-Path $grandParentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${grandParentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $grandParentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${grandParentFolder}: ${newItemResult}" }
}

# Erstellen Sie den ParentFolder im Ausgabeordner
$parentFolder += $resolution
if (-not (Test-Path $parentFolder)) {
$testPathResult = Test-Path $parentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${parentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $parentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${parentFolder}: ${newItemResult}" }
}

# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name)) {
if ($destinationFolder -match "_\d+$") {
$destinationFolder = $destinationFolder -replace "_\d+$", "_$i"
} else {
$destinationFolder = $destinationFolder + "_$i"
}
$i++
}

if (-not (Test-Path $destinationFolder)) {
$testPathResult = Test-Path $destinationFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${destinationFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${destinationFolder}: ${newItemResult}" }
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder
if ($debug2) { $output2 += "`nCopying file: $($ddsFile.FullName) to ${destinationFolder}" } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping += [PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
}
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}




if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

Hier Version 15.1:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = @()
$ddsFormatFiles = @()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles += @{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
}
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$conditionMet = $false
$fileNameCheckMet = $false
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds") -Recurse
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$godFatherFolder += "_UltraLowRes"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$godFatherFolder += "_color"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie eine Variable, um zu überprüfen, ob eine Bedingung erfüllt wurde
$conditionMet = $false

# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16)) {
$resolution = "_LowRes"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height) {
$resolution = "_UltraHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536) {
$resolution = "_UltraHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536) {
$resolution = "_UltraHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Beginnen Sie mit der Prüfung
if ([int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height) {
$resolution = "_HyperHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 2048 -and [int]$height -le 2048) {
$resolution = "_HyperHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 2048 -and [int]$width -le 2048) {
$resolution = "_HyperHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height) {
$resolution = "_HighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024) {
$resolution = "_HighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024) {
$resolution = "_HighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -eq 1536 -and [int]$height -eq 1536) {
$resolution = "_SuperHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -eq 1536 -and [int]$height -lt 1536) {
$resolution = "_SuperHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -eq 1536 -and [int]$width -lt 1536) {
$resolution = "_SuperHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie eine Variable, um zu überprüfen, ob eine Bedingung erfüllt wurde
$fileNameCheckMet = $false

# Prüfen, ob der Dateiname der .dds-Datei "BM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "BM.*dds$") {
$fileNameCheck = "_BM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), BM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_CM([^M].*|)\\.dds$") {
$fileNameCheck = "_CM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), CM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "GM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "GM.*dds$") {
$fileNameCheck = "_GM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), GM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "NUKI" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "NUKI.*dds$") {
$fileNameCheck = "_NUKI"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), NUKI gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CMM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "CMM.*dds$") {
$fileNameCheck = "_CMM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), CMM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "MM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_MM.*dds$") {
$fileNameCheck = "_MM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), MM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "AM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "AM.*dds$") {
$fileNameCheck = "_AM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), AM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "LM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "LM.*dds$") {
$fileNameCheck = "_LM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), LM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "TM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "TM.*dds$") {
$fileNameCheck = "_TM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), TM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "HM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "HM.*dds$") {
$fileNameCheck = "_HM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), HM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "DM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "DM.*dds$") {
# Prüfen, ob $parentFolder auf "NM" endet und ersetzen Sie es durch "DM", wenn es zutrifft
if ($parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
}
$fileNameCheckMet = $true
}

}

# Erstellen Sie den GodFatherFolder im Ausgabeordner
if (-not (Test-Path $godFatherFolder)) {
$testPathResult = Test-Path $godFatherFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${godFatherFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $godFatherFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${godFatherFolder}: ${newItemResult}" }
}


# Erstellen Sie den GrandParentFolder im Ausgabeordner
if (-not (Test-Path $grandParentFolder)) {
$testPathResult = Test-Path $grandParentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${grandParentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $grandParentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${grandParentFolder}: ${newItemResult}" }
}

# Erstellen Sie den ParentFolder im Ausgabeordner
$parentFolder += $resolution
if (-not (Test-Path $parentFolder)) {
$testPathResult = Test-Path $parentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${parentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $parentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${parentFolder}: ${newItemResult}" }
}

# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name)) {
if ($destinationFolder -match "_\d+$") {
$destinationFolder = $destinationFolder -replace "_\d+$", "_$i"
} else {
$destinationFolder = $destinationFolder + "_$i"
}
$i++
}

if (-not (Test-Path $destinationFolder)) {
$testPathResult = Test-Path $destinationFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${destinationFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${destinationFolder}: ${newItemResult}" }
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder
if ($debug2) { $output2 += "`nCopying file: $($ddsFile.FullName) to ${destinationFolder}" } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping += [PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
}
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}




if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

ich habe jetzt herausgefunden, dass es hier ein zeichenlimit von irgendwas mit 70k gibt :D

also in der nächste nAntwort der rest

Platos
2023-12-09, 13:43:41
Hier ist version 15.2:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $false # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = @()
$ddsFormatFiles = @()

# Zeigen Sie eine Nachricht an, bevor Sie Get-ChildItem aufrufen
Write-Host "Running process, please wait..."
$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Erstellen Sie eine generische Liste anstelle eines Arrays
$ddsFormatFiles = New-Object System.Collections.Generic.List[object]

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
# Fügen Sie das Element zur Liste hinzu
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}


# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$conditionMet = $false
$fileNameCheckMet = $false
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds") -Recurse
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$godFatherFolder += "_UltraLowRes"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$godFatherFolder += "_color"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie eine Variable, um zu überprüfen, ob eine Bedingung erfüllt wurde
$conditionMet = $false

#Prüfen, ob keine der Sonderauflösungen zutreffen wird
if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
} else {

# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16)) {
$resolution = "_LowRes"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height) {
$resolution = "_UltraHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536) {
$resolution = "_UltraHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536) {
$resolution = "_UltraHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Beginnen Sie mit der Prüfung
if ([int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height) {
$resolution = "_HyperHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 2048 -and [int]$height -le 2048) {
$resolution = "_HyperHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 2048 -and [int]$width -le 2048) {
$resolution = "_HyperHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height) {
$resolution = "_HighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024) {
$resolution = "_HighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024) {
$resolution = "_HighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -eq 1536 -and [int]$height -eq 1536) {
$resolution = "_SuperHighResSquare"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -eq 1536 -and [int]$height -lt 1536) {
$resolution = "_SuperHighResWide"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -eq 1536 -and [int]$width -lt 1536) {
$resolution = "_SuperHighResTall"
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
}
}
# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie eine Variable, um zu überprüfen, ob eine Bedingung erfüllt wurde
$fileNameCheckMet = $false

# Prüfen, ob der Dateiname der .dds-Datei "BM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "BM.*dds$") {
$fileNameCheck = "_BM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), BM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_CM([^M].*|)\\.dds$") {
$fileNameCheck = "_CM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), CM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "GM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "GM.*dds$") {
$fileNameCheck = "_GM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), GM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "NUKI" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "NUKI.*dds$") {
$fileNameCheck = "_NUKI"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), NUKI gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CMM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "CMM.*dds$") {
$fileNameCheck = "_CMM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), CMM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "MM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_MM.*dds$") {
$fileNameCheck = "_MM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), MM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "AM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "AM.*dds$") {
$fileNameCheck = "_AM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), AM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "LM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "LM.*dds$") {
$fileNameCheck = "_LM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), LM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "TM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "TM.*dds$") {
$fileNameCheck = "_TM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), TM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "HM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "HM.*dds$") {
$fileNameCheck = "_HM"
$parentFolder += $fileNameCheck
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), HM gefunden, neuer fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "DM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "DM.*dds$") {
# Prüfen, ob $parentFolder auf "NM" endet und ersetzen Sie es durch "DM", wenn es zutrifft
if ($parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
}
$fileNameCheckMet = $true
}

}

# Erstellen Sie den GodFatherFolder im Ausgabeordner
if (-not (Test-Path $godFatherFolder)) {
$testPathResult = Test-Path $godFatherFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${godFatherFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $godFatherFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${godFatherFolder}: ${newItemResult}" }
}


# Erstellen Sie den GrandParentFolder im Ausgabeordner
if (-not (Test-Path $grandParentFolder)) {
$testPathResult = Test-Path $grandParentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${grandParentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $grandParentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${grandParentFolder}: ${newItemResult}" }
}

# Erstellen Sie den ParentFolder im Ausgabeordner
$parentFolder += $resolution
if (-not (Test-Path $parentFolder)) {
$testPathResult = Test-Path $parentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${parentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $parentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${parentFolder}: ${newItemResult}" }
}

# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name)) {
if ($destinationFolder -match "_\d+$") {
$destinationFolder = $destinationFolder -replace "_\d+$", "_$i"
} else {
$destinationFolder = $destinationFolder + "_$i"
}
$i++
}

if (-not (Test-Path $destinationFolder)) {
$testPathResult = Test-Path $destinationFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${destinationFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${destinationFolder}: ${newItemResult}" }
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder
if ($debug2) { $output2 += "`nCopying file: $($ddsFile.FullName) to ${destinationFolder}" } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping += [PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
}
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}




if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

und hier Version 15.3:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = New-Object System.Text.StringBuilder
$output2 = New-Object System.Text.StringBuilder
$output3 = New-Object System.Text.StringBuilder

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = New-Object System.Collections.Generic.List[object]
$ddsFormatFiles = New-Object System.Collections.Generic.List[object]

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { [void]$output2.Append("`nÜberprüfe Datei: $($file.FullName)") } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$conditionMet = $false
$fileNameCheckMet = $false
$resolution = New-Object System.Text.StringBuilder
$fileNameCheck = New-Object System.Text.StringBuilder
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds") -Recurse
if ($ddsFile) {
if ($debug2) { [void]$output2.Append("`nFound .dds file: $($ddsFile.FullName)") } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = New-Object System.Text.StringBuilder (Join-Path -Path $outputFolder -ChildPath $result)
if ($debug2) { [void]$output2.Append("`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}") }

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = New-Object System.Text.StringBuilder (Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf))

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = New-Object System.Text.StringBuilder (Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf))

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
[void]$godFatherFolder.Append("_UltraLowRes")
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) {
[void]$output2.Append("`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}")
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
[void]$godFatherFolder.Append("_color")
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) { [void]$output2.Append("`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}") }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
[void]$godFatherFolder.Append("_NM")
if ($debug2) { [void]$output2.Append("`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}") }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
[void]$godFatherFolder.Append("_NM")
if ($debug2) { [void]$output2.Append("`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}") }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = New-Object System.Text.StringBuilder (Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf))

#Prüfen, ob keine der Sonderauflösungen zutreffen wird
if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
} else {

# Definieren Sie eine Variable, um zu überprüfen, ob eine Bedingung erfüllt wurde
$conditionMet = $false

# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16)) {
[void]$resolution.Append("_LowRes")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height) {
[void]$resolution.Append("_UltraHighResSquare")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536) {
[void]$resolution.Append("_UltraHighResWide")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 2048) {
[void]$resolution.Append("_UltraHighResTall")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Beginnen Sie mit der Prüfung
if ([int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height) {
[void]$resolution.Append("_HyperHighResSquare")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 2048 -and [int]$height -le 2048) {
[void]$resolution.Append("_HyperHighResWide")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 2048 -and [int]$width -le 2048) {
[void]$resolution.Append("_HyperHighResTall")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height) {
[void]$resolution.Append("_HighResSquare")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024) {
[void]$resolution.Append("_HighResWide")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024) {
[void]$resolution.Append("_HighResTall")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
}
# Prüfen, ob der Dateiname der .dds-Datei "BM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "BM.*dds$") {
[void]$fileNameCheck.Append("_BM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), BM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_CM([^M].*|)\\.dds$") {
[void]$fileNameCheck.Append("_CM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), CM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "GM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "GM.*dds$") {
[void]$fileNameCheck.Append("_GM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), GM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "NUKI" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "NUKI.*dds$") {
[void]$fileNameCheck.Append("_NUKI")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), NUKI gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CMM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "CMM.*dds$") {
[void]$fileNameCheck.Append("_CMM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), CMM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "MM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_MM.*dds$") {
[void]$fileNameCheck.Append("_MM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), MM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "AM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "AM.*dds$") {
[void]$fileNameCheck.Append("_AM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), AM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "LM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "LM.*dds$") {
[void]$fileNameCheck.Append("_LM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), LM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "TM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "TM.*dds$") {
[void]$fileNameCheck.Append("_TM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), TM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "HM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "HM.*dds$") {
[void]$fileNameCheck.Append("_HM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), HM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "DM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "DM.*dds$") {
# Prüfen, ob $parentFolder auf "NM" endet und ersetzen Sie es durch "DM", wenn es zutrifft
if ($parentFolder.ToString() -match "NM$") {
$parentFolder = New-Object System.Text.StringBuilder ($parentFolder.ToString() -replace "NM$", "DM")
if ($debug2) { [void]$output2.Append("`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}") }
}
$fileNameCheckMet = $true
}
}

# Erstellen Sie den GodFatherFolder im Ausgabeordner
if (-not (Test-Path $godFatherFolder.ToString())) {
$testPathResult = Test-Path $godFatherFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${godFatherFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $godFatherFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${godFatherFolder}: ${newItemResult}") }
}


# Erstellen Sie den GrandParentFolder im Ausgabeordner
if (-not (Test-Path $grandParentFolder.ToString())) {
$testPathResult = Test-Path $grandParentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${grandParentFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $grandParentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${grandParentFolder}: ${newItemResult}") }
}

# Erstellen Sie den ParentFolder im Ausgabeordner
[void]$parentFolder.Append($resolution)
if (-not (Test-Path $parentFolder.ToString())) {
$testPathResult = Test-Path $parentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${parentFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $parentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${parentFolder}: ${newItemResult}") }
}

# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = New-Object System.Text.StringBuilder (Join-Path -Path $parentFolder.ToString() -ChildPath (Split-Path $parentFolder.ToString() -Leaf))

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder.ToString() -ChildPath $ddsFile.Name)) {
if ($destinationFolder.ToString() -match "_\d+$") {
$destinationFolder = New-Object System.Text.StringBuilder ($destinationFolder.ToString() -replace "_\d+$", "_$i")
} else {
[void]$destinationFolder.Append("_$i")
}
$i++
}

if (-not (Test-Path $destinationFolder.ToString())) {
$testPathResult = Test-Path $destinationFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${destinationFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $destinationFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${destinationFolder}: ${newItemResult}") }
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder.ToString()
if ($debug2) { [void]$output2.Append("`nCopying file: $($ddsFile.FullName) to ${destinationFolder}") } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder.ToString() -ChildPath $ddsFile.Name

$mapping.Add(@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { [void]$output2.Append("`n`$mapping has $(($mapping.Count)) items after adding a new item") }
} else {
if ($debug2) { [void]$output2.Append("`nKeine .dds-Datei gefunden für $($file.FullName)") }
}
}

if ($debug2) {
[void]$output2.Append("`n`$mapping has $(($mapping.Count)) items before Export-Csv")
[void]$output2.Append("`n`$csvPath is: $csvPath")
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { [void]$output2.Append("`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)") }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { [void]$output2.Append("`n`$result is: $result") }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
[void]$output1.Append("`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden.")
[void]$output1.Append("`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert.")
[void]$output1.Append("`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden.")
[void]$output1.Append("`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden.")
}

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

if ($debug2) {
[void]$output2.Append("`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert.")
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
[void]$output2.Append("`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden.")
[void]$output2.Append("`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden.")
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
[void]$output3.Append("`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden.")
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2.ToString()
Write-Host $output1.ToString()
Write-Host $output3.ToString()

# Am Ende des Skripts
Stop-Transcript


warum poste ich jetzt so viele Versionen? Weil ich mir nicht sicher bin, ob ich alles kaputt optimiert habe xD

Weil Skript 14 ist quasi von den Funktionen her (glaube ich) das aktuelle. 15.1-15.3 sind einfach optimierungsversuche.

Also ihr wolltest sehen ,was das skript ist :D

da ist es :D

myMind
2023-12-09, 14:55:46
Eine sehr einfache Verbesserung ist das Verwenden einer Log-Funktion / eines Loggers. So etwas:
[void]$output2.Append(...
ist ungünstig. Benötigt bei viel Textausgabe viel Speicher. Du siehst das Ergebnis erst nachdem alles abgelaufen ist und nicht währenddessen. Außerdem muss der vom StringBuffer belegte Speicherblock immer wieder neu in der Größe angepasst werden.

Stattdessen eine Logfunktion einbauen, die die Texte direkt ausgibt und einen Zeitstempel vorhängt. Z.B.
$((Get-Date).tostring('ddMMyyyy HH:mm:ss'))

So in der Art
https://gist.github.com/markwragg/f3ac7bd6098dec485e8f11c1fd056fad

Durch die Zeitstempel bekommst du schon einen Eindruck, wo die Zeiten liegenbleiben.

Monger
2023-12-09, 15:35:36
Uff. Was ein Klotz. Aber ich sehe ne Reihe von Get-ChildItem und Test-Item Aufrufe. Und natürlich Get-Content. Ist alles naturgemäß langsam. Muss man das alles in Schleife machen?

Monger
2023-12-09, 15:55:52
Stattdessen eine Logfunktion einbauen, die die Texte direkt ausgibt und einen Zeitstempel vorhängt. Z.B.
$((Get-Date).tostring('ddMMyyyy HH:mm:ss'))

Das sprengt jetzt den Rahmen des Themas, aber: Powershell kennt ja sechs verschiedene Streams für diagnostische Zwecke, in die man sehr einfach schreiben kann: Write-Debug, Write-Progress, Write-Error etc.

Bis auf Output schreiben die alle asynchron. Man kommt von innen an diese Streams schlecht ran, und Powershell kann Asynchronität nicht gut.

Aber es ist verblüffend einfach, sich ne eigene Powershell.exe zu machen. Session öffnen, Streams abonnieren, Messages mit Zeitstempel o.ä. dekorieren und loggen, fertig.
Es gibt nur leider dafür (unverständlicherweise) kaum fertige Lösungen.

Platos
2023-12-09, 18:02:17
Eine sehr einfache Verbesserung ist das Verwenden einer Log-Funktion / eines Loggers. So etwas:

ist ungünstig. Benötigt bei viel Textausgabe viel Speicher. Du siehst das Ergebnis erst nachdem alles abgelaufen ist und nicht währenddessen. Außerdem muss der vom StringBuffer belegte Speicherblock immer wieder neu in der Größe angepasst werden.

Stattdessen eine Logfunktion einbauen, die die Texte direkt ausgibt und einen Zeitstempel vorhängt. Z.B.
$((Get-Date).tostring('ddMMyyyy HH:mm:ss'))

So in der Art
https://gist.github.com/markwragg/f3ac7bd6098dec485e8f11c1fd056fad

Durch die Zeitstempel bekommst du schon einen Eindruck, wo die Zeiten liegenbleiben.

Aber das ist ja genau das unwichtigste, denn die ganzen debugging-ausgaben von $debug2 sind ja sowieso ausgeschalten per default. Das nutze ich nur, wenn ich fehlermeldung kriege (und vlt. lösche ich am Ende $debug und alles entsprechende Debugging ohnehin aus dem Code). Es geht ja um den Grundteil des skripts. Wenn debugging lange geht, ist das eben so... Aber es geht mir ja um die Geschwindigkeit ohne debugging.

Bzw. gehts ja darum, wie ich das herausfinden kann. Also wo genau es hackt. Also welcher teil im code verursacht wie viel Zeit (über alle iterationen hinweg).

Wie gesagt, habe ich das versucht mit measure-commmands zu machen, aber das hat nur bei der kleinen Schleife funktioniert. Bei der grossen hatte ich dann fehlermeldungen. Ich glaube, irgendwelche else-Klammern wurden dann von Powershell falsch interpretiert oder so.

Kennt sich da jemand aus ?



Uff. Was ein Klotz. Aber ich sehe ne Reihe von Get-ChildItem und Test-Item Aufrufe. Und natürlich Get-Content. Ist alles naturgemäß langsam. Muss man das alles in Schleife machen?

Naja, es soll für jede .dds Datei eben all diese dinge geprüft werden in der zweiten schleife. Und dann muss der pfad gespeichert werden. Dafür muss man alle pfade abrufen. Der Sinn des ganze nist ja:

Ordner nach Dateien durchsuchen -> Auf bestimmte Art und weise Sortieren und dadurch eine bestimmte Ordnerstruktur im Zielordner erstellen --> Dateien kopieren und der Pfad vorher und nachher in einer csv schreiben (und verknüpfen), so dass ich die ganzen kopierten Dateien später (Tage, woche) wieder an den exakt gleichen Ursprungsort kopieren kann.

Ob ich dafür genau das verwenden muss, weiss ich nicht. Wie gesagt, ich kenne mich mit Programmieren nicht aus. Deswegen frage ich ja hier.

myMind
2023-12-09, 19:34:43
Aber das ist ja genau das unwichtigste, denn die ganzen debugging-ausgaben von $debug2 sind ja sowieso ausgeschalten per default. Das nutze ich nur, wenn ich fehlermeldung kriege. Es geht ja um den Grundteil des skripts. Wenn debugging lange geht, ist das eben so... Aber es geht mir ja um die Geschwindigkeit ohne debugging.
Logging != Debugging

Wenn du so willst, dann hast du aktuell kein vernünftiges Logging. Dann wäre meine Empfehlung: Bau ein gescheites Logging ein. Das liefert dir die aller ersten Hinweise wo die Zeiten liegen bleiben.
Bzw. gehts ja darum, wie ich das herausfinden kann. Also wo genau es hackt. Also welcher teil im code verursacht wie viel Zeit (über alle iterationen hinweg).
Wenn man mit den Hausmitteln (Logzeitstempel) nicht weiterkommt, kann man es mit einem Profiler probieren.

Auf die schnelle sowas z.B. https://github.com/nohwnd/Profiler. Ohne das jetzt ausprobiert zu haben.

Platos
2023-12-09, 19:52:31
Logging != Debugging

Wenn du so willst, dann hast du aktuell kein vernünftiges Logging. Dann wäre meine Empfehlung: Bau ein gescheites Logging ein. Das liefert dir die aller ersten Hinweise wo die Zeiten liegen bleiben.

Das wollte ich doch. Das ist ja quasi der Titel meiner Frage. Aber es hat nicht geklappt. Aber das das anscheinend nicht so einfach ist, poste ich hier mal meinen nicht funktionsfähigen Code. Vlt. kann mir ja jemand sagen, warum das nicht funktioniert ?

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = New-Object System.Text.StringBuilder
$output2 = New-Object System.Text.StringBuilder
$output3 = New-Object System.Text.StringBuilder

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = New-Object System.Collections.Generic.List[object]
$ddsFormatFiles = New-Object System.Collections.Generic.List[object]

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Start der Gesamtzeitmessung für die gesamte Schleife
$totalTime = Measure-Command {

$totalTime1 = 0 # Variable zur Speicherung der Gesamtzeit für den ersten Codeabschnitt
$totalTime2 = 0 # Variable zur Speicherung der Gesamtzeit für den zweiten Codeabschnitt

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { [void]$output2.Append("`nÜberprüfe Datei: $($file.FullName)") } # Debugging-Ausgabe

# Start der Zeitmessung für den ersten Codeabschnitt
$time1 = Measure-Command {
$result = Search-DDSFormat -filePath $file.FullName
}
# Aktualisierung der Gesamtzeit für den ersten Codeabschnitt
$totalTime1 += $time1.TotalMilliseconds

if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}

# Start der Zeitmessung für den zweiten Codeabschnitt
$time2 = Measure-Command {
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}
# Aktualisierung der Gesamtzeit für den zweiten Codeabschnitt
$totalTime2 += $time2.TotalMilliseconds
}
}

$totalTime3 = 0 # Variable zur Speicherung der Gesamtzeit für den ersten Codeabschnitt
$totalTime4 = 0 # Variable zur Speicherung der Gesamtzeit für den zweiten Codeabschnitt
$totalTime5 = 0 # Variable zur Speicherung der Gesamtzeit für den ersten Codeabschnitt
$totalTime6 = 0 # Variable zur Speicherung der Gesamtzeit für den zweiten Codeabschnitt

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$conditionMet = $false
$fileNameCheckMet = $false
$resolution = New-Object System.Text.StringBuilder
$fileNameCheck = New-Object System.Text.StringBuilder
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']

# Start der Zeitmessung für den ersten Codeabschnitt
$time3 = Measure-Command {
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds") -Recurse
}
# Aktualisierung der Gesamtzeit für den ersten Codeabschnitt
$totalTime3 += $time3.TotalMilliseconds

if ($ddsFile) {
# Start der Zeitmessung für den zweiten Codeabschnitt
$time4 = Measure-Command {
if ($debug2) { [void]$output2.Append("`nFound .dds file: $($ddsFile.FullName)") } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = New-Object System.Text.StringBuilder (Join-Path -Path $outputFolder -ChildPath $result)
if ($debug2) { [void]$output2.Append("`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}") }

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = New-Object System.Text.StringBuilder (Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf))

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = New-Object System.Text.StringBuilder (Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf))
}
# Aktualisierung der Gesamtzeit für den zweiten Codeabschnitt
$totalTime4 += $time4.TotalMilliseconds

# Start der Zeitmessung für den dritten Codeabschnitt
$time5 = Measure-Command {
# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
[void]$godFatherFolder.Append("_UltraLowRes")
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) {
[void]$output2.Append("`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}")
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
[void]$godFatherFolder.Append("_color")
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) { [void]$output2.Append("`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}") }
}
}
# Aktualisierung der Gesamtzeit für den dritten Codeabschnitt
$totalTime5 += $time5.TotalMilliseconds

# Start der Zeitmessung für den vierten Codeabschnitt
$time6 = Measure-Command {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
[void]$godFatherFolder.Append("_NM")
if ($debug2) { [void]$output2.Append("`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}") }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
[void]$godFatherFolder.Append("_NM")
if ($debug2) { [void]$output2.Append("`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}") }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = New-Object System.Text.StringBuilder (Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf))
}
# Aktualisierung der Gesamtzeit für den vierten Codeabschnitt
$totalTime6 += $time6.TotalMilliseconds

#Prüfen, ob keine der Sonderauflösungen zutreffen wird
if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
} else {

# Definieren Sie eine Variable, um zu überprüfen, ob eine Bedingung erfüllt wurde
$conditionMet = $false

# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16)) {
[void]$resolution.Append("_LowRes")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height) {
[void]$resolution.Append("_UltraHighResSquare")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536) {
[void]$resolution.Append("_UltraHighResWide")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 2048) {
[void]$resolution.Append("_UltraHighResTall")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Beginnen Sie mit der Prüfung
if ([int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height) {
[void]$resolution.Append("_HyperHighResSquare")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 2048 -and [int]$height -le 2048) {
[void]$resolution.Append("_HyperHighResWide")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 2048 -and [int]$width -le 2048) {
[void]$resolution.Append("_HyperHighResTall")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height) {
[void]$resolution.Append("_HighResSquare")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024) {
[void]$resolution.Append("_HighResWide")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
# Führen Sie die nächste Prüfung nur durch, wenn die vorherige Bedingung nicht erfüllt wurde
if ($conditionMet -eq $false -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024) {
[void]$resolution.Append("_HighResTall")
[void]$grandParentFolder.Append($resolution)
if ($debug2) {
[void]$output2.Append("`nWidth: ${width}")
[void]$output2.Append("`nHeight: ${height}")
[void]$output2.Append("`nResolution: ${resolution}")
}
$conditionMet = $true
}
}
# Prüfen, ob der Dateiname der .dds-Datei "BM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "BM.*dds$") {
[void]$fileNameCheck.Append("_BM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), BM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_CM([^M].*|)\\.dds$") {
[void]$fileNameCheck.Append("_CM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), CM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "GM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "GM.*dds$") {
[void]$fileNameCheck.Append("_GM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), GM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "NUKI" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "NUKI.*dds$") {
[void]$fileNameCheck.Append("_NUKI")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), NUKI gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "CMM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "CMM.*dds$") {
[void]$fileNameCheck.Append("_CMM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), CMM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "MM" (aber nicht CMM) enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "_MM.*dds$") {
[void]$fileNameCheck.Append("_MM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), MM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "AM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "AM.*dds$") {
[void]$fileNameCheck.Append("_AM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), AM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "LM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "LM.*dds$") {
[void]$fileNameCheck.Append("_LM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), LM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "TM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "TM.*dds$") {
[void]$fileNameCheck.Append("_TM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), TM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "HM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "HM.*dds$") {
[void]$fileNameCheck.Append("_HM")
[void]$parentFolder.Append($fileNameCheck)
if ($debug2) { [void]$output2.Append("`nDateiname: $($ddsFile.Name), HM gefunden, neuer fileNameCheck: ${fileNameCheck}") }
$fileNameCheckMet = $true
}
# Prüfen, ob der Dateiname der .dds-Datei "DM" enthält
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch "DM.*dds$") {
# Prüfen, ob $parentFolder auf "NM" endet und ersetzen Sie es durch "DM", wenn es zutrifft
if ($parentFolder.ToString() -match "NM$") {
$parentFolder = New-Object System.Text.StringBuilder ($parentFolder.ToString() -replace "NM$", "DM")
if ($debug2) { [void]$output2.Append("`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}") }
}
$fileNameCheckMet = $true
}
}

# Erstellen Sie den GodFatherFolder im Ausgabeordner
if (-not (Test-Path $godFatherFolder.ToString())) {
$testPathResult = Test-Path $godFatherFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${godFatherFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $godFatherFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${godFatherFolder}: ${newItemResult}") }
}


# Erstellen Sie den GrandParentFolder im Ausgabeordner
if (-not (Test-Path $grandParentFolder.ToString())) {
$testPathResult = Test-Path $grandParentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${grandParentFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $grandParentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${grandParentFolder}: ${newItemResult}") }
}

# Erstellen Sie den ParentFolder im Ausgabeordner
[void]$parentFolder.Append($resolution)
if (-not (Test-Path $parentFolder.ToString())) {
$testPathResult = Test-Path $parentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${parentFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $parentFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${parentFolder}: ${newItemResult}") }
}

# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = New-Object System.Text.StringBuilder (Join-Path -Path $parentFolder.ToString() -ChildPath (Split-Path $parentFolder.ToString() -Leaf))

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder.ToString() -ChildPath $ddsFile.Name)) {
if ($destinationFolder.ToString() -match "_\d+$") {
$destinationFolder = New-Object System.Text.StringBuilder ($destinationFolder.ToString() -replace "_\d+$", "_$i")
} else {
[void]$destinationFolder.Append("_$i")
}
$i++
}

if (-not (Test-Path $destinationFolder.ToString())) {
$testPathResult = Test-Path $destinationFolder.ToString()
if ($debug2) { [void]$output2.Append("`nTest-Path Ergebnis für ${destinationFolder}: ${testPathResult}") }

$newItemResult = New-Item -ItemType Directory -Path $destinationFolder.ToString()
if ($debug2) { [void]$output2.Append("`nNew-Item Ergebnis für ${destinationFolder}: ${newItemResult}") }
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder.ToString()
if ($debug2) { [void]$output2.Append("`nCopying file: $($ddsFile.FullName) to ${destinationFolder}") } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder.ToString() -ChildPath $ddsFile.Name

$mapping.Add(@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { [void]$output2.Append("`n`$mapping has $(($mapping.Count)) items after adding a new item") }
} else {
if ($debug2) { [void]$output2.Append("`nKeine .dds-Datei gefunden für $($file.FullName)") }
}
}

if ($debug2) {
[void]$output2.Append("`n`$mapping has $(($mapping.Count)) items before Export-Csv")
[void]$output2.Append("`n`$csvPath is: $csvPath")
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { [void]$output2.Append("`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)") }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { [void]$output2.Append("`n`$result is: $result") }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
[void]$output1.Append("`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden.")
[void]$output1.Append("`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert.")
[void]$output1.Append("`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden.")
[void]$output1.Append("`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden.")
}

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

if ($debug2) {
[void]$output2.Append("`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert.")
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
[void]$output2.Append("`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden.")
[void]$output2.Append("`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden.")
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
[void]$output3.Append("`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden.")
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2.ToString()
Write-Host $output1.ToString()
Write-Host $output3.ToString()
Write-Host "Gesamtzeit 1: $($totalTime1 / 1000) Sekunden"
Write-Host "Gesamtzeit 2: $($totalTime2 / 1000) Sekunden"
# Ausgabe der Gesamtzeit für die gesamte 1. Schleife
Write-Host "Gesamtzeit für die gesamte Schleife: $($totalTime.TotalSeconds) Sekunden"
Write-Host "Gesamtzeit 3: $($totalTime3 / 1000) Sekunden"
Write-Host "Gesamtzeit 4: $($totalTime4 / 1000) Sekunden"
Write-Host "Gesamtzeit 5: $($totalTime5 / 1000) Sekunden"
Write-Host "Gesamtzeit 6: $($totalTime6 / 1000) Sekunden"

# Am Ende des Skripts
Stop-Transcript


Die Powershell gibt mir dann sowas aus:

else : Die Benennung "else" wurde nicht als Name eines Cmdlet, einer Funktion, einer Skriptdatei oder eines
ausführbaren Programms erkannt. Überprüfen Sie die Schreibweise des Namens, oder ob der Pfad korrekt ist (sofern
enthalten), und wiederholen Sie den Vorgang.
In Zeile:340 Zeichen:7
+ } else {
+ ~~~~
+ CategoryInfo : ObjectNotFound: (else:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

PS C:\Windows\system32> }
In Zeile:1 Zeichen:1
+ }
+ ~
Unerwartetes Token "}" in Ausdruck oder Anweisung.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken

PS C:\Windows\system32>
PS C:\Windows\system32> if ($debug2) {
>> [void]$output2.Append("`n`$mapping has $(($mapping.Count)) items before Export-Csv")
>> [void]$output2.Append("`n`$csvPath is: $csvPath")
>> }
PS C:\Windows\system32>
PS C:\Windows\system32> # Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
PS C:\Windows\system32> try {
>> $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
>> } catch {
>> if ($debug2) { [void]$output2.Append("`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)") }
>> }
PS C:\Windows\system32>
PS C:\Windows\system32> # Ausgabe des Ergebnisses des `Set-Content`-Befehls
PS C:\Windows\system32> $result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
PS C:\Windows\system32> if ($debug2) { [void]$output2.Append("`n`$result is: $result") }
PS C:\Windows\system32>
PS C:\Windows\system32> # Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
PS C:\Windows\system32> if ($debug1) {
>> [void]$output1.Append("`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden.")
>> [void]$output1.Append("`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert.")
>> [void]$output1.Append("`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden.")
>> [void]$output1.Append("`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden.")
>> }
PS C:\Windows\system32>
PS C:\Windows\system32> # Nachdem alle Kopiervorgänge abgeschlossen sind
PS C:\Windows\system32> $specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
PS C:\Windows\system32> $totalCopiedDuplicates = 0
PS C:\Windows\system32>
PS C:\Windows\system32> foreach ($folder in $specialFolders) {
>> $totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
>> }
PS C:\Windows\system32>
PS C:\Windows\system32> if ($debug2) {
>> [void]$output2.Append("`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert.")
>> }
PS C:\Windows\system32>
PS C:\Windows\system32> # Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
PS C:\Windows\system32> if ($debug2) {
>> [void]$output2.Append("`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden.")
>> [void]$output2.Append("`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden.")
>> }
PS C:\Windows\system32>
PS C:\Windows\system32> # Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
PS C:\Windows\system32> if ($debug3) {
>> $ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
>> $totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
>> [void]$output3.Append("`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden.")
>> }
PS C:\Windows\system32>
PS C:\Windows\system32> # Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
PS C:\Windows\system32> Write-Host $output2.ToString()

PS C:\Windows\system32> Write-Host $output1.ToString()

Es wurden insgesamt 2639 .dds-Dateien gefunden.
Es wurden insgesamt 2639 .dds-Dateien kopiert.
Es wurden insgesamt 2639 .txt-Dateien mit 'DDSFormat=' gefunden.
Es wurden insgesamt 2639 .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden.
PS C:\Windows\system32> Write-Host $output3.ToString()

Es wurden insgesamt 2639 .dds-Dateien im Ausgabeordner gefunden.
PS C:\Windows\system32> Write-Host "Gesamtzeit 1: $($totalTime1 / 1000) Sekunden"
Gesamtzeit 1: 3.7473771 Sekunden
PS C:\Windows\system32> Write-Host "Gesamtzeit 2: $($totalTime2 / 1000) Sekunden"
Gesamtzeit 2: 0.0494113000000001 Sekunden
PS C:\Windows\system32> # Ausgabe der Gesamtzeit für die gesamte 1. Schleife
PS C:\Windows\system32> Write-Host "Gesamtzeit für die gesamte Schleife: $($totalTime.TotalSeconds) Sekunden"
Gesamtzeit für die gesamte Schleife: 4.1038402 Sekunden
PS C:\Windows\system32> Write-Host "Gesamtzeit 3: $($totalTime3 / 1000) Sekunden"
Gesamtzeit 3: 2.28144870000001 Sekunden
PS C:\Windows\system32> Write-Host "Gesamtzeit 4: $($totalTime4 / 1000) Sekunden"
Gesamtzeit 4: 0.906903000000001 Sekunden
PS C:\Windows\system32> Write-Host "Gesamtzeit 5: $($totalTime5 / 1000) Sekunden"
Gesamtzeit 5: 0.0637269 Sekunden
PS C:\Windows\system32> Write-Host "Gesamtzeit 6: $($totalTime6 / 1000) Sekunden"
Gesamtzeit 6: 0.3507583 Sekunden
PS C:\Windows\system32>
PS C:\Windows\system32> # Am Ende des Skripts
PS C:\Windows\system32> Stop-Transcript
Die Aufzeichnung wurde beendet. Die Ausgabedatei ist "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt".
PS C:\Windows\system32>

Das etwas nervige ist, diese Angabe von "In Zeile: 340 Zeichen7:" Ich verstehe nicht, auf was sich das bezieht. Es bezieht sich nicht auf mein Skript und wenn ich die logfile ansehe, dann ist es auch nicht Zeile 340. Ich habe also kurz gesagt keine Ahnung, welche Zeile da gemeint ist.

Edit: Wenn ich mir das genauer ansehe, ist die Zeitmessung eig. erfolgreich gewesen. Allerdings kann ich nicht sagen, ob die stimmt, wenn ich in der Powershell Fehlermeldungen kriege. Dann weiss ich ja nicht, ob mein Skript richtig arbeitet und somit kann ich auch nicht sagen, ob es das richtige misst.

Wenn man mit den Hausmitteln (Logzeitstempel) nicht weiterkommt, kann man es mit einem Profiler probieren.

Auf die schnelle sowas z.B. https://github.com/nohwnd/Profiler. Ohne das jetzt ausprobiert zu haben.

OK, ich werde es mir ansehen. Sieht kompliziert aus, auf den ersten Blick. Aber ich schau mir mal das Youtube video an dazu.

Monger
2023-12-09, 20:05:43
Ja, also das kann schon gut möglich sein. Das Skript durchsucht texturfiles und die sind halt einzeln betrachtet klein und es gibt viele. Je nach Ordner durchaus mal 10 tausend.

Wie lange würde denn das Skript unter realen Bedingungen (geschätzt) laufen? Und wie lange sollte es maximal laufen?
Weil... tausende von Dateien zu durchwühlen, zu lesen, zu verarbeiten und wieder auszuspucken... das ist schon ein Wort. Da musste dich auch fragen, was eigentlich passiert wenn dein Skript abgebrochen wird. Kommst du dann danach noch klar? Oder hast du kaputte Daten, und weißt nicht mehr was du nehmen kannst?

Du beteuerst zwar mehrmals dass du von Programmieren keine Ahnung hast, hast aber andererseits an dem Skript schon einiges gemacht. Deshalb mal hier noch ein Tipp, der sich in der Praxis bewährt hat: trenne zwischen Funktionen und Ausführung. Mach eine ps1 Datei nur mit Funktionen. Mach dann eine zweite ps1 Datei, die die erste dot sourced, und mach darin dann die Funktionsaufrufe. Vorteil: du kannst so indem du die erste Datei lädst auch nur einzelne Funktionen aufrufen, messen, debuggen etc. . Das große Monster in kleine Stücke schneiden. Dann werden auch diese gigantischen Schleifen überschaubar.

Asaraki
2023-12-09, 21:07:25
Zu deiner eigentlichen Frage :
CPU ist bei den allerallerallermeisten Dingen nicht der Flaschenhals. Schon seit den 70ern werden grosse Systeme auf IO, Cache und Paralellität getrimmmt, denn mehr CPU kann man fast immer einbauen/freischalten. Als Stichwort z.B InQueue vs CPU Load. Stell dir dein System wie eine grosse Kugelbahn vor. Die ist nicht erst dann ausgelastet wenn überall auf der Bahn eine Kugel ist, sondern wenn du warten musst bevor die nächste Kugel in die Bahn kann.

Wieso das relevant ist? Kombiniere das mit dem, was Monger gerade schrieb. Funktionen isolieren und erst einmal diese einzeln auf Performance testen.

Performanceanalyse ist nicht schwierig sondern mühsam. Abkürzungen gibt es keine.

Also mal dort anfangen und schauen welche einzelnen Aktionen wie viel Zeit brauchen, ob sie linear bleiben oder z.B. nach X Iterationen in den vollen Cache laufen und so weiter. Mit jeder dir bekannten Grösse wird es dann einfacher das grosse Ganze zu beurteilen.

Step by Step :-)

Platos
2023-12-09, 22:00:45
Wie lange würde denn das Skript unter realen Bedingungen (geschätzt) laufen? Und wie lange sollte es maximal laufen?
Weil... tausende von Dateien zu durchwühlen, zu lesen, zu verarbeiten und wieder auszuspucken... das ist schon ein Wort. Da musste dich auch fragen, was eigentlich passiert wenn dein Skript abgebrochen wird. Kommst du dann danach noch klar? Oder hast du kaputte Daten, und weißt nicht mehr was du nehmen kannst?

Das war noch konservativ. Es kommt aber auf den Ordner drauf an. Die Unterordner sind so 2-10k, aber wenn ich den Überordner nehme, sind es 75k dateien.

Was passiert, wenn was schief läuft? Von vorne anfangen... Aber die Daten sind ja jetzt nicht so kritisch. Kann ich ja bei steam erneut runter laden. Und ich habe ja sowieso von allem Kopien. Geht paar Sekunden für paar GB.

Du beteuerst zwar mehrmals dass du von Programmieren keine Ahnung hast, hast aber andererseits an dem Skript schon einiges gemacht. Deshalb mal hier noch ein Tipp, der sich in der Praxis bewährt hat: trenne zwischen Funktionen und Ausführung. Mach eine ps1 Datei nur mit Funktionen. Mach dann eine zweite ps1 Datei, die die erste dot sourced, und mach darin dann die Funktionsaufrufe. Vorteil: du kannst so indem du die erste Datei lädst auch nur einzelne Funktionen aufrufen, messen, debuggen etc. . Das große Monster in kleine Stücke schneiden. Dann werden auch diese gigantischen Schleifen überschaubar.

Also ich verstehe schon nicht, was Funktionen und was Ausführungen sind in diesem Kontext.

Asaraki
2023-12-09, 22:05:59
Vielleicht wäre ein Programmierkurs das beste… wird schwierig das zu erklären, wenn dir Funktionen kein Begriff sind :-/

Ist ja nicht dein erstes Ding, bei dem du eigentlich besser dran wärst du würdest gleich die richtige Technik dafür lernen / zumindest die geläufige Sprache in dem Thema zu kennen damit man mit standardbegriffen reden kann. Dazu musst du kein Profi werden, aber halt die Konzepte und Begriffe etwas kennen

Lohnt sich imho sowieso und geht auch nicht unbedingt länger als solche Postings zu verfassen ;-)

Eine Funktion ist ein gekapseltes Stück Code, eine logisch diskrete Funktion halt. Kann 1 oder 100 Zeilen Code sein, völlig egal. Aber halt logisch in sich geschlossen, testbar und hoffentlich auch verstanden was darin passiert und vor allem was eben nicht. Das ist quasi der Backstein des Programmierens und den Backstein verbaut man erst, wenn er weiss, dass er auch gut backsteint :D

Alles ohne Funktionen ist quasi Spaghetticode, unmöglich zu verstehen da zu komplex (nicht kompliziert).

Historisch waren Funktionen früher "reusable code" der typischerweise mit einer JMP / GOTO oder äquivalenten Instruktion von überall im Code angesprochen werden konnte. Vielleicht hast du ja mal Basic oder sowas programmiert :D Nur dass man dann danach entscheiden musste wohin man 'zurückgeht'. Das, und nur das, nimmt einem eine moderne Funktion ab. Der Rest dient alleine der Lesbarkeit und dem erkennen von Fehlern noch vor der Kompilierung. Daher führte man früh Labels ein, die mit lesbaren Begriffen funktionierten. Dann konnte man schreiben "Goto DoThisAndThat". War aber auch nur ein Tag für den Compiler um dann aus "Goto DoThisAndThat" und "Label DoThisAndThat" wieder eine normale Instruktion zu machen. Irgendwann war dann einer so schlau und kombinierte das zu einer "Funktion" (Methode, Procedure, Section, alles das gleiche)

Ausführungen sind alles was Code ausführt. 1000 Funktionen alleine tun ja noch nichts, das ist das schöne daran. Die sind nur da und bereit etwas zu tun.
Übrig bleiben dann noch Dinge wie Datendefinitionen etc und obwohl auch die natürlich performancerelevant sind würde man deren negativen/positiven Effekt beim Testen der einzelnen Funktionen finden.

Z.B. ganz klassisch schreibt man 4 Funktionen die das gleiche tun (logisch) und ruft jede 1 Million mal auf. Danach weisst du welche von den 4 Versionen die schnellste ist. Das wiederholt man dann für alle benötigten Funktionen und dann hat man ein performanceoptimiertes Stück Software.

P.S: Ich hatte beruflich viele deiner Fälle auf dem Tisch. Jemand baut was und 'behauptet' dann es sei langsam. Ganz gemütlich frage ich dann jeweils als erstes : "Wie schnell sollte es denn sein?" Daher Mongers Frage, er ist einfach viel netter als ich :D Aber du weisst ja gar nicht wirklich ob es langsam ist (vermutlich schon, ist ja generierter Rubbishcode) oder ob es eventuell sogar so schnell ist, wie es eben sein kann auf deinem System.
D.h. eigentlich brauchst du eine Referenz bevor man irgendwas tut und am besten halt runtergebrochen auf einzelne Teilstücke (Funktionen oder Funktionalitäten). Zum Beispiel "bei 15.6k FileOps pro Sekunde kommt mein System an den Anschlag, das Skript führt aber nur 2.3k aus." Ansonsten fischt man im Trüben.

Long story short : Oft war meine Antwort auch... zu aufwändig, zu viel gewollt. Modularisieren, aufteilen und dann läuft das. Man kann Systeme sehr einfach überlasten und auf einen Bruchteil ihrer eigentlichen Peakperformance limitieren indem man Dinge in der falschen Reihenfolge tut. Dazu muss man die einzelnen Subsyteme kennen respektive die zuständigen SysEngineers nerven.

Du würdest staunen wie wenige "Profis" dir sagen können wie viele 'Insert in order' z.B. ihr Datenbanksystem theoretisch schaffen würde. Da wird einfach mal behauptet etwas sei langsam oder schnell ohne überhaupt einen Referenzwert zu kennen. Performance by Gefühl, verbreiteter als man meint ^^

P.P.S: Solche Dinge solltest du kennen, nur schon um besser im Internet suchen zu können oder Erkärungen zu verstehen, wenn du in der Lage sein willst an deinem Code zu arbeiten :
https://hackr.io/blog/programming-terms-definitions-for-beginners

P.P.P.S: @Monger: Ich mag wie du denkst. <3 Mit dir könnte man vermutlich gut arbeiten :D

Ich habe nie gesagt, dass es langsam ist. Ich habe nur gesagt, dass weder die CPU noch die SSD vollständig ausgelastet sind (also bei der CPU ist auch kein einzige Kern ganz ausgelastet.).

Und es könnte halt schneller sein.
Btw, absolutes Gold. Ist halt ein klassischer Denkfehler.
=> Du _glaubst_ es könnte schneller sein. Es liegt nun an dir erst einmal zu beweisen, dass es schneller sein könnte.
=> 2. Wie hier im Forum schon zu oft erklärt : Auslastungen sind ein Fantasiewert. Ich behaupte 99,5% aller PC-Nutzer wissen gar nicht was dieser Wert bedeutet geschweige denn wie er errechnet wird. Hint : Ein richtiges System hat niemals nur einen Auslastungswert pro Komponente.

myMind
2023-12-09, 22:33:43
Das etwas nervige ist, diese Angabe von "In Zeile: 340 Zeichen7:" Ich verstehe nicht, auf was sich das bezieht. Es bezieht sich nicht auf mein Skript und wenn ich die logfile ansehe, dann ist es auch nicht Zeile 340. Ich habe also kurz gesagt keine Ahnung, welche Zeile da gemeint ist.

Edit: Wenn ich mir das genauer ansehe, ist die Zeitmessung eig. erfolgreich gewesen. Allerdings kann ich nicht sagen, ob die stimmt, wenn ich in der Powershell Fehlermeldungen kriege. Dann weiss ich ja nicht, ob mein Skript richtig arbeitet und somit kann ich auch nicht sagen, ob es das richtige misst.

Zähl einfach mal die { und die } in deinem Code. Vermutlich unterschiedlich.

Asaraki
2023-12-09, 22:43:32
Zähl einfach mal die { und die } in deinem Code. Vermutlich unterschiedlich.

Und wohin kommen dann die Fehlenden? ;)

Platos, das hilft dir vielleicht besser zu verstehen, wie du vorgehen musst :

ich weiss (wegen der fortschrittsanzeige), dass die 2. foreach schleife langsam ist. Ich habe aber schon einige optimierungen vorgenommen, die haben aber praktisch nix geholfen. Also beispielsweise prüfe ich die auflösung von dateien, d.h ich sortiere sie in bestimmte kategorien ein. Vermutlich etwa 80% passen nicht in diese kategorie, deswegen habe ich eine if-anweisung eingeführt, die das gegenteil prüft. wenn das zutrifft (was in 80% der fällen sein sollte), überspringt es jegliche anderen prüfungen dieser art. das mache ich mit einer eingerückten else-klammer.

Als ich das gemacht habe, war der code genau gleich schnell in der 2. schleife.


Der erste Teil ist super, genau das ist eine Optimierung.
Aber deine Schlussfolgerung ist falsch, auch wenn die Aussage umgangssprachlich korrekt ist. Dein Code ist sehr wahrscheinlich schneller geworden, aber nur dort wo du optimiert hast. Da dies aber nicht der schlimmste Flaschenhals war merkst du davon oberflächlich nichts.
Hättest du diesen Code jetzt in einer Funktion (siehste, nützlich!) dann wäre es eine Sache von 30 Sekunden schnell zu testen wie sich die Execution Time (aka Performance) für diesen Abschnitt geändert hätte.

Sehr oft, wenn man blind optimiert, optimiert man die Dinge, die einem auffallen, aber nicht unbedingt die, die man zuerst tun müsste. Beispiel :
Jemand hat 5 Flaschenhälse, alle kosten 100% Zeit, verdoppeln also die Ausführungszeit insgesamt von 10 Sekunden auf 10*10*10*10*10 = 100'000 Sekunden. Darüber gibt es einen Falschenhals, der alles auf 100 Ausführungen pro Sekunde limitiert und aus diesen 100'000 Sekunden 150'000 Sekunden macht. Sagen wir weil es nach jeder komplleten Ausführungen etwas Dummes macht bis zu einer Sekunde vom System angehalten wird, je nachdem wie lange die Ausführung brauchte. Waren es 0.5 Sekunden, dann wartet er 0.5 Sekunden. Waren es nur 0.01 Sekunden dann wartet er dafür 0.99 (Frei erfunden und nicht logisch, aber egal!)

Jetzt findet er alle 5 Flaschenhälse zuerst und verbessert diese, aber das Programm läuft immer noch fast 150'000 Sekunden. Vielleicht 149'200. :D
Dann findet er den Flaschenhals ganz oben und jetzt läuft es in 15 Sekunden durch. Hurrah, der letzte Flaschenhals kostete 149'975 Sekunden? Nope, nope, nope!

Ein Echtweltbeispiel dazu sind oft Dinge bei denen eine Queue zuläuft. Dann ist deine Performance nicht davon abhängig, wie schnell du den Cache/Queue/xyz füllen kannst sondern nur davon wie schnell er abgearbeitet wird. Z.b. I/O Operationen können das sein. Dann kannst du optimieren wie du willst, du läufst nur jeweils schneller in den Flaschenhals und kannst länger chillen. Sogenannte "WAIT Time". Wir schauen nicht auf verbrauchte CPU-Zeit sondern wann das Programm warten muss und weshalb. Aber ganz ehrlich das sagt dir Windows nicht. Wenn man sich in diese Bereiche vorwagt merkt man mal wieder, dass Windows halt ein Playmobil-OS ist für Kinder, die bloss nichts kaputt machen sollen :D

Und darum modularisieren wir unseren Code in Funktionen, Module (Funktionssammlungen) etc. um die maximale Kontrolle zu haben und bei Bedarf den Code in beliebigen Grössen und Teilstücken auf Performance untersuchen zu können.

Und noch eine Weisheit aus dem Performancebereich : Dein Code ist nur die kleine Spitze des Eisbergs. Das System unter der Oberfläche ist das Interessante, denn Code interagiert lediglich damit und tut selbst herzlich wenig. Programmierer sehen aber oft nur den Code, den sie halt kennen und übersehen mögliche Flaschenhälse, die in ihrem Code nicht explizit vorkommen oder ihnen gleich komplett unbekannt sind.

Und : Code läuft nicht einfach. Code(der Prozess) wird explizit vom System dazu aufgefordert weiterzulaufen. Kann man sich mal so merken und auf der Zunge zergehen lassen. Klingt simpel, aber es bedeutet eine ganze Menge in Bezug auf Performance.

#44
2023-12-09, 23:16:30
Das etwas nervige ist, diese Angabe von "In Zeile: 340 Zeichen7:" Ich verstehe nicht, auf was sich das bezieht. Es bezieht sich nicht auf mein Skript und wenn ich die logfile ansehe, dann ist es auch nicht Zeile 340. Ich habe also kurz gesagt keine Ahnung, welche Zeile da gemeint ist.

Doch, das bezieht sich auf dein Skript. Aber nur weil der Compiler/Interpreter in Zeile 340 endgültig stolpert, heißt das nicht, dass du ihm erst da das Bein gestellt hast.
Und ja, das ist quasi immer die Schuld des Programmierers. Bugs im Parser sind so extrem unwahrscheinlich.

Zeile 179 sieht bspw. verdächtigt aus. Ab da ist mal mindestens die Formatierung kaputt.

Platos
2023-12-09, 23:43:05
@ Asaraki und deine letzten 3 Antworten:

Also im Grunde ist der Grund, warum ich hier frage, der, dass ich herausfinden will, ob der code oder die Hardware limitiert. Das mit dem I/O habe ich auch schon gedacht, aber ich habe kein Tool gefunden, dass das untersuchen kann. Und ansonsten habe ich mit keinem Tool eine auffälligkeit feststellen können.

Aber deswegen (weil ich nichts gefunden habe), habe ich dann nach einer Möglichkeit gesucht, eben den code zu untersuchen (um zu schauen, ob im Code überhaupt etwas viel Zeit beansprucht oder ob es gar nicht schneller geht mit meinem Rechner).

Bezüglich Auslastung: Wie berechnet sich denn die Auslastung? Ich vermute dann mal, ich weiss es nicht :D

Also bezüglich Funktionen: Ich weiss ja, was eine Funktion in der Mathematik ist, aber ich verstehe dennoch nicht, wie ich jetzt so etwas beim Programmieren machen kann/muss. Könntest du mir ein Beispiel machen aus meinem Code? Also irgendwas kleines nehmen und dass dann so schreiben, dass es eine Funktion ist. Oder ist das viel Arbeit bei einem kleinen Code-Teil ?

Und bezüglich Wartezeit: Wie kann ich denn herausfinden, warum mein Programm (also Skript hier) warten muss? Ich würde das ja noch so gerne wissen.

Bezüglich dem Thema Profiling (ich glaube, das nennt man so): Ich habe jetzt eine Möglichkeit gefunden (eine erstaunlich leichte), das ganze Skript Codezeile für Codezeile zu untersuchen und mir die Ausführungszeit auszugeben. Es gibt mir sogar aus, wie oft diese Zeile quasi etwas gemacht hat (also ausgeführt wurde).

Das ist ja mal echt mega geil. Muss jetzt aber zuerst mal untersuchen, was am meisten Zeit konsumiert. Ich werde dann berichten. Weil jetzt, wo ich schauen kann, was viel Zeit frisst, kann ich wenigstens spezifisch nach Problemlösungen suchen.

hier ist das Tool: https://github.com/IISResetMe/PSProfiler
Und hier ist ein Video dazu: https://www.youtube.com/live/_ymfgrgXILc?feature=shared&t=1376

hier ein Ausschnitt von dem, was ausgegeben wird:

Count Line Time Taken Statement
----- ---- ---------- ---------
0 1 00:00.0000000 {
0 2 00:00.0000000
0 3 00:00.0000000 # Geben Sie Ihre Pfade an
1 4 00:00.0023136 $rootFolder =
"C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
1 5 00:00.0000116 $outputFolder =
"C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
1 6 00:00.0000053 $csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscal
ed\mapping.csv"
0 7 00:00.0000000
0 8 00:00.0000000 # Debugging-Flags
1 9 00:00.0000051 $debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
1 10 00:00.0000045 $debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
1 11 00:00.0000047 $debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es
wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)
0 12 00:00.0000000
0 13 00:00.0000000 # Erstellen Sie leere Strings für die Ausgaben
1 14 00:00.0005518 $output1 = New-Object System.Text.StringBuilder
1 15 00:00.0001004 $output2 = New-Object System.Text.StringBuilder
1 16 00:00.0000378 $output3 = New-Object System.Text.StringBuilder
0 17 00:00.0000000
0 18 00:00.0000000 function Search-DDSFormat {
0 19 00:00.0000000 param (
0 20 00:00.0000000 [string]$filePath
0 21 00:00.0000000 )
2736 22 00:01.2612956 $content = Get-Content -Path $filePath
2736 23 00:00.3544500 $match = $content | Select-String -Pattern "DDSFormat=(.*)"
2736 24 00:00.2627039 $width = $content | Select-String -Pattern "Width=(.*)"
2736 25 00:00.2319937 $height = $content | Select-String -Pattern "Height=(.*)"
0 26 00:00.0000000 if ($match) {
2639 27 00:00.1762389 @{
0 28 00:00.0000000 'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
2639 29 00:00.0177688 'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else {
$null }
2639 30 00:00.0155596 'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" }
else { $null }
0 31 00:00.0000000 }
0 32 00:00.0000000 }
0 33 00:00.0000000 }
0 34 00:00.0000000
0 35 00:00.0000000 # Definition einiger Variablen
1 36 00:00.0005595 $mapping = New-Object System.Collections.Generic.List[object]
1 37 00:00.0002084 $ddsFormatFiles = New-Object System.Collections.Generic.List[object]
0 38 00:00.0000000
1 39 00:00.5921127 $files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
1 40 00:00.0000101 $totalDdsFiles = 0
1 41 00:00.0000079 $totalTxtFilesWithDDSFormat = 0
1 42 00:00.0000108 $totalTxtFilesWithWidth = 0
1 43 00:00.0000108 $totalTxtFilesWithHeight = 0
1 44 00:00.0000078 $totalCopiedFiles = 0
0 45 00:00.0000000
0 46 00:00.0000000 # Durchsuchen des Inhalts der .txt Dateien im rootFolder
1 47 00:00.0015505 $filesCount = $files.Count
1 48 00:00.0000086 $k = 0
0 49 00:00.0000000 foreach ($file in $files) {
2736 50 00:00.0098461 $k++
0 51 00:00.0000000 if ($k % 100 -eq 0) {
27 52 00:00.1629705 Write-Progress -Activity "Running Script" -Status "Processing File $k of
$filesCount" -PercentComplete (($k / $filesCount) * 100)
0 53 00:00.0000000 }
0 54 00:00.0000000 if ($debug2) { [void]$output2.Append("`nÜberprüfe Datei: $($file.FullName)") }
# Debugging-Ausgabe
2736 55 00:02.7320446 $result = Search-DDSFormat -filePath $file.FullName

Doch, das bezieht sich auf dein Skript. Aber nur weil der Compiler/Interpreter in Zeile 340 endgültig stolpert, heißt das nicht, dass du ihm erst da das Bein gestellt hast.
Und ja, das ist quasi immer die Schuld des Programmierers. Bugs im Parser sind so extrem unwahrscheinlich.

Zeile 179 sieht bspw. verdächtigt aus. Ab da ist mal mindestens die Formatierung kaputt.

Ja ok, du hast recht. Ich habe in diesem Skript wirklich die else bedinung gekillt mit einer falschen Tabulator-Einrückung. Gestern um 4 Uhr morgens ist mir das ums Verrecken nicht aufgefalle:freak:

Aber das gute ist, dass ich das jetzt sein lassen kann, da ich jetzt ein Tool gefunden habe, das mir dieses mühsame einfügen von tausend Measure-Commands komplett abnimmt (siehe oben in diesem Kommentar)

Monger
2023-12-10, 00:10:50
hier ist das Tool: https://github.com/IISResetMe/PSProfiler
Und hier ist ein Video dazu: https://www.youtube.com/live/_ymfgrgXILc?feature=shared&t=1376

Cool! Kannte ich noch nicht! Gabs auch noch nicht als ich es am dringendsten gebraucht hätte :D

Vielleicht ist das der Moment wo man mal das Problem würdigen sollte. Was du versuchst, ist ja wirklich nicht banal. Du hast hier komplexe Logik, du bearbeitest große Datenmengen, du benötigst exotische Tools, Robustheit und Performance spielen eine Rolle...
Das ist genug, um einen ausgebildeten Junior Entwickler in die Knie zu zwingen. Dass du dich an sowas getraut hast, und ChatGPT dich immerhin bis an diesen Punkt gebracht hat, ist respektabel.

Monger
2023-12-10, 00:15:16
P.P.P.S: @Monger: Ich mag wie du denkst. <3 Mit dir könnte man vermutlich gut arbeiten :D

Merci!
Die allermeisten Devs geben mir normalerweise gute Noten :)

Asaraki
2023-12-10, 00:26:50
Ich (und die meisten sicher) hab schon verstanden, was du versuchst :) Das Ding ist halt, dass das keine kurze Antwort darauf gibt, weshalb das immer automatisch ausufert :D

=> Wie kann ich denn herausfinden, warum mein Programm (also Skript hier) warten muss? Ich würde das ja noch so gerne wissen.
&
=> Weil jetzt, wo ich schauen kann, was viel Zeit frisst, kann ich wenigstens spezifisch nach Problemlösungen suchen.

Genau und die Wait Time ist dann ein Teil der "Zeit". Neben CPU Zeit addieren sich alle anderen Zeiten (=Detailgrad der Messsoftware/OS etc.) und je besser das Tool desto mehr verschiedene Zeiten siehst du :D Aber selbst wenn du nur deine CPU Zeite und die Total Elapsed Time siehst, kannst du immer noch einiges ableiten.

Das WARUM man warten muss hängt halt davon ab ob man eine Software hat, die das überhaupt weiss. Das ist nicht ganz so trivial. Oft muss man das dann selbst ableiten, in dem man andere Dinge ausschliesst und nur noch etwas als Grund für die Wartezeit übrigbleibt. Dann braucht man neue Tools um in dem Ding dann rauszufinden, warum es zum warten kommt... und dann geht es vielleicht noch eine Ebene tiefer. Und dann muss man dem Hersteller der Festplatten schreiben und die müssen das in ihrem Labor nachstellen und dann gibt es eine neue Firmware für alle weltweit und 99% haben noch gar nicht gewusst, dass sie ein Problem hatten. ;)

=> Bezüglich Auslastung: Wie berechnet sich denn die Auslastung? Ich vermute dann mal, ich weiss es nicht

Gute Frage, nächste Frage :D Wichtiger ist zu verstehen, dass man relativ gut austesten kann was man alles unterkriegen kann bevor man ausgelastet ist. Zum Beispiel nehmen wir mal dich selbst. Du tust gerade etwas am Computer, z.B. hier lange Postings verfassen. Jetzt bist du nicht 100% ausgelastet. Du könntest vermutlich dazu Musik hören und mit einem Fuss wippen.

Aber Karotten schneiden wäre gerade schwierig, obwohl du sonst auch Karotten schneiden, Musik hören und mit dem Fuss wippen kannst.

D.h. wenn du ein Posting verfasst (als CPU) kannst du nicht gleichzeitig Karotten schneiden. Für den Task Karotten schneiden wärst du jetzt virtuell 100% ausgelastet. Aber du könntest eben z.B. parallel Musik hören.

So ähnlich ist die Auslastung einer CPU, eines Systems, einer GPU etc... es gibt nicht Prozente von Auslastung sondern freie Plätze in der Queues der einzelnen, unzähligen Pipelines innerhalb dieser komplexen Architektur.

Und all das wird dann quasi als Prozentzahl zur Vereinfachung angezeigt.

Auf Scripte/Programme bezogen bedeutet das :
Wenn man alles Modular aufbaut, also in gut gekapselte Funktionen verpackt und logisch aufbaut kann man relativ einfach herausfinden wie viel von welchen Dingen gleichzeitig wie schnell läuft. Und daraus abgeleitet ist es oft am schnellsten, wenn man gewisse Dinge nicht parallel macht sondern vorgängig in einem dedizierten Schritt und dann nur diejenigen Dinge parallel anhand der bereits vorbereiteten "Arbeitsliste" ausführt, die man eben schnell nebeneinander oder hintereinander in Folge abarbeiten kann.

Kleiner Nachtrag : Oftmals hat man Performanceprobleme mit eingekaufter Software oder Dingen, die man wie du nicht selbst entwickelt hat. Quasi eine Black Box. Dann ist der beste Weg oft die Szenarien zu kontrollieren anstelle des Codes. Z.B. hättest du dein Script erstmal auf vorgefertigten Testordern laufen lassen können. Gefüllt z.B. einmal mit nur Files der Grösse 1kb, einmal mit maximal 10 Files pro Order etc. etc. Das würde dir bereits viele der von mir erwähnten Referenzwerte geben.
Z.B.
"Ich weiss, das Script läuft mit 100000 Files in einer Ordertiefe von 5 bei sehr kleinen Files sehr schnell."
"Ich weiss, das Script läuft mit 1000 Files in einem flachen Order bei sehr vielen verschiedenen Filetypen eher langsam"
etc.

So kann man schneller eine Idee dafür kriegen in welchen Teil des Codes man vielleicht mal genauer reinschaut.

Merci!
Die allermeisten Devs geben mir normalerweise gute Noten :)
Have a Kudos, bitch :D

Platos
2023-12-10, 01:40:45
Danke an alle übrigens, die sich hier die Mühe machen, einem Noob wie mir zu helfen.

@ Asaraki: Bezüglich den 1000 und 100000 Files... Hmm du magst da einen guten Punkt getroffen haben. Wenn ich mir jetzt mal so überlege... Dieser Testordner hat 2700 .txt Dateien und 2639 .dds Dateien und benötigt so 17-20sekunden (schwankt). Der Ordner mit ~75k Dateien benötigt aber 25 Minuten. Der Ordner ist also grob 28 Mal grösser, benötigt aber 75 mal mehr Zeit. Ist also grob 3x langsamer pro Datei.

Dabei geht es um den Überordner. Also der grosse ist /Rom und der kleine ist /Rom/Enemy

Denn ich habe nun die verschiedenen Versionen verglichen (14, 15.1 und 15.2 - 15.3 funktioniert gar nicht richtig, habe ich jetzt herausgefunden): Die Zeiten sind nicht auffällig. Sie sind eig. praktisch alle gleich.

Was ich auch festgestellt habe (bzw. noch aus der Erinnerung weiss): Das Skript wird von Datei zu Datei langsamer. Am Anfang ist es noch flott, aber dann gehts viel länger. Ich glaube, das Problem muss ich suchen. Ich nämlich im Kopf immer diese 25 minuten vom grossen Ordner gehabt und die Perfomance dann fälschlicherweise am kleinen gemessen.

Aber zum Thema wie lange hat jetzt welcher Befehl benötigt:

2736 21 00:01.0785154 $content = Get-Content -Path $filePath
2736 22 00:00.3201638 $match = $content | Select-String -Pattern "DDSFormat=(.*)"
2736 23 00:00.2340322 $width = $content | Select-String -Pattern "Width=(.*)"
2736 24 00:00.2524288 $height = $content | Select-String -Pattern "Height=(.*)"
[...]
1 40 00:00.5578933 $files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
[...]
2736 59 00:02.4430370 $result = Search-DDSFormat -filePath $file.FullName
[...]
2639 98 00:02.3886215 $ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter
($file.BaseName + ".dds") -Recurse
[...]
2639 102 00:00.2170766 $godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
[...]
2639 429 00:02.4546405 Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder
[...]
2639 435 00:00.3249847 $mapping += [PSCustomObject]@{
0 436 00:00.0000000 OldDdsFilePath = $ddsFile.FullName
0 437 00:00.0000000 NewDdsFilePath = $newDdsFilePath
0 438 00:00.0000000 DDSFormat = $result
0 439 00:00.0000000 }


Was ich hier nicht ganz verstehe: Die unzähligen Dinge, die ich an den Dateien untersuche (Dateinamen, Auflösung) benötigen jeweils quasi nichts oder gar nichts an Zeit.

Wieso ist das so? Sollte nicht genau das der fordernste Teil sein?

EDIT: Ein Teil des codes konnte ich mal optimieren. Die Zeile $ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds") -Recurse sucht in allen Unterverzeichnissen, in denen die zugehörige .txt datei ist. Dabei soll eig. bewusst nur im gleichen Ordner gesucht werden, in dem diese zugehörige .txt datei ist und dieser ordner wird ja direkt davor definiert. Das ist noch so ein klassischer Überbleibsel von "Ich sage, was ChatGPT tun soll und er macht was anderes" (diesen Fall hatte ich dutzende Male).

Habe ich erst jetzt erkannt. Ich habe das -Recurse nun gelöscht und die Zeit ist von ~2.38s auf ~1.25s geschrumpft. Das ist schon bisschen was, wenn ich es in Relation zu den anderen Zeiten setze. Den grossen Ordner muss ich aber weiterhin testen. Bin noch nicht dazu gekommen.

Cool! Kannte ich noch nicht! Gabs auch noch nicht als ich es am dringendsten gebraucht hätte :D

Vielleicht ist das der Moment wo man mal das Problem würdigen sollte. Was du versuchst, ist ja wirklich nicht banal. Du hast hier komplexe Logik, du bearbeitest große Datenmengen, du benötigst exotische Tools, Robustheit und Performance spielen eine Rolle...
Das ist genug, um einen ausgebildeten Junior Entwickler in die Knie zu zwingen. Dass du dich an sowas getraut hast, und ChatGPT dich immerhin bis an diesen Punkt gebracht hat, ist respektabel.

Danke viel mals :)

Für Erfahrene mag der Code vlt. richtig schlecht aussehen, aber es war dennoch nicht einfach (für mich), das zu erstellen. Klar, ChatGPT hat mir den Grossteil des Codes geschrieben, aber eben nicht funktionierend und ich musste das dann zum laufen bringen. Das hört sich für jemand, der programmieren kann, nach nichts an, aber das ist so wie wenn man einen Chinesen ein Text schreiben lässt (auf chinesisch), ihn dann weiter leitet und dann kriegt man die Rückmeldung, dass das nur kauderwelsch ist und es keinen Sinn ergibt. Und ich muss dass dann "flicken". In etwa so, ist das für mich, micht ChatGPT zu arbeiten.

jetzt verstehe ich ja immerhin (im Kontext), was in meinem Code steht. Aber neues kenne ich ja nicht und verstehen (im Kontext) und Sprechen ist ein Unterschied.

Btw: Perfomance ist jetzt nicht unbedingt das wichtigste. Das wichtigste ist, dass es funktioniert. Aber ich optimiere gerne und warum nicht schneller, wenns schneller geht? Aber ich muss zuerst mal herausfinden, obs überhaupt schneller geht... Aber das ist jetzt einfacher mit PSProfiler.

][immy
2023-12-10, 10:15:26
Also mein erster unnützer Tipp, schreib es in c# um ;)
Ok, war nicht hilfreich

Ausgaben vermeiden:
Hast du ja bereits. Aber immer beachten Konsolenausgaben sind in .net Applikationen (Powershell basiert ja darauf) erstaunlich teuer. In .net entkoppel ich daher meist die logging Ausgaben vom Rest des Codes, denn wenn du live loggst bekommst du nur ein paar hundert Operationen zusammen bei voller CPU Auslastung. Fasst du allerdings Log Ausgaben zusammen mit einer Ausgabe sparst du massig Ressourcen.

Auf Stringbilder hast du ja bereits umgestellt. Alternativ kannst du sich eine Liste von Strings sammeln an die du nur anhängst. Alternativ auch einfach mit appendline arbeiten. Das spart die die neuen Zeilen innerhalb des Codes (/n). Am Ende dann einmal joinen und gut ist. Hat aber eher nur mit der Übersichtlichkeit zu tun.

Etwas was gerne in Powershell vergessen wird ist das abräumen von Objekten. In .net hast du hier häufig noch ein Objekt offen das disposed werden muss, ansonsten bleibt gern mal ein Datei-Handler offen. Das ist in einem normalen Skript nicht tragisch, weil es mit der Session geschlossen wird, aber bei einer Art Massenverarbeitung kann sich da was ansammeln und das ganze System verlangsamen.

In Methoden unterteilen macht das Messen der Performance deutlich einfacher. Zudem ist der Code dann unterteilt, leichter zu debuggen und es wird auch nur der Skript Part kompiliert der genutzt wird. Läuft also insgesamt etwas runder.

myMind
2023-12-10, 10:21:37
Was ich auch festgestellt habe (bzw. noch aus der Erinnerung weiss): Das Skript wird von Datei zu Datei langsamer. Am Anfang ist es noch flott, aber dann gehts viel länger. Ich glaube, das Problem muss ich suchen. Ich nämlich im Kopf immer diese 25 minuten vom grossen Ordner gehabt und die Perfomance dann fälschlicherweise am kleinen gemessen.
Das könnte aus einer ähnlichen Ecke wie das oben bereits angesprochene "[void]$output2.Append(..." kommen. Globale Variablen sind bequem, bergen aber immer die Gefahr dass viel Speicher allokiert und vor allem bei großem Wachstum dann umallokiert werden muss. Es ist häufig günstiger mit lokalem Scope (Funktionen, lokale Variablen) zu arbeiten, wenn möglich. Bessere Profiler geben dir auch Auskunft über die Speicherverbräuche.

Es kann aber auch schlicht daran liegen, dass Caches vollaufen, bzw. umgekehrt, dass die ersten Dateien die du liest schon im Lesecache sind, weil du nach jedem Programmierschritt immer wieder das Programm startest, aber dann abbrichst. Dann sind nur die ersten Dateien im Lesecache. D.h. am Anfang aus dem Cache geht's schnell, wenn von der Platte gelesen werden muss dann langsam.


Aber zum Thema wie lange hat jetzt welcher Befehl benötigt:

[CODE]
...
2736 21 00:01.0785154 $content = Get-Content -Path $filePath
...
[/SPOILER]

Was ich hier nicht ganz verstehe: Die unzähligen Dinge, die ich an den Dateien untersuche (Dateinamen, Auflösung) benötigen jeweils quasi nichts oder gar nichts an Zeit.

Wieso ist das so? Sollte nicht genau das der fordernste Teil sein?

Wie oben schon gesagt wurde, wird die IO vermutlich den größten Einfluss haben. Eine Datei zu öffnen, komplett in den Speicher zu lesen und dann wieder zu schließen ist eine teure Operation. Schreiben ist gleich nochmal teuerer. Textoperationen (z.B. Vergleichen) im Speicher sind dagegen deutlich billiger. Rechenoperationen - in gleicher Anzahl - sind typischerweise nochmal viel billiger.

Skriptsprachen wie Powershell kapseln viel weg und sind dahingehend optimiert dass man schnell zu Ergebnissen kommt. Ein richtiger Parser, der dir aus der DDS-Datei unter Kenntnis der Dateistruktur nur die Dinge herauszieht, die benötigt werden, wäre schneller als ein "gib mir alles und suche folgenden Text in allem". Je nach Dateiformat müsste man vielleicht sogar nur einen Header lesen, statt der ganzen Datei. Aber der Aufwand sowas umzusetzen ist eben auch ungleich größer als ein Get-Content | Select-String.

Monger
2023-12-10, 10:50:08
Für Erfahrene mag der Code vlt. richtig schlecht aussehen, aber es war dennoch nicht einfach (für mich), das zu erstellen.

Es braucht sehr viel Erfahrung, um Code zu schreiben der einfach aussieht. Wenn du dir mal ein gut gepflegtes Open Source Projekt wie z.B. NUnit anschaust, das ist superklar geschrieben. Das kannst vielleicht sogar du lesen. Aber da sind halt sehr erfahrene Profis dran.


Btw: Perfomance ist jetzt nicht unbedingt das wichtigste. Das wichtigste ist, dass es funktioniert.
Und funktioniert es denn? Hast du es schon einmal für alles durchlaufen lassen? Kam was erwartungsgemäßes raus?

Platos
2023-12-10, 20:31:45
[immy;13451224']Also mein erster unnützer Tipp, schreib es in c# um ;)
Ok, war nicht hilfreich

Ausgaben vermeiden:
Hast du ja bereits. Aber immer beachten Konsolenausgaben sind in .net Applikationen (Powershell basiert ja darauf) erstaunlich teuer. In .net entkoppel ich daher meist die logging Ausgaben vom Rest des Codes, denn wenn du live loggst bekommst du nur ein paar hundert Operationen zusammen bei voller CPU Auslastung. Fasst du allerdings Log Ausgaben zusammen mit einer Ausgabe sparst du massig Ressourcen.

Ok, also hört sich gut an, aber ich verstehe nicht, wie ich das umsetzten kann. Also ich verstehe nicht ganz. Kannst du mir ein Beispiel machen, was entkoppelt und was nicht entkoppelt ist?

Ich habe die Ausgabe doch schon entkoppelt? Ich mache das mit den output-dinger doch so, dass alles am schluss ausgegeben wird (zumal ich ja eig. nur ein paar wenige Host-Writes habe, DIE ich wirklich aktiviert habe. $debug2 Host-Writes lösche ich vermutlich alle raus später).

[immy;13451224']Auf Stringbilder hast du ja bereits umgestellt. Alternativ kannst du sich eine Liste von Strings sammeln an die du nur anhängst. Alternativ auch einfach mit appendline arbeiten. Das spart die die neuen Zeilen innerhalb des Codes (/n). Am Ende dann einmal joinen und gut ist. Hat aber eher nur mit der Übersichtlichkeit zu tun.

Mhh, nur um sicher zu gehen: Auf welche Skriptversion beziehst du dich da, wenn du das sagst? Bzw. auf Welche Codezeile? Weil Version 15.3 habe ich über Bord geschmissen, da ich herausgefunden habe, dass das nicht funktioniert. Es hat richtig ausgesehen auf den ersten Blick, aber es kam zu fehler.

[immy;13451224']Etwas was gerne in Powershell vergessen wird ist das abräumen von Objekten. In .net hast du hier häufig noch ein Objekt offen das disposed werden muss, ansonsten bleibt gern mal ein Datei-Handler offen. Das ist in einem normalen Skript nicht tragisch, weil es mit der Session geschlossen wird, aber bei einer Art Massenverarbeitung kann sich da was ansammeln und das ganze System verlangsamen.

In Methoden unterteilen macht das Messen der Performance deutlich einfacher. Zudem ist der Code dann unterteilt, leichter zu debuggen und es wird auch nur der Skript Part kompiliert der genutzt wird. Läuft also insgesamt etwas runder.

Ich muss nochmals in Erinnerung rufen, dass ich mit den ganzen Fachwörtern wie "abräumen von Objekten" und "disposed" und "Datei-Handler" nichts anfangen kann bzw. das nicht verstehe. Würdest du das nochmals erklären für mich?

Das könnte aus einer ähnlichen Ecke wie das oben bereits angesprochene "[void]$output2.Append(..." kommen. Globale Variablen sind bequem, bergen aber immer die Gefahr dass viel Speicher allokiert und vor allem bei großem Wachstum dann umallokiert werden muss. Es ist häufig günstiger mit lokalem Scope (Funktionen, lokale Variablen) zu arbeiten, wenn möglich. Bessere Profiler geben dir auch Auskunft über die Speicherverbräuche.


Also diese version (15.3) hat leider nicht funktioniert. Ich weiss nicht wieso, bin aber deswegen auf eine Version davor zurück gerudert. Also ich meine das void-zeugs, das nutze ich nicht mehr momentan.

Es braucht sehr viel Erfahrung, um Code zu schreiben der einfach aussieht. Wenn du dir mal ein gut gepflegtes Open Source Projekt wie z.B. NUnit anschaust, das ist superklar geschrieben. Das kannst vielleicht sogar du lesen. Aber da sind halt sehr erfahrene Profis dran.


Und funktioniert es denn? Hast du es schon einmal für alles durchlaufen lassen? Kam was erwartungsgemäßes raus?

Ja, funktionieren tut es seit Version 14. Die Version 15.3 ist da eine Ausnahme. ich habe jetzt aber nochmals den Code etwas überarbeitet und konnte 10k Zeichen sparen und es funktioniert alles, wie es sollte.

Ich Versuche aber weiterhin Möglichkeiten umzusetzen, die mir empfohlen wurden, aber ich schaffe es einfach nicht.

- Die += durch "[void]$output1.AppendLine("my message")" zu ersetzen hat nicht geklappt. Das habe ich ja in Version 15.3 gemacht, aber es hat nicht geklappt, wie ich dann herausgeunden habe.
- Die FilenameChecks und die ResolutionChecks durch Funktionen ersetzen habe ich nun schon oft gehört. Leider weiss ich einfach nicht, wie ich das mache. Und irgendwie habe ich es noch nicht geschafft, Codebeispiele zu finden, wo ich mich orientieren könnte.
- Dann habe ich noch irgendwo gelesen, dass ich Get-ChildItem durch "[IO.DirectoryInfo]'s EnumerateFiles() instance method" ersetzen könnte. Das habe ich probiert (Ich habe ChatGPT gesagt, er soll es einbauen), aber es hat nicht funktioniert.

Aber ich denke, es wäre sinnvoll, mal mein aktuelles Skript zu posten mit den neuesten Änderungen:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-upscalingDDS\rom2\log.txt"
Start-Transcript -Path $logFile -Append
Measure-Script {
# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-ARCtool-entpackte\rom-ARCtool\ARCtool\rom"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-upscalingDDS\rom2"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-upscalingDDS\rom2\rom2-mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$godFatherFolder += "_UltraLowRes"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$godFatherFolder += "_color"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

$conditionMet = $false

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if ($conditionMet -eq $false -and (& $resolutionCheckItem.Condition)) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
break
}
}
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

$fileNameCheckMet = $false

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$fileNameCheck = $filenameCheckItem.FileName
if ($fileNameCheck -eq "_DM" -and $parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
} else {
$parentFolder += $fileNameCheck
}
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), Item: $($filenameCheckItem.Description), fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
break
}
}
}

# Erstellen Sie den GodFatherFolder im Ausgabeordner
if (-not (Test-Path $godFatherFolder)) {
$testPathResult = Test-Path $godFatherFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${godFatherFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $godFatherFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${godFatherFolder}: ${newItemResult}" }
}


# Erstellen Sie den GrandParentFolder im Ausgabeordner
if (-not (Test-Path $grandParentFolder)) {
$testPathResult = Test-Path $grandParentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${grandParentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $grandParentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${grandParentFolder}: ${newItemResult}" }
}

# Erstellen Sie den ParentFolder im Ausgabeordner
$parentFolder += $resolution
if (-not (Test-Path $parentFolder)) {
$testPathResult = Test-Path $parentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${parentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $parentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${parentFolder}: ${newItemResult}" }
}

# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name)) {
if ($destinationFolder -match "_\d+$") {
$destinationFolder = $destinationFolder -replace "_\d+$", "_$i"
} else {
$destinationFolder = $destinationFolder + "_$i"
}
$i++
}

if (-not (Test-Path $destinationFolder)) {
$testPathResult = Test-Path $destinationFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${destinationFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${destinationFolder}: ${newItemResult}" }
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder
if ($debug2) { $output2 += "`nCopying file: $($ddsFile.FullName) to ${destinationFolder}" } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $($mapping.Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}




if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3
}
# Am Ende des Skripts
Stop-Transcript

Übrigens: Ich lasse momentan gerade den grossen Ordner laufen und mit PSPRofiler alles auslesen. Bin jetzt bei 25k von 75k dateien und es ist schon extrem langsam. Jetzt gerade benötigen 100 Dateien 6 sekunden (also in der 2. Schleife). Bei meinem Testordner, der ja 2600 Dateien hatte, benötigten 100 Dateien weniger wie eine Sekunde (also in der 2. schleife). Ich denke, das sagt alles aus oder? Das Skript verlangsamt sich enorm mit der Zeit. Liegt das an den vielen += ?

Kann jemand vlt. nochmals ganz kurz über die 2. Schleife im neuen Skript schauen?

Weil am Speicher liegt es nicht. Ich habe kaum RAM-Auslastung.

Monger
2023-12-10, 20:50:43
Das mit den Debug Ausgaben ist viel zu kompliziert. Spar dir die Collections, das +=, das Write-Host. Nutze Write-Debug, Write-Warning, Write-Error und Write-Verbose. Die meisten dieser Streams werden in der Konsole standardmäßig nicht geloggt, aber wenn du z.B.
$DebugPreference = Continue
setzst, spuckt die Konsole die Meldungen farblich unterschiedlich aus. Wenn du die unbedingt noch in ne Datei haben willst, kann man das beim Aufruf des Skripts in ne Datei umleiten. Siehe hier:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-7.4

Aber das Logging macht dir glaub grad mehr Scherereien als es dir hilft.
Weißt du eigentlich wie man debuggt? Breakpoints setzt und Variablen abfragt?

Platos
2023-12-10, 21:06:52
Das mit den Debug Ausgaben ist viel zu kompliziert. Spar dir die Collections, das +=, das Write-Host. Nutze Write-Debug, Write-Warning, Write-Error und Write-Verbose. Die meisten dieser Streams werden in der Konsole standardmäßig nicht geloggt, aber wenn du z.B.
$DebugPreference = Continue
setzst, spuckt die Konsole die Meldungen farblich unterschiedlich aus. Wenn du die unbedingt noch in ne Datei haben willst, kann man das beim Aufruf des Skripts in ne Datei umleiten. Siehe hier:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_redirection?view=powershell-7.4

Aber das Logging macht dir glaub grad mehr Scherereien als es dir hilft.
Weißt du eigentlich wie man debuggt? Breakpoints setzt und Variablen abfragt?

Wie soll ich mir das += sparen? Das kann ich doch nicht weglassen. Ich muss ja die Variablen aktualisieren, indem ich eben diese Anhängsel an diese Variablen anhänge. Das verstehe ich nicht so ganz

und bezüglich Debugging: Also wie gesagt, das kommt vermutlich ganz weg. Also $debug 2 kommt vermutlich weg. Ich lasse es einfach noch drinn, bis der Code final ist. Aber $debug2 Sachen kann man ignorieren.

Warum macht mir das Logging mehr Scherereien ? Eig. achte ich gar nicht darauf.

Ja also Variablen habe ich schon abgefragt ja. Breakpoints nicht. Aber Debuggen muss ich eig nichts mehr, weil der Code an sich ja funktioniert. Ich lasse es einfach drinn, da ich noch am optimieren bin und falls da mal etwas nicht funktioniert, kann ich das vlt. noch gebrauchen (aber dann spielt es auch keine Rolle, wenns bisschen länger geht).

#44
2023-12-11, 07:03:47
Ja, funktionieren tut es seit Version 14. Die Version 15.3 ist da eine Ausnahme. ich habe jetzt aber nochmals den Code etwas überarbeitet und konnte 10k Zeichen sparen und es funktioniert alles, wie es sollte.
Da bin ich mir nicht ganz sicher.

Dein Skript beginnt damit die txt-Dateien im rootverzeichnis zu analysieren. Danach werden neue txt-Dateien geschrieben. Zum Schluss wird ausgegeben, was am Anfang ausgelesen wurde - gemischt mit Daten dazu, was in diesem Durchlauf kopiert wurde.

D.h. die Analyse der txt-Dateien in der Ausgabe am Ende bezieht sich auf den vorherigen Skriptdurchlauf.

Soll das wirklich so sein?

€: Das habe ich wohl falsch verstanden. Die txt Dateien sind von Anfang an da und werden nicht vom Skript erzeugt.

€2: Wieso eigentlich die zigfache Verschachtelung der nach dem DDS-Format benannten Ordner in der Ausgabe?

myMind
2023-12-11, 08:31:19
Wie soll ich mir das += sparen? Das kann ich doch nicht weglassen. Ich muss ja die Variablen aktualisieren, indem ich eben diese Anhängsel an diese Variablen anhänge. Das verstehe ich nicht so ganz

Statt

if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}



Write-Debug "Breite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"

Die $output2 Variable löschen.


Alternativ mit Zeitstempel:

$logTrace = $false
$logDebug = $false

function TS {Get-Date -Format 'ddMMyyyy hh:mm:ss'}

function LogInfo ([string]$message) {
"$(TS) $message" | Write-Host
}

function LogDebug ([string]$message) {
if ($logDebug) {
"$(TS) $message" | Write-Host
}
}

function LogTrace ([string]$message) {
if ($logTrace) {
"$(TS) $message" | Write-Host
}
}


und dann

LogDebug "Breite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"

Monger
2023-12-11, 08:36:36
@MyMind: du meinst bei LogDebug sicher Write-Debug, nicht Write-Host, oder? LogInfo und LogTrace analog dazu.

#44
2023-12-11, 08:49:04
Aber ich denke, es wäre sinnvoll, mal mein aktuelles Skript zu posten mit den neuesten Änderungen:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-upscalingDDS\rom2\log.txt"
Start-Transcript -Path $logFile -Append
Measure-Script {
# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-ARCtool-entpackte\rom-ARCtool\ARCtool\rom"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-upscalingDDS\rom2"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-upscalingDDS\rom2\rom2-mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$godFatherFolder += "_UltraLowRes"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$godFatherFolder += "_color"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

$conditionMet = $false

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if ($conditionMet -eq $false -and (& $resolutionCheckItem.Condition)) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
$conditionMet = $true
break
}
}
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

$fileNameCheckMet = $false

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($fileNameCheckMet -eq $false -and $ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$fileNameCheck = $filenameCheckItem.FileName
if ($fileNameCheck -eq "_DM" -and $parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
} else {
$parentFolder += $fileNameCheck
}
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), Item: $($filenameCheckItem.Description), fileNameCheck: ${fileNameCheck}" }
$fileNameCheckMet = $true
break
}
}
}

# Erstellen Sie den GodFatherFolder im Ausgabeordner
if (-not (Test-Path $godFatherFolder)) {
$testPathResult = Test-Path $godFatherFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${godFatherFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $godFatherFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${godFatherFolder}: ${newItemResult}" }
}


# Erstellen Sie den GrandParentFolder im Ausgabeordner
if (-not (Test-Path $grandParentFolder)) {
$testPathResult = Test-Path $grandParentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${grandParentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $grandParentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${grandParentFolder}: ${newItemResult}" }
}

# Erstellen Sie den ParentFolder im Ausgabeordner
$parentFolder += $resolution
if (-not (Test-Path $parentFolder)) {
$testPathResult = Test-Path $parentFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${parentFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $parentFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${parentFolder}: ${newItemResult}" }
}

# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name)) {
if ($destinationFolder -match "_\d+$") {
$destinationFolder = $destinationFolder -replace "_\d+$", "_$i"
} else {
$destinationFolder = $destinationFolder + "_$i"
}
$i++
}

if (-not (Test-Path $destinationFolder)) {
$testPathResult = Test-Path $destinationFolder
if ($debug2) { $output2 += "`nTest-Path Ergebnis für ${destinationFolder}: ${testPathResult}" }

$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
if ($debug2) { $output2 += "`nNew-Item Ergebnis für ${destinationFolder}: ${newItemResult}" }
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder
if ($debug2) { $output2 += "`nCopying file: $($ddsFile.FullName) to ${destinationFolder}" } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $($mapping.Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}




if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalCopiedDuplicates Duplikate in 'Sonderordner' kopiert."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3
}
# Am Ende des Skripts
Stop-Transcript

Ich helfe mal ein bisschen beim Aufräumen:

Zeilen 99-103 sind nutzlos.
$parentFolder und $grandParentFolder werden im nachfolgenden if/else in jedem Pfad überschrieben bevor sie irgendwie verwendet werden.

Zeilen 107-109 können in eine kombiniert werden. $godFatherFolder und $grandParentFolder werden nicht weiter verarbeitet.

Zeilen 116-118 das Gleiche.

Zeile 164: Wird durch das direkt darauf folgende break nutzlos. $conditionMet kann komplett entfallen.

Zeile 199: Das selbe für $fileNameCheckMet

Zeile 205 - 232 sind ebenfalls nutzlos.
Beim Erstellen des DestinationFolder werden sowieso automatisch alle übergeordneten Ordner mit angelegt, wenn sie nicht existiern.

Zeile 248: Nutzlos. Durch die Prüfung eine Zeile davor wissen wir bereits, dass das Ergebnis "false" ist. Das für das selbe Ergebnis nochmal auszuführen bremst hier nur.

Ich glaube zwar nicht, dass es die Performance groß beeinflusst - aber mit weniger Code zu arbeiten ist immer besser. Sei es nur, weil man nicht von überflüssigem abgelenkt wird.
€: Wobei doch einige - teilweise doppelte! - Dateisystemoperationen entfallen :uponder:

myMind
2023-12-11, 09:01:30
@Monger: Ja, das kann man variieren.


$DebugPreference = "Continue"
$VerbosePreference = "Continue"
Write-Debug "Debug"
Write-Verbose "Verbose"

Also statt $logTrace dann mit $VerbosePreference und anstelle von $logDebug dann mit $DebugPreference arbeiten. Das ist dann mehr im Sinne des Powershell Standardansatzes.

Z.B.

$DebugPreference = "Continue"
$VerbosePreference = "Continue"

function TS {Get-Date -Format 'ddMMyyyy hh:mm:ss'}

function LogInfo ([string]$message) {
"$(TS) $message" | Write-Host
}

function LogDebug ([string]$message) {
if ($DebugPreference -eq "Continue") {
"$(TS) $message" | Write-Debug
}
}

function LogVerbose ([string]$message) {
if ($VerbosePreference -eq "Continue") {
"$(TS) $message" | Write-Verbose
}
}

Monger
2023-12-11, 09:07:51
function LogDebug ([string]$message) {
if ($DebugPreference -eq "Continue") {
"$(TS) $message" | Write-Debug
}
}

Wenn du das so machst, brauchst du auch kein If. Write-Debug schreibt eh ins Leere wenn DebugPreference noch auf SilentlyContinue steht.

myMind
2023-12-11, 09:12:06
Wenn du das so machst, brauchst du auch kein If. Write-Debug schreibt eh ins Leere wenn DebugPreference noch auf SilentlyContinue steht.
Stimmt.
Ich würde auch das Format noch ändern:

function TS {Get-Date -Format 'ddMMyyyy hh:mm:ss.fff'}

Freestaler
2023-12-11, 09:25:37
Wie wärs wenn du mal effektiven Satz, also die Files und Script zum testen bereit stellst.(files abgespeckt reicht also z.b. 1000 stk. wenn das bereits länger als 60s. Läuft)

Aktuell, so frech ich bin, reden wir über optimierungen auf theoretischen level. Debug aufbau schön, wenn aber sowieso deaktiviert, wer interessierts ? Ja, ich weiss, den Dev Monk in einem.

Einzig die Vorschläge von #44 sind wesentlich meines erachtens.

Aber am einfachsten wäre mMn. wenn du ein Testset bereitstellst und allenfalls das Ziel des Scripts (nicht den Ablauf, den hast du schon) bereitstellst.

Platos
2023-12-11, 14:36:00
Also ich habe jetzt bei 2 Testordner die Powershell.exe auf einen CPU Thread fixiert und festgestellt, dass die CPU Lat über 97% ist. Da ich ja nix selber parallelisiert habe, gehe ich mittlerweile davon aus, dass es ein CPU Bottleneck ist.

was meint ihr?

Ich habe gestern versucht, mit diesen "Jobs" die kleine Schleife zu parallelisieren (2x parallel laufen lassen). Hat nicht funktioniert, also ich habs nicht hingekriegt.

Was denkt ihr, liegt es am SIngle-Thread Skript ? Könnte es Früchte tragen, das zu parallelisieren? Soll ich vlt. mal das nicht funktionsfähige Skript mit der Parallelisierung hier posten?

Wichtig zu beachten ist, dass ich ja die alte Powershell nutze (also nicht Version 7), weil das Skript mit vorinstallierten Dingen laufen soll. D.h auf Powershell 7 ausweichen will ich nicht. Mehr dazu steht in meiner ANtwort zu #44



€: Das habe ich wohl falsch verstanden. Die txt Dateien sind von Anfang an da und werden nicht vom Skript erzeugt.

€2: Wieso eigentlich die zigfache Verschachtelung der nach dem DDS-Format benannten Ordner in der Ausgabe?

Das dient primär 3 Sachen:

- Übersichtlichkeit
- Sortierung eben, was wiederum dem..
- Batchupscaling dient. Ich scale die Texturen hoch. NM muss ich anders upscalen und ich habe nun alle anderen Textur-typen auch gleich mitgenommen in die Trennung, so dass ich spezifischer upscalen kann. Dann noch nach Auflösung weil: die ganz kleinen Texturen upscale ich nicht, ich sortiere sie quasi aus. color_ID ist zwar nicht Auflösung, sortiere ich aber auch aus. Dann LowRes, weil das eben meistens so kleine Bildchen sind, die man separat behandeln könnte/müsste (auf andere Weise upscalen). Dann noch die hohen Auflösungen aus 2 Gründen: Erstens, um verschieden hoch upzuscalen, denn ich nutze dann Faktoren (4x) und das ist so gemacht, dass Texturen, die höher als ne bestimmte Auflösung werden würden, dann in ein Ordner kommen. Und hier gibts verschiedene, um verschieden hohes Upscaling einfach zu ermöglichen. Und dann Sqaure, Tall, Wide um wieder besser batchupscaling zu machen. Und dann gibts noch ne andere Klasse, die dafür sorgt, das Limit dieses Spiels von 6144x6144 nicht überschritten wird. Also kurz gesagt: Viele Gründe :D

Die will ich auch alle da lassen.

Ich habe übrigens gestern dann weiteres optimieren vorerst aufgegeben und mich nochmal an dein Rat des parallelisieren gewandt: Ich habe die Powershell auf einen Thread fixiert im TaskManager und hatte dann 97% oder höhere Auslastung. Daher habe ich mich jetzt dem Versuch gewagt, zu parallelisieren. Wie du dir vorstellen kannst, ist das hart misglückt und ich finde nicht heraus, wieso. Ich will ja, dass es auf der normalen Powershell läuft, die Windows vorinstalliert hat (also nicht 7). Und da kann man glaube ich nur diese Jobs nutzen. DAs Problem: Ich versuche gerade mit host-writes bzw- host-outputs herauszuinfden, warum es nicht geht. Nur es will nix aus dem Job-Scriptblock raus kommen. Da kommt nix mehr raus.



Statt

if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}



Write-Debug "Breite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"

Die $output2 Variable löschen.




Also danke für die Hilfe, aber muss es nochmals erwähnen: Bitte beachtet debugging nicht, speziell $debug2 nicht. Wie gesagt, habe ich das auf false und wie auch schon gesagt, wird das dann am Ende im finalen Skript alles raus gelöscht. IRgendwas da optimieren ist Zeitverschwendung.

Ich helfe mal ein bisschen beim Aufräumen:

Zeilen 99-103 sind nutzlos.
$parentFolder und $grandParentFolder werden im nachfolgenden if/else in jedem Pfad überschrieben bevor sie irgendwie verwendet werden.

Zeilen 107-109 können in eine kombiniert werden. $godFatherFolder und $grandParentFolder werden nicht weiter verarbeitet.

Zeilen 116-118 das Gleiche.

Zeile 164: Wird durch das direkt darauf folgende break nutzlos. $conditionMet kann komplett entfallen.

Zeile 199: Das selbe für $fileNameCheckMet

Zeile 205 - 232 sind ebenfalls nutzlos.
Beim Erstellen des DestinationFolder werden sowieso automatisch alle übergeordneten Ordner mit angelegt, wenn sie nicht existiern.

Zeile 248: Nutzlos. Durch die Prüfung eine Zeile davor wissen wir bereits, dass das Ergebnis "false" ist. Das für das selbe Ergebnis nochmal auszuführen bremst hier nur.

Ich glaube zwar nicht, dass es die Performance groß beeinflusst - aber mit weniger Code zu arbeiten ist immer besser. Sei es nur, weil man nicht von überflüssigem abgelenkt wird.
€: Wobei doch einige - teilweise doppelte! - Dateisystemoperationen entfallen :uponder:

Bezüglich 99-103: Verstehe ich nicht, das ist die erste definition, ich brauche die Definition der Variablen, um nachher etwas an den variablen anhängen zu können? Die Definition wird nur überschrieben, wenn if zutrifft für die _UltraLowRes und die _color Prüfung. Sie wird aber gebraucht für die elseif prüfung auf "NM", denn da wird sie nicht überschrieben.

Bezüglich 107-109: Wie meinst du das, sie werden nicht weiter verarbeitet? Also bei Zeile 205-232 werden $grandParentFolder und $parentFolder erstellt. Diese sind aber nicht so definiert, wie ich es in Zeile 107-109 getan habe. Würde ich sie in 107-109 nicht separat definieren, würden die Ordnernamen nicht so herauskommen. Um zu erklären warum (vlt. liege ich auch falsch, dann sags mir): der GrandParentFolder würde normalerweise im godFatherFolder erstellt werden und zwar mit dem Namen des godFatherFolder. Das ist aber nicht das gleiche, wie wenn ich sie gleich setze, denn wenn ich sie gleich setzte, heisst das, dass der GrandParentfolder im selben Verzeichnis erstellt wird, wie der GodFatherFolder (und das mit gleichem Namen). Das führt unter Windows automatisch dazu, dass die Ordner verschmelzen und das ist auch das Ziel. Das selbe Prinzip gilt für den Parentfolder. Der soll auch verschmelzen. Ich musste das so machen, weil der DestinationFolder darf auf keinen Fall verschwinden und dieser basiert auf dem ParentFolder und der wiederum auf den GrandparentFolder usw. D.h ich muss die schon haben.

Zeile 164: Kannst du mir das erklären warum? Ich sehe das erst jetzt. Wofür ist brake? Bzw. zu was führt es?

Zeile 205-232: Ah echt, ist das so? Werden dann die anderen 3 Überordner direkt erstellt oder wird dass dann so gemacht, dass 3 mal kein Überordner erkannt wird und dann muss 3 mal separat einer erstellt werden? Aber wäre ja gut, wenn das gar nicht gemacht werden müsste.

zu 248: Du meinst diese Zeile: $testPathResult = Test-Path $destinationFolder

Die kann ich einfach raus löschen? Ich weiss gar nicht, warum das da steht. Ich glaube, das ist damals wegen dem $debug2 darunter eingeführt worden um das auszugeben. Das könnte vlt. die Perofmance beeinflussen (im positiven). Weil das Erstellen des DestinationFolders hat viel Zeit verurssacht laut PSProfiler.

Wie wärs wenn du mal effektiven Satz, also die Files und Script zum testen bereit stellst.(files abgespeckt reicht also z.b. 1000 stk. wenn das bereits länger als 60s. Läuft)

Aktuell, so frech ich bin, reden wir über optimierungen auf theoretischen level. Debug aufbau schön, wenn aber sowieso deaktiviert, wer interessierts ? Ja, ich weiss, den Dev Monk in einem.

Einzig die Vorschläge von #44 sind wesentlich meines erachtens.

Aber am einfachsten wäre mMn. wenn du ein Testset bereitstellst und allenfalls das Ziel des Scripts (nicht den Ablauf, den hast du schon) bereitstellst.

Also 2700 Dateien gehen 18 sekunden. Das Problem ist ja, dass bei mgrossen 75k Ordner das Skript immer langsamer wird, je länger es läuft (also je mehr dateien es verarbeitet hat). Dieser Effekt ist bei den 2700 Dateien zwar spürbar, aber nicht sehr gross. Deswegen halte ich die Testmethode für schlecht, da es nicht das Problem testen kann. Ich habe aber mittlerweile ein anderen Testordner, der hat viel mehr, dafür kleinere Dateien. Dafür benötige ich mit dem aktuellen Skript 2min 35 Sekunden und die Verlangsamung im Verlauf des skripts ist schon deutlich spürbar (man siehts hald am Fortschrittsbalken, wie schnell der Zähler hoch geht). Du "brauchst" also so viele Dateien, damit du mehr als die gewünschten 60s kriegst. Ich habe auch bei diesem Testordner (in einem zweiten Test) die powershell.exe auf einen Thread fixiert und die CPU Last war dauerhaft 100%. Aber die anderen Kernen waren auch nicht unbedingt extrem niedrig. Ich glaube der Testornder ist noch forderner. Ideal für Tests :D Also meine Vermutung ist mittlerweile: Single-Thread Bottleneck.

Aber hier der eq-ordner gezippt (hoffe du kannst schnell entpacken :) )

: https://uploadnow.io/f/sLtd1BQ

Das Ziel des Skripts, ist vom Prinzip her einfach, ausführlich aber sehr schwierig zu erklären. Einfaches Prinzip:
Im Rootfolder sollen alle Ordner und Unterordner nach .dds Dateien kopiert werden und nach:

- DDSFormat
- Auflösung
- Bestimmten Dateinamenmuster

...sortiert werden (und entsprechend benannte Ordner erstellt werden). Das DDSFormat kann man aber nicht mit der Powershell auslesen. Deswegen behilft sich das Skript auf die ganzen .txt Dateien, die es für jede .dds Datei gibt (bzw. eig. für jede .tex datei).

D.h, im Grunde muss follgendes gemacht werden:

- Im Rootfodler und seinen Unterordnern soll nach .txt Dateien gesucht werden, die "DDSFormat=" enthalten, dann wird der Wert hinter "DDSFormat=" und nebenbei auch gleich von "Width=" und "Height=" extrahiert bzw. in einer Variable gespeichert.
- Dann werden die zugehörigen (= im selben Ordner befindlichen, gleichnamigen) .dds Dateien gesucht.
- Dann werden die ganzen .dds Dateien nach eben einem Muster kopiert bzw. eine Ordnerstruktur erstellt.

Die genauen Dateinamen-Checks und Auflösungschecks stehen im Skript. Wenn ich das hier erklären würde, müsste ich 3 Seiten schreiben. Oder willst du, dass ich das mache? Kann ich schon :D

Platos
2023-12-11, 14:44:11
Also ich habe mich jetzt entschieden, hier mal der missglückte Parallelisierungsversuch von mir zu posten. Vlt. sieht ja jemand etwas auf den ersten Blick:

Es ist "nur" das Skript vom Anfang bis VOR die 2. Foreach Schleife (um hier nicht zu sehr zu spamen). Der Teil basiert direkt auf der letzten Version, die ich hier geschickt habe. Jetzt eben noch mit dem missglücktem Versuch der Parallelisierung.

Mein Problem ist: Es funktioniert hald einfach nicht. Die Write-Hosts NACH der parallelisierten Schleife (bzw. direkt vor der 2. Schleife) sagen alle, dass die ganzen Variablen 0 sind.

Jetzt wollte ich eben Debugging-Zeugs IN die schleifen einbauen, aber ich kriege es nicht hin, dass die angezeigt werden. Ich habe es schon mit write-output und nem Pfad probiert, hat aber nicht geholfen (der pfad ist in dieser Version noch nicht drinn. Die andere Version ist wegen den ganzen Write-Outputs aber unübersichtlicher).

Bitte beachtet aber: Ich will bei dieser Powershellversion bleiben und nicht 7 nutzen.

Hat da jemand eine Idee?

edit: Der grund, warum ich die erste Schleife genommen habe, wo doch die 2. der eigentliche Zeitfresser ist, ist der, dass diese einfach deutlich weniger komplex ist.

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

# Starten Sie den ersten Job
$job1 = Start-Job -ScriptBlock {
param($files)

# Definieren Sie die Funktion Search-DDSFormat innerhalb des Skriptblocks
function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
Write-Output "Content of file ${filePath}: ${content}" # Debugging-Ausgabe
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

$k = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalTxtFiles = 0
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $files.Count" -PercentComplete (($k / $files.Count) * 100)
}
$result = Search-DDSFormat -filePath $file.FullName
Write-Output "Result for file ${file.FullName}: ${result}" # Debugging-Ausgabe
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}
return @{
'totalTxtFilesWithDDSFormat' = $totalTxtFilesWithDDSFormat
'totalTxtFilesWithWidth' = $totalTxtFilesWithWidth
'totalTxtFilesWithHeight' = $totalTxtFilesWithHeight
'totalTxtFiles' = $totalTxtFiles
'ddsFormatFiles' = $ddsFormatFiles
}
} -ArgumentList $files1

# Empfangen Sie die Ausgabe des Jobs
Receive-Job $job1 -Wait

# Starten Sie den zweiten Job
$job2 = Start-Job -ScriptBlock {
param($files)

# Definieren Sie die Funktion Search-DDSFormat innerhalb des Skriptblocks
function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
Write-Output "Content of file ${filePath}: ${content}" # Debugging-Ausgabe
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

$k = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalTxtFiles = 0
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $files.Count" -PercentComplete (($k / $files.Count) * 100)
}
$result = Search-DDSFormat -filePath $file.FullName
Write-Output "Result for file ${file.FullName}: ${result}" # Debugging-Ausgabe
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}
return @{
'totalTxtFilesWithDDSFormat' = $totalTxtFilesWithDDSFormat
'totalTxtFilesWithWidth' = $totalTxtFilesWithWidth
'totalTxtFilesWithHeight' = $totalTxtFilesWithHeight
'totalTxtFiles' = $totalTxtFiles
'ddsFormatFiles' = $ddsFormatFiles
}
} -ArgumentList $files2

# Empfangen Sie die Ausgabe des Jobs
Receive-Job $job2 -Wait

# Warten Sie, bis beide Jobs abgeschlossen sind
Wait-Job $job1, $job2

# Holen Sie die Ergebnisse der Jobs
$results1 = Receive-Job $job1
$results2 = Receive-Job $job2

# Fügen Sie die Ergebnisse zusammen
$totalTxtFilesWithDDSFormat = $results1.totalTxtFilesWithDDSFormat + $results2.totalTxtFilesWithDDSFormat
$totalTxtFilesWithWidth = $results1.totalTxtFilesWithWidth + $results2.totalTxtFilesWithWidth
$totalTxtFilesWithHeight = $results1.totalTxtFilesWithHeight + $results2.totalTxtFilesWithHeight
$totalTxtFiles = $results1.totalTxtFiles + $results2.totalTxtFiles

# Fügen Sie die Listen von Dateien zusammen
$ddsFormatFiles = $results1.ddsFormatFiles
$ddsFormatFiles.AddRange($results2.ddsFormatFiles)

# Klonen Sie $results1 und fügen Sie die Elemente von $results2 hinzu
$results = $results1.Clone()
foreach ($key in $results2.Keys) {
$results.set_Item($key, $results2[$key])
}

# Geben Sie die Werte der Variablen aus
Write-Host "totalTxtFilesWithDDSFormat: $totalTxtFilesWithDDSFormat"
Write-Host "totalTxtFilesWithWidth: $totalTxtFilesWithWidth"
Write-Host "totalTxtFilesWithHeight: $totalTxtFilesWithHeight"
Write-Host "totalTxtFiles: $totalTxtFiles"
Write-Host "ddsFormatFiles: $ddsFormatFiles"
Write-Host "results: $results"

#44
2023-12-11, 14:51:12
Also ich habe jetzt bei 2 Testordner die Powershell.exe auf einen CPU Thread fixiert und festgestellt, dass die CPU Lat über 97% ist. Da ich ja nix selber parallelisiert habe, gehe ich mittlerweile davon aus, dass es ein CPU Bottleneck ist.

was meint ihr?

Ich habe gestern versucht, mit diesen "Jobs" die kleine Schleife zu parallelisieren (2x parallel laufen lassen). Hat nicht funktioniert, also ich habs nicht hingekriegt.

Was denkt ihr, liegt es am SIngle-Thread Skript ? Könnte es Früchte tragen, das zu parallelisieren? Soll ich vlt. mal das nicht funktionsfähige Skript mit der Parallelisierung hier posten?
Dann scheint zumindest meine Vermutung schon mal richtig.
Das heißt in der Folge also auch, dass man mit Optimierung in Richtung weniger CPU-Last etwas gewinnen kann.

Parallelisieren muss man dazu nicht generell. (Was nicht heißt, dass es in diesem Fall nicht vielleicht das einzig sinnvolle ist; müsste man mal sehen. Dafür wäre es aber sinnig, den Code erstmal besser zu strukturieren.)


- Batchupscaling dient. Ich scale die Texturen ab. NM muss ich anders upscalen und ich habe nun alle anderen Textur-typen auch gleich mitgenommen in die trennung, so dass ich spezifischer upscalen kann. Dann noch nach Auflsöung weil: die ganz kleinen Texturen upscale ich nicht, ich sortiere sie quasi aus. color_ID ist zwar nicht auflösung, sortiere ich aber auch aus. Dann lowres, weil das eben meistens so kleine bildchen sind, die man separat behandeln könnte (auf andere weise). Dann noch die hohen auflösungen aus 2 Gründen: Erstens, um verschieden hoch upzuscalen, denn ich nutze dann faktoren (4x) und das so gemacht, dass Texturen, die höher als ne bestimmte Auflösung werden würden, dann in ein Ordner kommen. Und hier gibts verschiedene, um verschieden hohes Upscaling einfach zu ermöglichen. Und dann Sqaure, Tall, Wide um wieder besser batchupscaling zu machen. Und dann gibts noch ne andere Klasse, die dafür sorgt, das Limit dieses SPiels von 6144x6144 nicht zu überschreiten. Also kruz gesagt: Viele Gründe :D

Die Frage ist halt, ob man das in so viele Unterebenen zerteilen muss. Was spricht bspw. gegen je ein Ordner für jeden Fall direkt im Ausgabeverzeichnis?

Wenn man eine bessere Struktur finden kann, lässt sich vielleicht auch einfacher sortieren. Optimieren heißt in vielen Fällen nicht bessere Befehle für das gleiche zu finden - sondern ggf. eine andere Herangehensweise zu nutzen, die für den jeweiligen Flaschenhals besser geeignet ist.


Bezüglich 99-103: Verstehe ich nicht, das ist die erste definition, ich brauche die Definition der Variablen, um nachher etwas an den variablen anhängen zu können? Die Definition wird nur überschrieben, wenn if zutrifft für die _UltraLowRes und die _color Prüfung. Sie wird aber gebraucht für die elseif prüfung auf "NM", denn da wird sie nicht überschrieben.

Solange auf die Definition keine Verwendung, sondern eine Umdefinition folgt kann die Variable auch einfach durch den späteren Befehl erstmalig definiert werden. Die erste Definition wird schließlich nicht verwendet...
Und nein - NM wird an den godFather angehängt - nicht an Parent oder Grandparent. Die beiden werden erst viel später benutzt. (Schönes Fallbeispiel, wieso aufgeräumter Code gut ist - er verwirrt weniger)


Bezüglich 107-109: Wie meinst du das, sie werden nicht weiter verarbeitet? Also bei Zeile 205-232 werden $grandParentFolder und $parentFolder erstellt. Diese sind aber nicht so definiert, wie ich es in Zeile 107-109 getan habe. Würde ich sie in 107-109 nicht separat definieren, würden die Ordnernamen nicht so herauskommen. Um zu erklären warum (vlt. liege ich auch falsch, dann sags mir): der GrandParentFolder würde normalerweise im godFatherFolder erstellt werden und zwar mit dem Namen des godFatherFolder. Das ist aber nicht das gleiche, wie wenn ich sie gleich setze, denn wenn ich sie gleich setzte, heisst das, dass der GrandParentfolder im selben Verzeichnis erstellt wird, wie der GodFatherFolder (und das mit gleichem Namen). Das führt unter Windows automatisch dazu, dass die Ordner verschmelzen und das ist auch das Ziel. Das selbe Prinzip gilt für den Parentfolder. Der soll auch verschmelzen. Ich musste das so machen, weil der DestinationFolder darf auf keinen Fall verschwinden und dieser basiert auf dem ParentFolder und der wiederum auf den GrandparentFolder usw. D.h ich muss die schon haben.


Zeile 205-232: Ah echt, ist das so? Werden dann die anderen 3 Überordner direkt erstellt oder wird dass dann so gemacht, dass 3 mal kein Überordner erkannt wird und dann muss 3 mal separat einer erstellt werden? Aber wäre ja gut, wenn das gar nicht gemacht werden müsste.

Die Beiden hängen zusammen. Da du $godFatherFolder und $gradParentFolder eben nicht händisch anlegen musst, reicht es wenn die $parentFolder Variable korrekt erzeugt (in dem Fall: Ergänzt) wird. Das kann man auch ohne die beiden anderen Variablen.
$parentFolder = $godFatherFolder + "_UltraLowRes"

Hat bei mir Lokal das gleicher Ergebnis geliefert. Aber da wäre ein einheitliches Testset sinvoll um Grenzfälle zu erkennen.


Zeile 164: Kannst du mir das erklären warum? Ich sehe das erst jetzt. Wofür ist brake? Bzw. zu was führt es?

break ist ein Befehl um Schleifen und Switch-Statements direkt an dieser Stelle abzubrechen. Genau das gleiche, was die Variablen tun sollen - diese müssen dazu aber erst in der Schleifenbedingung ausgewertet werden - was aber nicht mehr passiert, denn das break hat die Schleife schließlich schon beendet.

zu 248: Du meinst diese Zeile: $testPathResult = Test-Path $destinationFolder

Die kann ich einfach raus löschen? Ich weiss gar nicht, warum das da steht. Ich glaube, das ist damals wegen dem $debug2 darunter eingeführt worden um das auszugeben. Das könnte vlt. die Perofmance beeinflussen (im positiven). Weil das Erstellen des DestinationFolders hat viel Zeit verurssacht laut PSProfiler.


Ja genau. Da wird für die Debugausgabe der Befehl nochmal ausgeführt um den Rückgabewert ausgeben zu können. Durch die Bedingung im if wissen wir aber, was das Ergebnis dieses Aufrufs gewesen sein muss.

myMind
2023-12-11, 15:02:52
Also danke für die Hilfe, aber muss es nochmals erwähnen: Bitte beachtet debugging nicht, speziell $debug2 nicht. Wie gesagt, habe ich das auf false und wie auch schon gesagt, wird das dann am Ende im finalen Skript alles raus gelöscht. IRgendwas da optimieren ist Zeitverschwendung.

Warum sollte es Zeitverschwendung sein endlich Zeitstempel an den Befehlen zu haben? Ich hab es oben schon mal geschrieben:

Debugging != Logging

Was du als "Debugging" bezeichnest, ist eigentlich etwas anderes. Es ist das was ein Logger macht. Und der ist bei dir einfach nur mist, weil er keine Zeitstempel liefert.

Beim Debugging arbeitest du mit dem Debugger, schaust dir Werte und den Ablauf im einzelnen an. Das kannst Du z.B. in VSCode wunderbar machen.

Du kannst nur dann zielgerichtet optimieren, wenn du die richtigen Informationen heranschaffst. Das mit dem Profiling war ja schon mal nicht schlecht, aber dort siehst du z.B. keine Ausreisser oder Tendenzen sondern nur Durchschnittswerte.
Parallelisieren kostet erstmal Performance und ist nur dann sinnvoll, wenn du dadurch brachliegende Resourcen aktivieren kannst. Also z.B. wenn die CPU tatsächlich limitierend ist und du noch Kerne über hast. Im Profiler hat man aber gesehen, dass Get-Content viel Zeit verschlingt und das hängt an der IO.

Platos
2023-12-11, 15:37:40
Dann scheint zumindest meine Vermutung schon mal richtig.
Das heißt in der Folge also auch, dass man mit Optimierung in Richtung weniger CPU-Last etwas gewinnen kann.

Parallelisieren muss man dazu nicht generell. (Was nicht heißt, dass es in diesem Fall nicht vielleicht das einzig sinnvolle ist; müsste man mal sehen. Dafür wäre es aber sinnig, den Code erstmal besser zu strukturieren.)

Ja, das stimmt. Nur schon das zusammenfassen der $resolution und $fileNameCheck-Checks haben alles viel übersichtlicher gemacht. Aber deswegen habe ich auch die erste Schleife genommen, die ja da nicht so komplex ist. Aber es scheitert schon daran. Und wenn ich es dort nicht kann, probiere ich die andere erst gar nicht, denn die ist wesentlich schwieriger, da ich dort Jobs an einer bestimmen Stelle halten muss. Das soll nämlich auf in älteren Powershell-Versionen mit

Wäre es dann aber nicht sinnvoller, zuerst einmal zu schauen, ob Parallelisieren überhjaupt funktioniert ? ALso eben an einem "einfachen" Beispiel, nämlich der ersten Schleife (Das verusche ich gerade).

Die zweite Schleife kann man ja dann optimieren, wenn die erste Schleife geklappt hat.

Die Frage ist halt, ob man das in so viele Unterebenen zerteilen muss. Was spricht bspw. gegen je ein Ordner für jeden Fall direkt im Ausgabeverzeichnis?

Wenn man eine bessere Struktur finden kann, lässt sich vielleicht auch einfacher sortieren. Optimieren heißt in vielen Fällen nicht bessere Befehle für das gleiche zu finden - sondern ggf. eine andere Herangehensweise zu nutzen, die für den jeweiligen Flaschenhals besser geeignet ist.

Ok, aber perfomance möchte ich definitiv nicht durch funktionalität tauschen. Der grund ist, dass ich tausend Ordner dann habe. Der wichtigste Grund ist aber wirklich das Batch-Upscaling. Ich brauche EINEN Ordner, den ich meinem Programm hinschmeissen kann. Wenn ich das nicht tun kann, geht das ganze vorhaben ad absurdum.
Der zweite Grund ist, dass ich vermutlich etwa 100 Ordner hätte ohne Duplikatoprdner eingerechnet. Das ist eine katastrophe.

Also an der Ordnerstruktur bzw. funktionalität werde ich definitiv nichts ändern. Vorher gebe ich mich mit der Perfomance zu frieden, als daran etwas zu ändern.

Solange auf die Definition keine Verwendung, sondern eine Umdefinition folgt kann die Variable auch einfach durch den späteren Befehl erstmalig definiert werden. Die erste Definition wird schließlich nicht verwendet...
Und nein - NM wird an den godFather angehängt - nicht an Parent oder Grandparent. Die beiden werden erst viel später benutzt. (Schönes Fallbeispiel, wieso aufgeräumter Code gut ist - er verwirrt weniger)

Ah, du hast recht, sorry. Ich habe mich falsch erinnert. Ich dachte, in der "NM" Prüfung würde ich $grandParentFolder oder $parentFolder aktualisieren. DAs habe ich nämlich früher mal gemacht und das war dann auch wirklich notwendig, die vorher korrekt zu definieren. Habe jetzt Zeile 99-103 gelöscht. Ist wirklich nicht nötig.

Ganz unten der Link auf Frestaller's Antwort beinhaltet ein Testordner ,der wunderbar zeigt, wie sehr sich das Skript (ziemlich schnell) verlangsamt in der zweiten Schleife (der zweite Progress-balken)

Ich habe nun Zeile 205-232 mal gelöscht und es funktioniert wie erwartet nicht. Die Ordner-Benennung funktioniert nicht mehr. Alle Parentfolder sind falsch.

Die Beiden hängen zusammen. Da du $godFatherFolder und $gradParentFolder eben nicht händisch anlegen musst, reicht es wenn der $parentFolder korrekt erzeugt (in dem Fall: Ergänzt) wird. Das kann man auch ohne diese Variablen.
$parentFolder = $godFatherFolder + "_UltraLowRes"

Hat bei mir Lokal das gleicher Ergebnis geliefert. Aber da wäre ein einheitliches Testset sinvoll um Grenzfälle zu erkennen.

Aber der $godFatherFolder hat nach einem match von "UltraLowRes" ja schon selbst das "_UltraLowRes" Anhängsel. Wenn man also $parentFolder so definiert, müsste da das dann xxxxx_UltraLowRes_UltraLowRes ergeben.

naja , auf jeden Fall habe ich mal gemacht, was du sagst und die Zeile 205-232 gelöscht. Es hat wie erwartet die Ordnererstellung vermurkst. Die ParentFolder heissen nun alle nicht mehr so, wie sie sollten.


break ist ein Befehl um Schleifen und Switch-Statements direkt an dieser Stelle abzubrechen. Genau das gleiche, was die Variablen tun sollen - diese müssen dazu aber erst in der Schleifenbedingung ausgewertet werden - was aber nicht mehr passiert, denn das break hat die Schleife schließlich schon beendet.

Ah, ja ok, dann lösche ich eines der beiden raus. Was wäre denn pefomanter? break zu löschen, oder das ganze System mit dieser $ConditionMet variable ?

Ja genau. Da wird für die Debugausgabe der Befehl nochmal ausgeführt um den Rückgabewert ausgeben zu können. Durch die Bedingung im if wissen wir aber, was das Ergebnis dieses Aufrufs gewesen sein muss.

Ok, dann lösche ich das mal raus.

Warum sollte es Zeitverschwendung sein endlich Zeitstempel an den Befehlen zu haben? Ich hab es oben schon mal geschrieben:

Debugging != Logging

Was du als "Debugging" bezeichnest, ist eigentlich etwas anderes. Es ist das was ein Logger macht. Und der ist bei dir einfach nur mist, weil er keine Zeitstempel liefert.

Beim Debugging arbeitest du mit dem Debugger, schaust dir Werte und den Ablauf im einzelnen an. Das kannst Du z.B. in VSCode wunderbar machen.

Du kannst nur dann zielgerichtet optimieren, wenn du die richtigen Informationen heranschaffst. Das mit dem Profiling war ja schon mal nicht schlecht, aber dort siehst du z.B. keine Ausreisser oder Tendenzen sondern nur Durchschnittswerte.
Parallelisieren kostet erstmal Performance und ist nur dann sinnvoll, wenn du dadurch brachliegende Resourcen aktivieren kannst. Also z.B. wenn die CPU tatsächlich limitierend ist und du noch Kerne über hast. Im Profiler hat man aber gesehen, dass Get-Content viel Zeit verschlingt und das hängt an der IO.

Ok, dann ist es eben kein Debugging, ändert aber nichts daran, dass ich es raus lösche (wie auch immer "es" genannt wird).
Also... Siehe Freestaler's Antwort.

Also wirklich:

Optimierungen am Logging (wie auch immer man es nennt) brauche ich nicht. Es kommt weg...

Du renovierst ein Haus, das morgen abgebrochen wird...

#44
2023-12-11, 15:44:36
Ja, das stimmt. Nur schon das zusammenfassen der $resolution und $fileNameCheck-Checks haben alles viel übersichtlicher gemacht. Aber deswegen habe ich auch die erste Schleife genommen, die ja da nicht so komplex ist. Aber es scheitert schon daran. Und wenn ich es dort nicht kann, probiere ich die andere erst gar nicht, denn die ist wesentlich schwieriger, da ich dort Jobs an einer bestimmen Stelle halten muss. Das soll nämlich auf in älteren Powershell-Versionen mit

Wäre es dann aber nicht sinnvoller, zuerst einmal zu schauen, ob Parallelisieren überhjaupt funktioniert ? ALso eben an einem "einfachen" Beispiel, nämlich der ersten Schleife (Das verusche ich gerade).

Die zweite Schleife kann man ja dann optimieren, wenn die erste Schleife geklappt hat.

Für mich kommt parallelisieren erst in Frage, wenn alles andere ziemlich gut abgeklopft ist. Parallelität bringt Probleme und Komplexität mit sich, die ich wo möglich vermeiden will.



Ich habe nun Zeile 205-232 mal gelöscht und es funktioniert wie erwartet nicht. Die Ordner-Benennung funktioniert nicht mehr. Alle Parentfolder sind falsch.

Zeile 225 ($parentFolder += $resolution) darf nicht weg :redface:

Aber der $godFatherFolder hat nach einem match von "UltraLowRes" ja schon selbst das "_UltraLowRes" Anhängsel. Wenn man also $parentFolder so definiert, müsste da das dann xxxxx_UltraLowRes_UltraLowRes ergeben.

Alles eine Frage der Strukturierung. Beim Programmieren kannst du das gleiche Resultat auf tausende Weisen erreichen. Du brauchst nicht glauben, dass dein Ablauf der einzig mögliche ist, dieses Ergebnis so zu erzielen.


Ah, ja ok, dann lösche ich eines der beiden raus. Was wäre denn pefomanter? break zu löschen, oder das ganze System mit dieser $ConditionMet variable ?

Die Variante mit break ist lesbarer. Performance dürfte fast keine Rolle spielen - da gewinnt man vielleicht ein paar Millisekunden.

Platos
2023-12-11, 15:58:52
Zeile 225 ($parentFolder += $resolution) darf nicht weg :redface:

Die Variante mit break ist lesbarer. Performance dürfte fast keine Rolle spielen - da gewinnt man vielleicht ein paar Millisekunden.

Ah, ja, die zeile 225 muss natürlich bestehen bleiben. Habs probiert, geht tatsächlich. Ja dann danke für den Tipp. Aber hab jetzt angst :D Ich verstehe nicht, warum es funktioniert. Würdest du es mir erklären?

Ist das normal so, dass wenn ich einen Ordner erstellen lasse, der mit einer Variable gebildet wird, dass dann diese Variable auch gleich als Ordner erstellt wird usw. usf. ?

Und bezüglich Break: Ok, wenn es gleich gut ist, ist mir das auch lieber, wenn ich nur ein Wort dafür brauche.

ich schicke gleich nochmals das aktualisierte skript und verlinke nochmals die testdateien usw, dass man sie nicht suchen muss. Ein Moment.

#44
2023-12-11, 15:59:36
Hab eben das Skript mal mit dem Testset ausgeführt.

Ein Grund, wieso die zweite Schleife immer langsamer wird, sind bestimmt Duplikate. Davon enthält dein Testset einen Haufen.

Du hast alleine 122x hair_CMM.dds (siehe DXT1_UltraLowRes Ausgabeordner) und 125x hair_NM.dds (siehe DXT5_UltraLowRes).
Für das x. Auftreten einer Textur muss dein Skript vorher x-1 anderen Pfaden prüfen werden, bis die Deduplizierung geklappt hat. Das sind hier im schlimmsten Fall 124 Prüfungen auf dem Dateisystem nur um eine einzige Textur zu verschieben.

Multithreading hilft gegen dieses Problem denkbar wenig.

€: Erklärung für die Datei-Anlage:

Nehmen wir mal den Ausgabepfad "eq_res\DXT5_NM\DXT5_NM\DXT5_NM\DXT5_NM\e_npc000_01_NM_HQ.dds" (eq_res ist mein Ausgabeverzeichnis)

File: e_npc000_01_NM_HQ.dds
DestinationFolder: eq_res\DXT5_NM\DXT5_NM\DXT5_NM\DXT5_NM
ParentFolder: eq_res\DXT5_NM\DXT5_NM\DXT5_NM
GrandParentFolder: eq_res\DXT5_NM\DXT5_NM
GodFatherFolder: eq_res\DXT5_NM

Kurz: Die ganzen Variablen die du Brauchst enthalten keine nützliche Information. Die Nützlichkeit geht mit jedem Arbeitsschritt auf eine andere Variable über.
GodFather steckt in GrandParent, dieser steckt in Parent und der steckt letztlich auch schon in Destination.
Entsprechend reicht eben eine Variable aus, wenn man sie entsprechend erzeugt.

Und dieser Ordnerpfad ist auch die Grundlage für meine Struktur-Frage. Vier ineinander verschachtelte Ordner mit dem Namen "DXT5_NM" liefern dir doch auch keinen Mehrwert.

Platos
2023-12-11, 16:05:56
Hab eben das Skript mal mit dem Testset ausgeführt.

Ein Grund, wieso die zweite Schleife immer langsamer wird, sind bestimmt Duplikate. Davon enthält dein Testset einen Haufen.

Du hast alleine 122x hair_CMM.dds (siehe DXT1_UltraLowRes Ausgabeordner) und 125x hair_NM.dds (siehe DXT5_UltraLowRes).
Für das x. Auftreten einer Textur muss dein Skript vorher x-1 anderen Pfaden prüfen werden, bis die Deduplizierung geklappt hat. Das sind hier im schlimmsten Fall 124 Prüfungen auf dem Dateisystem nur um eine einzige Textur zu verschieben.

Multithreading hilft gegen dieses Problem denkbar wenig.

Ok, das hört sich gut an (also dass du einen fehler finden konntest).

Die Duplikate muss ich natürlich behalten. Ich muss ja alle upscalen, nicht nur eine (das ist halt bei Games so, die haben die gleichen Texturen mehrfach gespeichert). Es gibt irgendwie Ordner, da kommt eine Textur 500 mal oder mehr vor (also nen anderen Testordner). Aber hast du eine Idee, wie man das Problem umgehen kann?

Und dann noch eine Frage bezüglich deiner aussge, dass man Zeile 107-109 zu einer zusammenfügen kann:

$godFatherFolder += "_UltraLowRes"
$grandParentFolder = $godFatherFolder
$parentFolder = $godFatherFolder

Also wie genau stellst du dir das vor? Brauchts die zwei unteren Zeilen einfach nicht, weil ja beim erstellen des Destination-Folders die Ordner alle "von selbst" erstellt werden?

#44
2023-12-11, 16:09:04
Die drei Zeilen kannst du durch
$parentFolder = $godFatherFolder + "_UltraLowRes"
ersetzen.

GodFatherFolder und GrandParentFolder sind nicht nützlich. Die Erklärung habe ich bereits im vorherigen Post ergänzt.

Momentan hast du 4 Variablen für den Pfad, weil du am Ende jede Ebene des Pfads einzeln anlegst. Wenn das automatisch beim Anlegen des DestinationFolders passiert, musst du die Pfade der Zwischenebenen nicht explizit kennen und sie dir entsprechend auch nicht merken.

Die Duplikate muss ich natürlich behalten. Ich muss ja alle upscalen, nicht nur eine (das ist halt bei Games so, die haben die gleichen Texturen mehrfach gespeichert). Es gibt irgendwie Ordner, da kommt eine Textur 500 mal oder mehr vor (also nen anderen Testordner). Aber hast du eine Idee, wie man das Problem umgehen kann?
Ja. Das Skript könnte sich merken, welche Files es schon gibt. Aber auch da würde eine andere (flachere) Ordnerstruktur helfen. Denn dieses Pfad-Wirrwar würde ich nicht nochmal in memory verwalten wollen.

Platos
2023-12-11, 16:16:56
Und dieser Ordnerpfad ist auch die Grundlage für meine Struktur-Frage. Vier ineinander verschachtelte Ordner mit dem Namen "DXT5_NM" liefern dir doch auch keinen Mehrwert.

Da natürlich nicht. Aber in einem anderen schon. In den "NM" Ordner kommen auch andere Dateien mit "DM" im Dateinamen. Die finden sich nicht in allen Ordnern.

Viel wichtiger sind aber z.B die Ordner, in "DXT1" oder "DXT5".

Ich mach mal ein hypoothetisches Beispiel (das so manchmal auftritt):

GodFatherFolder: DXT1
GrandParentFolder: DXT1_UltraHighRes
ParentFolder: DXT1_AM_UltraHighRes
DestinationFolder: DXT1_AM_UltraHighRes_199

Das hat alles seinen Sinn. Auch, der GrandParentFolder hat hier seinen bewussten Sinn. Der ist dafür da, wenn man keine Unterscheidung zwischen AM, BM, GM usw. haben möchte. Dann kann man einfach den GrandParentFolder batch-upscalen.

Ich habe mir das wirklich genau überlegt. Jeder Ordner hat seinen Sinn. Die Ordnerhierarchie will ich nicht ändern.

Manche Dinge, wie das, was du jetzt gesagt hast (die NM-Verschachtelung) sind halt eben die Fälle, in denen es keine anderen Ordner gibt. Das sieht dann so aus, als wäre es Unnötig.

Hier ein Beispiel:

GodFatherFolder: DXT1
GrandParentFolder: DXT1_UltraHighRes
ParentFolder: DXT1_AM_UltraHighRes
DestinationFolder: DXT1_AM_UltraHighRes

Der letzte Ordner erscheint unnötig. Aber nur, weil es keine Duplikate gibt. Das selbe Prinzip ist hald in deinem Fall. Nur, weil es in diesem Testordner keine "DM" Dateien gibt, sieht das so unnötig aus. Und nur, weil es keine Duplikate gibt, sieht es noch unnötiger aus.

Man könnte natürlich jetzt eine Funktion einbauen, die in NUR einem solchen Fall keine unnötige Verschatelung macht. Aber das war mir zu kompliziert.

Die drei Zeilen kannst du durch
$parentFolder = $godFatherFolder + "_UltraLowRes"
ersetzen.

GodFatherFolder und GrandParentFolder sind nicht nützlich. Die Erklärung habe ich bereits im vorherigen Post ergänzt.

Momentan hast du 4 Variablen für den Pfad, weil du am Ennde jede Ebene des Pfads einzeln anlegst. Wenn das automatisch beim Anlegen des DestinationFolders passiert, musst du die Pfade der Zwischenebenen nicht explizit kennen und sie dir entsprechend auch nicht merken.


Ja. Das Skript könnte sich merken, welche Files es schon gibt. Aber auch da würde eine andere (flachere) Ordnerstruktur helfen. Denn dieses Pfad-Wirrwar würde ich nicht nochmal in memory verwalten wollen.

Ok, in diesem Fall bei UltraLowRes und color ist das wirklich unnötig. Ich wusste nur nicht, dass ich einfach diese Ordner auslassen kann. Weil der ParentFolder wird für die Erstellung des DestinationFolder benötigt und die Erstellung des ParentFolder benötigt (normalerweise) den GrandParentFolder.

Aber jetzt wo du es sagst... Das war ein Denkfehler. In diesem Spezialfall brauche ich den GrandParentFolder ja gar nicht, weil ich ja die grosse else-Klammer überspringe.

Das aktualisierte Skript kommt noch und die Test-Ordner (nochmals neu verlinken).

komme gerade nur nicht hinterher mit allem :D

#44
2023-12-11, 16:26:03
Das hat alles seinen Sinn. Auch, der GrandParentFolder hat hier seinen bewussten Sinn. Der ist dafür da, wenn man keine Unterscheidung zwischen AM, BM, GM usw. haben möchte. Dann kann man einfach den GrandParentFolder batch-upscalen.
Ok, verstehe. Aaaber da könnte man auch am Anfang des Skripts den Aufrufer fragen, was er nun haben will. Dann müsste das Skript nicht alle Fälle abdecken.
Oder eben nochmal überdenken, ob man den selben Komfort mit einer simpleren Struktur erreichen kann :biggrin:
(Kill your darlings! Soll heißen: Lass deinen Stolz auf die mühsam erarbeitete Struktur dir beim finden einer besseren Lösung nicht im Weg stehen.)

Der letzte Ordner erscheint unnötig. Aber nur, weil es keine Duplikate gibt. Das selbe Prinzip ist hald in deinem Fall. Nur, weil es in diesem Testordner keine "DM" Dateien gibt, sieht das so unnötig aus. Und nur, weil es keine Duplikate gibt, sieht es noch unnötiger aus.
Duplikate könnte man auch durch eine Umbenennung der Datei auflösen. Denn ein Mapping, welche Datei nun wohin gehört hast du ja eh schon...

P.S:

Verschiebe mal den Block

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

In das if ($debug2) hinein. Wenn es keine Ausgabe gibt, müssen die Daten dafür auch nicht gesammelt werden. Und zwei direkt aufeinander folgende if ($debug2) können auch zu einem kombiniert werden.

Platos
2023-12-11, 16:36:47
Ok, verstehe. Aaaber da könnte man auch am Anfang des Skripts den Aufrufer fragen, was er nun haben will. Dann müsste das Skript nicht alle Fälle abdecken. Oder eben nochmal überdenken, ob man den selben Komfort mit einer simpleren Struktur erreichen kann :biggrin:

Das wäre eine Idee, aber ich möchte, dass man das auch im Nachherein noch entscheiden kann.


Duplikate könnte man auch durch eine Umbenennung der Datei lösen. Denn ein Mapping, welche Datei nun wohin gehört hast du ja eh schon...

P.S:

Verschiebe mal den Block

# Nachdem alle Kopiervorgänge abgeschlossen sind
$specialFolders = Get-ChildItem -Path $outputFolder -Directory -Recurse | Where-Object { $_.Name -match "_\d+$" }
$totalCopiedDuplicates = 0

foreach ($folder in $specialFolders) {
$totalCopiedDuplicates += (Get-ChildItem -Path $folder.FullName -File | Measure-Object).Count
}

In das if ($debug2) hinein. Wenn es keine Ausgabe gibt, müssen die Daten dafür auch nicht gesammelt werden.

Also das mit den Duplikaten: Das ist aber eine ganz gefährliche Sache. Die Dateien müssen genau gleich benannt sein, sonst funktioniert das Spiel nicht mehr.
Das würde eine Umbenenung erfodern und dann im Skript 2 eine "Zurück-Umbenennung", bevor alles kopiert wird. Das finde ich nicht so gut. Das skript wird ja auch von anderen Benutzt und die finden das auch nicht so lustig, wenn die Dateien nicht so benannt sind, wie das Game es vorsieht. Bin da nicht so ein Freund von Dateien umbenennen irgendwie. Gibt es keine andere Möglichkeit, wo man die Dateien selbst unberührt lassen kann?

Das mit dem Block mache ich mal.

ich bin jetzt dann bald fertig mit dem rest. Also muss jetzt nur noch das mit dem "break" in diesen 2 Fällen umsetzen.

Edit: Ich kann von mir aus auch alles, was mit $debug2 zu tun hat, rausnehmen. Aber empfehlen würde ich es vermutlich nicht. Wenn ich dann später beim parallelisieren auf fehler treffe und alle nochmals neu einfügen muss... :D

Edit2: Also das mit dem $totalCopiedDuplicates und $specialFolder habe ich gelöscht. Brauche ich nicht mehr.

Platos
2023-12-11, 17:03:58
Also, ich habe nun das unnötige Zeug aus dem Skript entfernt. Danke übrigens mal für die ganze Hilfe hier. Vielen Dank!

Hier ist das Skript als .txt datei:

https://uploadnow.io/f/Q8Swtgy

Hier ist das Skript als code direkt:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if (& $resolutionCheckItem.Condition) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
break
}
}
}


# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$fileNameCheck = $filenameCheckItem.FileName
if ($fileNameCheck -eq "_DM" -and $parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
} else {
$parentFolder += $fileNameCheck
}
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), Item: $($filenameCheckItem.Description), fileNameCheck: ${fileNameCheck}" }
break
}
}
}

$parentFolder += $resolution
# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name)) {
if ($destinationFolder -match "_\d+$") {
$destinationFolder = $destinationFolder -replace "_\d+$", "_$i"
} else {
$destinationFolder = $destinationFolder + "_$i"
}
$i++
}

if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder
if ($debug2) { $output2 += "`nCopying file: $($ddsFile.FullName) to ${destinationFolder}" } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $($mapping.Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}




if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

Hier ist nochmals der Testordner "eq":

https://uploadnow.io/f/sLtd1BQ

Hier ist noch ein zusätzlicher Testordner "Enemy". Dieser eignet sich zwar nicht, um das verlangsamende Verhalten zu untersuchen, aber er eignet sich wesentlich besser, um die grundsätzliche Funktionalität zu untersuchen (weil es nur 20s benötigt, um das skript damit auszuführen. Das obwohl er viel grösser ist):

https://uploadnow.io/f/1vXlrZJ

Also so oder so. Ob ich nun das mit dem Parallelisieren umsetzen kann oder nicht. Der Code sieht jetzt schonmal besser aus. Das hat schonmal sehr geholfen.

myMind
2023-12-11, 17:35:59
Ist es denn jetzt schneller geworden?

Ich hab es mit einer älteren Version ausprobiert und da sehe ich folgendes:
86300

Der Teil, der das Zielverzeichnis erzeugt hat(te) ein Problem. Gelegentlich deutliche Ausreißer.

Freestaler
2023-12-11, 17:58:55
Copy Item ersetzen durch Robocopy allenfalls auch die Regexsucher der Dateinamen? CopyItem ist langsam von Haus aus wenns den Pfad mit beachten muss ;-) Gibt aber neuaufbau des Scripts.

Ausserdem Get-Content allenfalls "-ReadCount 5000" mitgeben.

#44
2023-12-11, 18:31:44
Copy Item ersetzen durch Robocopy allenfalls auch die Regexsucher der Dateinamen? CopyItem ist langsam von Haus aus wenns den Pfad mit beachten muss ;-) Gibt aber neuaufbau des Scripts.

Ausserdem Get-Content allenfalls "-ReadCount 5000" mitgeben.
Anmerkungen für den TS:

Bei den Vorschlägen (auch bei meinen!) muss man immer bedenken, dass da aktuell nur eine Vermutung im Raum steht.

Man hat zwar irgendwie durch Logging und den PSProfiler einige erste Hinweise bekommen - aber wie immer gilt: Wer misst, misst Mist.
Die Messungen sollten durch eine Gegenprobe verifiziert werden.

Also: Den Code komplett entfernen, von dem man glaubt, dass er bremst. Wenn's dann schneller ist, darf man sich zumindest etwas sicherer sein, da was reißen zu können.

Das ist aus meiner Sicht der nächste Schritt.
Schmeiß die Duplikatvermeidung weg und schau wie schnell es wird. Dann weißt du welches Performancepotential ein besseres Duplikathandling hat.
Selbiges für CopyItem.

Und dann kannst du entscheiden, was sich davon mehr lohnt.

P.S.: -ReadCount wird vmtl. nix bringen. Die Textdateien sind klein und das DDSFormat steht auf der letzten Zeile...
Da wäre interessanter, ob deren Aufbau garantiert ist.

Platos
2023-12-11, 19:11:32
Wie ihr mir hier helft ist echt grossartig, danke !

Ist es denn jetzt schneller geworden?

Ich hab es mit einer älteren Version ausprobiert und da sehe ich folgendes:
86300

Der Teil, der das Zielverzeichnis erzeugt hat(te) ein Problem. Gelegentlich deutliche Ausreißer.

Wie hast du das ausgelesen? Ich sehe halt sowas nicht.

Also die neueste Version ist beim enemy-folder 16s, während dem es früher 18-20 war. So niedrig war die Zeit noch nie, aber eine Schwankung von 1-2s hatte ich hier immer, aber eben eher gegen oben hinaus. Ich habs mit der Stoppuhr gemessen.

PSProfiler's Zeiten waren hingegen mehr oder weniger gleich.

Beim eq-Ordner habe ich nun 2 min 16s anstatt vorher 2min 35s. Ich denke, da kann man eine Schwankung ausschliessen. Die Messung mit 2min 25s habe ich mit Version 15.4.4 gemacht und diese hier mit Version 16.1 (das ist die version, die ich im Kommentar 61 gepostet habe).

Die Vesion 15.4.4 habe ich noch nicht mit PSProfiler untersucht mit dem eq ordner. mache ich gleich noch.

Aber mich würde interessieren, wie du das ausgelesen hast diese Wartezeiten?

Edit: Also hier habe ich eine Verbesserung gefunden (Getestet mit eq-ordner):

Aus der 15.4.4:

1401 245 00:00.1749153 $testPathResult = Test-Path $destinationFolder
0 246 00:00.0000000 if ($debug2) { $output2 += "`nTest-Path Ergebnis für
${destinationFolder}: ${testPathResult}" }
0 247 00:00.0000000
1401 248 00:03.0100272 $newItemResult = New-Item -ItemType Directory -Path $destinationFolder
0 249 00:00.0000000 if ($debug2) { $output2 += "`nNew-Item Ergebnis für
${destinationFolder}: ${newItemResult}" }

Aus 16.1:

1401 202 00:00.5070895 $newItemResult = New-Item -ItemType Directory -Path $destinationFolder

Edit2: Also der Teil, den ich im Spoiler geschrieben habe, ist nur beim eq-ordner so viel besser. Beim enemy-ordner ist dieser vorteil nicht gegeben. Das könnte daran liegen, dass der enemy-ordner nicht so viele duplikate hat und dieser Codeteil geht ja um die duplikate.

Copy Item ersetzen durch Robocopy allenfalls auch die Regexsucher der Dateinamen? CopyItem ist langsam von Haus aus wenns den Pfad mit beachten muss ;-) Gibt aber neuaufbau des Scripts.

Ausserdem Get-Content allenfalls "-ReadCount 5000" mitgeben.

Was macht denn Robocopy? Brauche ich denn die pfade nicht? Ich muss doch wissen, wohin (pfad) kopieren?

Und das mit dem -ReadCount was macht das? Die .txt. Datei hat 11 Zeile mit pro Zeile 5-20 Zeichen. Also nichts. Oder worauf bezieht sich das ?

Anmerkungen für den TS:

Bei den Vorschlägen (auch bei meinen!) muss man immer bedenken, dass da aktuell nur eine Vermutung im Raum steht.

Man hat zwar irgendwie durch Logging und den PSProfiler einige erste Hinweise bekommen - aber wie immer gilt: Wer misst, misst Mist.
Die Messungen sollten durch eine Gegenprobe verifiziert werden.

Also: Den Code komplett entfernen, von dem man glaubt, dass er bremst. Wenn's dann schneller ist, darf man sich zumindest etwas sicherer sein, da was reißen zu können.

Das ist aus meiner Sicht der nächste Schritt.
Schmeiß die Duplikatvermeidung weg und schau wie schnell es wird. Dann weißt du welches Performancepotential ein besseres Duplikathandling hat.
Selbiges für CopyItem.

Und dann kannst du entscheiden, was sich davon mehr lohnt.

P.S.: -ReadCount wird vmtl. nix bringen. Die Textdateien sind klein und das DDSFormat steht auf der letzten Zeile...
Da wäre interessanter, ob deren Aufbau garantiert ist.

Ok, ja also das mit der Duplikatvermeidung kann ich machen zu Testzwecken. Die Dateien werden dann eifnach alle überschrieben. Ich weiss nicht, ob das ddas Bild verfälscht, also ob das zusätzlich Zeit benötigt (das Überschreiben)

Ok, das mit dem Robocopy versuche ich mal. Ich frag mal ChatGPT, wies geht, füge es dann ein und berichte ebenfalls.

Bezüglich garantiertem Aufbau: Du meinst, ob immer in der selben Zeile das gleiche steht oder meinst du, ob es vlt. manchmal gar nicht erst drinn steht?

Also das sollt schon funktionieren. Das Tool, welches diese Dateien nutzt, wurde schon von ziemlich vielen Leuten benutzt und die haben alle erfolgreich Mods damit raus gebracht. Also das sollte schon funktionieren. (Das Tool heisst ARCtool).

#44
2023-12-11, 19:44:00
Bezüglich garantiertem Aufbau: Du meinst, ob immer in der selben Zeile das gleiche steht oder meinst du, ob es vlt. manchmal gar nicht erst drinn steht?

Also das sollt schon funktionieren. Das Tool, welches diese Dateien nutzt, wurde schon von ziemlich vielen Leuten benutzt und die haben alle erfolgreich Mods damit raus gebracht. Also das sollte schon funktionieren. (Das Tool heisst ARCtool).
Ja genau. Eben ob alle Elemente immer da sind oder ob es da optionale Felder gibt oder ob sich der Aufbau gar für versch. Formate komplett unterscheiden kann.
Ich habe da nicht wirklich was an Dokumentation zu gefunden. (Habe aber auch nicht ernsthaft gesucht)

Dass Andere das ja auch nutzen sagt da ja erstmal nicht viel - die könnten ja auch einfach nur alle korrekt mit den Sonderfällen umgehen.

Platos
2023-12-11, 20:27:42
Ja genau. Eben ob alle Elemente immer da sind oder ob es da optionale Felder gibt oder ob sich der Aufbau gar für versch. Formate komplett unterscheiden kann.
Ich habe da nicht wirklich was an Dokumentation zu gefunden. (Habe aber auch nicht ernsthaft gesucht)

Dass Andere das ja auch nutzen sagt da ja erstmal nicht viel - die könnten ja auch einfach nur alle korrekt mit den Sonderfällen umgehen.

Ah nein, ich kenne Leute, die schon seit Jahren dieses Tool nutzen. Das läuft glaube ich relativ gut. Also da machen wir uns mal keine gedanken.

Abgesehen davon: Man kann das überprüfen, indem man mal nach dds filtert im explorer (habe ich auch schon). Dann stimmt die Anzahl immer mit der überein, die ich beim Skript ausgegeben krieg. Also scheint zu stimmen.

ich teste jetzt mal das mit dem duplikatordner usw.

bezüglich duplikate: Siehe auch die letzte Antwort beim Spoiler. Da habe ich heraufunden, dass der eq-ordner vom neuen code profitiert, der enemy ordner aber nicht (der scheint viel weniger duplikate zu haben. Hypthose: Es liegt am Unterschied der Duplikatanzahl).

#44
2023-12-11, 20:32:03
Ah nein, ich kenne Leute, die schon seit Jahren dieses Tool nutzen. Das läuft glaube ich relativ gut. Also da machen wir uns mal keine gedanken.

Abgesehen davon: Man kann das überprüfen, indem man mal nach dds filtert im explorer (habe ich auch schon). Dann stimmt die Anzahl immer mit der überein, die ich beim Skript ausgegeben krieg. Also scheint zu stimmen.
Mir geht es um den Inhalt der .txt Dateien. Wenn der Aufbau garantiert gleich ist, könnte man die Infos effizienter auslesen, als jede Datei 3x mit Regex zu durchkämmen.

Dein Skript behandelt ja explizit die Fälle, dass Width oder Height NICHT vorhanden sind.

Platos
2023-12-11, 20:37:55
Mir geht es um den Inhalt der .txt Dateien. Wenn der Aufbau garantiert gleich ist, könnte man die Infos effizienter auslesen, als die Datei 3x mit Regex zu durchkämmen.

Dein Skript behandelt ja explizit die Fälle, dass Width oder Height NICHT vorhanden sind.

Echt? Wo ?

Und bezüglich der Reihenfolge. Das weiss ich nicht, aber ich gehe davon aus, dass ein Programm nicht Roullete spielt, sondern dass das genau definiert ist. Aber wissen tue ich nicht.

myMind
2023-12-11, 20:43:40
Wie hast du das ausgelesen? Ich sehe halt sowas nicht.

Also die neueste Version ist beim enemy-folder 16s, während dem es früher 18-20 war. So niedrig war die Zeit noch nie, aber eine Schwankung von 1-2s hatte ich hier immer, aber eben eher gegen oben hinaus. Ich habs mit der Stoppuhr gemessen.

PSProfiler's Zeiten waren hingegen mehr oder weniger gleich.

Beim eq-Ordner habe ich nun 2 min 16s anstatt vorher 2min 35s. Ich denke, da kann man eine Schwankung ausschliessen. Die Messung mit 2min 25s habe ich mit Version 15.4.4 gemacht und diese hier mit Version 16.1 (das ist die version, die ich im Kommentar 61 gepostet habe).

Die Vesion 15.4.4 habe ich noch nicht mit PSProfiler untersucht mit dem eq ordner. mache ich gleich noch.

Aber mich würde interessieren, wie du das ausgelesen hast diese Wartezeiten?

So wie in #44 und #46 beschrieben. Logger eingebaut mit Millisekunden und die Ausgaben etwas getrimmt.

Die Tests in der while-Schleife würde ich mir mal anschauen.

Bei diesem eq-Datensatz den du gepostet hast, sind bei mir ~6000 Dateien im Schnitt mit 40 ms pro Kopiervorgang. Dabei gibt es schnelle Kopiervorgänge mit 6 bis 13 ms und langsame von anfangs 50 ms bis > 250 ms. Ansteigend je länger es läuft.


DEBUG: 11122023 08:20:18.845 Found .dds file: fs003_BM.dds
DEBUG: 11122023 08:20:18.846 Creating destination folder...
DEBUG: 11122023 08:20:18.847 Creating destination folder...before while
DEBUG: 11122023 08:20:19.139 Creating destination folder...before create
DEBUG: 11122023 08:20:19.142 Copying file: fs003_BM.dds
DEBUG: 11122023 08:20:19.144 Done copy file: fs003_BM.dds
DEBUG: 11122023 08:20:19.146 Copy time taken: 00 seconds 299 millis
DEBUG: 11122023 08:20:19.147 Analysis time taken: 00 seconds 000 millis
DEBUG: 11122023 08:20:19.148 Directory time taken: 00 seconds 295 millis
DEBUG: 11122023 08:20:19.149 Copy time taken: 00 seconds 003 millis
11122023 08:20:19.150 $mapping has 6151 items after adding a new item


Ich vermute die Testanzahl steigt hier an und verlangsamt das Ganze:
LogDebug "Creating destination folder...before while"
# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = New-Object System.Text.StringBuilder (Join-Path -Path $parentFolder.ToString() -ChildPath (Split-Path $parentFolder.ToString() -Leaf))

$i = 2
while (Test-Path (Join-Path -Path $destinationFolder.ToString() -ChildPath $ddsFile.Name)) {
if ($destinationFolder.ToString() -match "_\d+$") {
$destinationFolder = New-Object System.Text.StringBuilder ($destinationFolder.ToString() -replace "_\d+$", "_$i")
}
else {
[void]$destinationFolder.Append("_$i")
}
$i++
}

LogDebug "Creating destination folder...before create"

Vielleicht einfach randomizen mit einem Test, statt aufsteigendem Zähler.

#44
2023-12-11, 20:51:30
Echt? Wo ?

Und bezüglich der Reihenfolge. Das weiss ich nicht, aber ich gehe davon aus, dass ein Programm nicht Roullete spielt, sondern dass das genau definiert ist. Aber wissen tue ich nicht.
Zeile 31 und 32.

Mir geht es auch weniger darum, ob die Reihenfolge fest ist, als darum, ob es optionale Zeilen gibt. Das hat am Ende ähnliche Auswirkungen.

Aber natürlich kann man das auch einfach mal ausprobieren.

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
$match = $content[10]
$width = $content[2]
$height = $content[3]
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = $width.Matches.Value -replace "Width="
'Height' = $height.Matches.Value -replace "Height="
}
}
}
Achtung: Ich bin mir gerade nicht sicher, ob ich die Zeilen-Indizes von width und height nicht vertauscht habe.

Bringt bei mir 16% in der 1. Schleife.

Und wenn wir gerade dabei sind:
$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
Kleine Ergänzung für Zeile 41 um Textdateien zu überspringen, die nicht interessieren.

Platos
2023-12-11, 20:52:38
Ok, danke. Ich habe jetzt die Duplikat-prüfung und die Nummerierung der Ordner (die ja für die Duplikatprüfung gemacht wird), entfernt und der eq-Ordner war jetzt in 22 Sekunden fertig anstatt 2min irgendwas.

also Ja, die Duplikatprüfung frisst ordentlich Ressourcen.

Gibt es da irgend eine Möglichkeit, das perfomanter zu machen, ohne alles umzubenennen ?

#44
2023-12-11, 20:54:46
Ich habe meinen vorherigen Post nochmal erweitert.

Gibt es da irgend eine Möglichkeit, das perfomanter zu machen, ohne alles umzubenennen ?
Ja. Zum Beispiel könntest du alle Dateinamen (€: natürlich die vollen Pfade...) vor der Deduplizierung (!) in ein Dictionary legen mit der Anzahl der Duplikate als Dictionary-Value. Dann kannst du für jede Datei einfach im Dictionary nachsehen, ob und welche Zahl du am ParentFolder anhängen musst. Und natürlich die Anzahl im Dictionary aktualisieren.

Platos
2023-12-11, 21:18:10
Ich habe meinen vorherigen Post nochmal erweitert.


Ja. Zum Beispiel könntest du alle Dateinamen (€: natürlich die vollen Pfade...) vor der Deduplizierung (!) in ein Dictionary legen mit der Anzahl der Duplikate als Dictionary-Value. Dann kannst du für jede Datei einfach im Dictionary nachsehen, ob und welche Zahl du am ParentFolder anhängen musst. Und natürlich die Anzahl im Dictionary aktualisieren.

Die Idee gefällt mir grundsätzlich ja, aber woher weiss ich, welche Dateien duplikate sind? Also dann muss ich ja irgendwie wieder prüfen, welche das sind. Wie mache ich das?

Zeile 31 und 32.

Mir geht es auch weniger darum, ob die Reihenfolge fest ist, als darum, ob es optionale Zeilen gibt. Das hat am Ende ähnliche Auswirkungen.

Aber natürlich kann man das auch einfach mal ausprobieren.



Ich könnte auch einfach ein kleines Skript schreiben, dass alle .txt dateien im ganzen rom/folder durchsucht und schaut, ob in Zeile x,y und z das "richtige" steht.

Das mache ich doch schnell. ALso ich zähle die .txt dateien, in denen das so stimmt und vergleich sie mit der Anzahl von .txt dateien, in denen "DDSFormat=" irgendwo vorkommt. Wenn die Zahl gleich ist, dann beantwortet das die Frage (sonst natürlich auch).

Edit: Fängt man bei der Zeile mit 0 oder 1 an zu zählen?

weil bei mir sieht das so aus:
TEX
Texversion=RE6
Textype=20
Width=8
Height=8
Mips=3
Uint1=536871065
Byte1=1
Byte2=1
Byte3=0
DDSFormat=DXT1


Also DDSFormat ist Zeile 11, Width ist Zeile 4, Height ist Zeile 5.

Hast du das falsch, fängt man bei 0 an zu zählen oder ist das schon der Beweis, dass deine .txt Datei nicht die gleichen Zeilen hat?

#44
2023-12-11, 21:23:55
Die Idee gefällt mir grundsätzlich ja, aber woher weiss ich, welche Dateien duplikate sind? Also dann muss ich ja irgendwie wieder prüfen, welche das sind. Wie mache ich das?
Wenn du jede Datei mit vollem Pfad vor Deduplizierung in das Dictionary einträgst, dann hast du genau dann ein Duplikat, wenn es bereits einen Eintrag im Dictionary für den gleichen Pfad gibt. Darum auch die Anzahl als Dictionary-Value - wenn du da mitzählst weißt du exakt wie oft es diesen Pfad bereits als Duplikat gab.

Edit: Fängt man bei der Zeile mit 0 oder 1 an zu zählen?

Also DDSFormat ist Zeile 11, Width ist Zeile 4, Height ist Zeile 5.

Hast du das falsch, fängt man bei 0 an zu zählen oder ist das schon der Beweis, dass deine .txt Datei nicht die gleichen Zeilen hat?
Das sind 0-basierte Indizes. Entsprechend 3 für Width und 4 für Height. Habe ich falsch im Gedächtnis gehabt.

Platos
2023-12-11, 21:30:22
Wenn du jede Datei mit vollem Pfad vor Deduplizierung in das Dictionary einträgst, dann hast du genau dann ein Duplikat, wenn es bereits einen Eintrag im Dictionary für den gleichen Pfad gibt. Darum auch die Anzahl als Dictionary-Value - wenn du da mitzählst weißt du exakt wie oft es diesen Pfad bereits als Duplikat gab.

Ah so, ja ok, d.h ich checke keine Pfade, ich trage die Pfade grad direkt ein.

D.h direkt vor der erstellung des $destinationFolders oder anders gesagt direkt nach der letzten aktualisierung des $parentfolders trage lasse ich den Wert von $parentFolder in dieses Dictionary schreiben und bei der erstellung des DestinationFolders wird dann das dictionary abgefragt. D.h ich prüfe nicht tatsächlich Pfade, sondern rufe nur Daten aus variablen und Listen ab, die ja dann im Endeffekt im RAM sind. Zumindest verstehe ich das so.

Edit: Zur Zeilenfrage: Ok, ich schreib kurz ein Skript und lass dann den ganzen /rom folder durchchecken.

Platos
2023-12-11, 21:45:32
Aktualisierung:

Ok, ich habe den ganzen Rom-Folder durchchecken lassen und es gibt gleich viele .txt dateien, die irgendwo "DDSFormat=" stehen haben, wie .txt die in Zeile 4,5 und 11 "Width=", "Height=" und "DDSFormat" (gleichzeitig) stehen haben. Also alle 3 bedinungen müssen gleichzeitig stimmen.

Die zahl stimmt auch mit der Zahl überein, die mir letztens mal das skript ausgeben hat, als ich den ganzen rom Folder kopiert habe mit dem Skript (also die Anzahl kopierter .dds Dateien).

Also ja, man kann nur gewisse Zeilen ansprechen. Gute Idee!

Dann versuche ich jetzt das mit dem DestinationFolder und dem "dictionary" umzusetzen.

gibst du mir noch ein Tipp, was ein dictionary ist? Ist das ein array, eine generische liste? eine hashtable oder was ist das ? Was eigenes ?

Monger
2023-12-11, 21:57:53
gibst du mir noch ein Tipp, was ein dictionary ist? Ist das ein array, eine generische liste? eine hashtable oder was ist das ? Was eigenes ?
Ein Dictionary ist im Kern ne Hashtable. Ergo, auf den schnellen Zugriff von Primärschlüsseln optimiert.

#44
2023-12-11, 22:06:38
Ah so, ja ok, d.h ich checke keine Pfade, ich trage die Pfade grad direkt ein.
Exakt. Wie immer beim Optimieren: Am schnellsten ist es, gar nicht erst irgendwas tun zu müssen.


gibst du mir noch ein Tipp, was ein dictionary ist? Ist das ein array, eine generische liste? eine hashtable oder was ist das ? Was eigenes ?
Das ist quasi eine etwas strenger typisierte Version der Hastable. Keys und Values müssen jeweils vom gleichen Typ sein.

Platos
2023-12-12, 00:49:11
Ja. Zum Beispiel könntest du alle Dateinamen (€: natürlich die vollen Pfade...) vor der Deduplizierung (!) in ein Dictionary legen mit der Anzahl der Duplikate als Dictionary-Value. Dann kannst du für jede Datei einfach im Dictionary nachsehen, ob und welche Zahl du am ParentFolder anhängen musst. Und natürlich die Anzahl im Dictionary aktualisieren.

:love4: danke #44, du bist der King :D

27sekunden hatte ich jetzt für den eq ordner (vorher über 2 minuten). Das hats ja mal voll gebracht :eek::D

Hier ist nun die neue Version 16.2 des Skripts (angehängt als .txt)

Und hier der Code direkt von 16.2:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-ARCtool-entpackte\eq-ARCtool\ARCtool\eq"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = if ($content[10] -match "DDSFormat=(.*)") { $Matches[1] } else { $null }
$width = if ($content[3] -match "Width=(.*)") { $Matches[1] } else { $null }
$height = if ($content[4] -match "Height=(.*)") { $Matches[1] } else { $null }
if ($match) {
@{
'DDSFormat' = $match
'Width' = $width
'Height' = $height
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary
$duplikate = @{}
# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if (& $resolutionCheckItem.Condition) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
break
}
}
}


# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$fileNameCheck = $filenameCheckItem.FileName
if ($fileNameCheck -eq "_DM" -and $parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
} else {
$parentFolder += $fileNameCheck
}
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), Item: $($filenameCheckItem.Description), fileNameCheck: ${fileNameCheck}" }
break
}
}
}

$parentFolder += $resolution

# Definieren Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$path = $destinationFolder + $ddsFile.Name

# Wenn der Pfad bereits im Dictionary existiert, erhöhen Sie den Zähler
if ($duplikate.ContainsKey($path)) {
$duplikate[$path]++
} else {
# Wenn der Pfad noch nicht im Dictionary existiert, fügen Sie ihn hinzu und setzen Sie den Zähler auf 1
$duplikate[$path] = 1
}

$duplikatZahl = $duplikate[$path]

# Fügen Sie die DuplikatZahl am DestinationFolder an, wenn der Zähler größer als 1 ist
if ($duplikatZahl -gt 1) {
$destinationFolder += "_$duplikatZahl"
}

if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $($mapping.Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}

if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

Ich habe jetzt beim Profilen aber eine komische Sache entdeckt. Ich habe am Anfang des skripts das mit den Zeilen umgesetzt. Also dass ich nur noch in zeile 4,5 und 11 suche. Jetzt habe ich habe festgestellt, dass ich die doppelte Anzahl an matches habe (sagt PSProfiler). Die Anzahl kopierter Dateien ist am Ende aber gleich. Also funktionieren tut alles.

Hier der auszug mit der relevanten codestell:

0 17 00:00.0000000 function Search-DDSFormat {
0 18 00:00.0000000 param (
0 19 00:00.0000000 [string]$filePath
0 20 00:00.0000000 )
7341 21 00:03.1821279 $content = Get-Content -Path $filePath
14682 22 00:00.1644072 $match = if ($content[10] -match "DDSFormat=(.*)") { $Matches[1] } else { $null }
14682 23 00:00.0870273 $width = if ($content[3] -match "Width=(.*)") { $Matches[1] } else { $null }
14682 24 00:00.0788778 $height = if ($content[4] -match "Height=(.*)") { $Matches[1] } else { $null }
0 25 00:00.0000000 if ($match) {
6189 26 00:00.0377528 @{
0 27 00:00.0000000 'DDSFormat' = $match
0 28 00:00.0000000 'Width' = $width

0 29 00:00.0000000 'Height' = $height

0 30 00:00.0000000 }
0 31 00:00.0000000 }
0 32 00:00.0000000 }

Die Zahl 14682 ist genau doppelt so gross wie die Anzahl .txt dateien (7341). Warum passiert das? Habe ich diesen Teil falsch gemacht ?

edit: Vergessen die Datei anzuhängen:

Freestaler
2023-12-12, 00:59:28
ChatGPT hat mich jetzt interessiert und mal Copy-Item ersetzt druch robocopy. Nicht ganzt perfekt das resultat von ChatGPT, habs daher noch kurz in eigenen Process gesteckt (Powershell soll nicht warten auf Robocopy). Grundlage war dein 16_1 Script.

Von 165 Sekunden runter auf 68s. 5800X3D.

Obacht, pfade für mich angepasst.


# Am Anfang des Skripts
$logFile = "C:\3dc\run\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\3dc\run\eq"
$outputFolder = "C:\3dc\run\upscaled"
$csvPath = "C:\3dc\run\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $false # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $false # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath -ReadCount 5000
$match = $content | Select-String -Pattern "DDSFormat=(.*)"
$width = $content | Select-String -Pattern "Width=(.*)"
$height = $content | Select-String -Pattern "Height=(.*)"
if ($match) {
@{
'DDSFormat' = $match.Matches.Value -replace "DDSFormat="
'Width' = if ($width) { $width.Matches.Value -replace "Width=", "" } else { $null }
'Height' = if ($height) { $height.Matches.Value -replace "Height=", "" } else { $null }
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Durchsuchen des Inhalts der .txt Dateien im rootFolder Step 1
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script Step 1" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien Step2
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script Step 2" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if (& $resolutionCheckItem.Condition) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
break
}
}
}


# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$fileNameCheck = $filenameCheckItem.FileName
if ($fileNameCheck -eq "_DM" -and $parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
} else {
$parentFolder += $fileNameCheck
}
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), Item: $($filenameCheckItem.Description), fileNameCheck: ${fileNameCheck}" }
break
}
}
}

$parentFolder += $resolution


# Erstellen Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

# Ensure destination folder exists
if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}

# Use robocopy for copying files
$robocopyArgs = "/E /NP /V" # You can add more options if needed
$robocopyCommand = "robocopy.exe `"$($ddsFile.Directory.FullName)`" `"$destinationFolder`" $($ddsFile.Name) $robocopyArgs"
Start-Process cmd.exe "/c $robocopycommand" -NoNewWindow

if ($debug2) { $output2 += "`nCopying file: $($ddsFile.FullName) to ${destinationFolder}" } # Debugging-Ausgabe

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $($mapping.Count) items after adding a new item" }

} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}




if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

Platos
2023-12-12, 01:02:37
ChatGPT hat mich jetzt interessiert und mal Copy-Item ersetzt druch robocopy. Nicht ganzt perfekt das resultat von ChatGPT, habs daher noch kurz in eigenen Process gesteckt (Powershell soll nicht warten auf Robocopy).

Von 165 Sekunden runter auf 68s. 5800X3D.

Obacht, pfade für mich angepasst.



nice, ich versuche das in mein Skript einzubauen. Ich weiss natürlich nicht, ob es nun nach meiner Änderung immer noch den selben Faktor bringen wird (köntne auch auf ~1 sinken), aber ich probiers aus. Über deinem Post habe ich gerade die neueste Version gepostet mit neuer Duplikatprüfung.

Edit: Hmm warte mal, du hast die Duplikat-Prüfung raus gelöscht oder? Oder ist das jetzt irgendwie bei Robocopy eingebaut ?

Freestaler
2023-12-12, 01:14:59
16_2 mit Robocopy will ne andere Syntax, sehe gerade nicht wieso. (no new Window verhält sich anders). Aber mit Hiddenstyle geht es auch.

Nur dann ist langsam. 16_1 mit Robocopy 68sekunden, 16_2 mit Robocopy 78sekunden.

Update: Betreffend Duplicats Prüfung nicht bewusst. bzw. Errorhandling Robocopy wenn bereits existiert mit gleichem Inhalt, dann egal.



# Am Anfang des Skripts
$logFile = "C:\3dc\run\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\3dc\run\eq"
$outputFolder = "C:\3dc\run\upscaled"
$csvPath = "C:\3dc\run\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $false # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $false # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param (
[string]$filePath
)
$content = Get-Content -Path $filePath
$match = if ($content[10] -match "DDSFormat=(.*)") { $Matches[1] } else { $null }
$width = if ($content[3] -match "Width=(.*)") { $Matches[1] } else { $null }
$height = if ($content[4] -match "Height=(.*)") { $Matches[1] } else { $null }
if ($match) {
@{
'DDSFormat' = $match
'Width' = $width
'Height' = $height
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalTxtFilesWithWidth = 0
$totalTxtFilesWithHeight = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary
$duplikate = @{}
# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.Width) {
$totalTxtFilesWithWidth++
}
if ($result.Height) {
$totalTxtFilesWithHeight++
}
if ($result.DDSFormat -and $result.Width -and $result.Height) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
if ($debug2) { $output2 += "`nFound .dds file: $($ddsFile.FullName)" } # Debugging-Ausgabe
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result
if ($debug2) { $output2 += "`nGodFatherFolder nach der Zuweisung: ${godFatherFolder}" }

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) {
$output2 += "`nBreite und Höhe sind kleiner oder gleich 16, GodFatherFolder: ${godFatherFolder}"
}
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_UltraLowRes"
if ($debug2) { $output2 += "`nDateiname enthält '*col*_ID.dds', GodFatherFolder: ${godFatherFolder}" }
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'NM', GodFatherFolder: ${godFatherFolder}" }
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
if ($debug2) { $output2 += "`nDateiname enthält 'DM', GodFatherFolder: ${godFatherFolder}" }
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if (& $resolutionCheckItem.Condition) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
if ($debug2) {
$output2 += "`nWidth: ${width}"
$output2 += "`nHeight: ${height}"
$output2 += "`nResolution: ${resolution}"
}
break
}
}
}


# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$fileNameCheck = $filenameCheckItem.FileName
if ($fileNameCheck -eq "_DM" -and $parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
if ($debug2) { $output2 += "`n'NM' in ParentFolder durch 'DM' ersetzt, neuer ParentFolder: ${parentFolder}" }
} else {
$parentFolder += $fileNameCheck
}
if ($debug2) { $output2 += "`nDateiname: $($ddsFile.Name), Item: $($filenameCheckItem.Description), fileNameCheck: ${fileNameCheck}" }
break
}
}
}

$parentFolder += $resolution

# Definieren Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$path = $destinationFolder + $ddsFile.Name

# Wenn der Pfad bereits im Dictionary existiert, erhöhen Sie den Zähler
if ($duplikate.ContainsKey($path)) {
$duplikate[$path]++
} else {
# Wenn der Pfad noch nicht im Dictionary existiert, fügen Sie ihn hinzu und setzen Sie den Zähler auf 1
$duplikate[$path] = 1
}

$duplikatZahl = $duplikate[$path]

# Fügen Sie die DuplikatZahl am DestinationFolder an, wenn der Zähler größer als 1 ist
if ($duplikatZahl -gt 1) {
$destinationFolder += "_$duplikatZahl"
}

if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}


# Use robocopy for copying files
$robocopyArgs = "/E /NP /V" # You can add more options if needed
$robocopyCommand = "robocopy.exe `"$($ddsFile.Directory.FullName)`" `"$destinationFolder`" $($ddsFile.Name) $robocopyArgs"
Start-Process cmd.exe "/c $robocopycommand" -WindowStyle Hidden


# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
if ($debug2) { $output2 += "`n`$mapping has $($mapping.Count) items after adding a new item" }
} else {
if ($debug2) { $output2 += "`nKeine .dds-Datei gefunden für $($file.FullName)" }
}
}

if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=', 'Width=' und 'Height=' gefunden."
}

# Ausgabe der Gesamtanzahl der gefundenen .txt-Dateien mit "Width=", der gefundenen .txt-Dateien mit "Height=" und der gefundenen .dds-Duplikate
if ($debug2) {
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithWidth .txt-Dateien mit 'Width=' gefunden."
$output2 += "`nEs wurden insgesamt $totalTxtFilesWithHeight .txt-Dateien mit 'Height=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript



Update: Übung halt. 16_2 original 22 sekunden.. vergiss robocopy ;-)

Platos
2023-12-12, 01:23:11
16_2 mit Robocopy will ne andere Syntax, sehe gerade nicht wieso. (no new Window verhält sich anders). Aber mit Hiddenstyle geht es auch.

Nur dann ist langsam. 16_1 mit Robocopy 68sekunden, 16_2 mit Robocopy 78sekunden.

Update: Betreffend Duplicats Prüfung nicht bewusst. bzw. Errorhandling Robocopy wenn bereits existiert mit gleichem Inhalt, dann egal.

Update: Übung halt. 16_2 original 22 sekunden.. vergiss robocopy ;-)

Schade, ist aber auch komisch.

Mit der Version 16_1 hilft es unheimlich und bei der Version 16_2 verlangsamt es sogar noch ?

wie kommt sowas zu stande ?

Edit: Aber da weiss ich gar nicht, ob sich parallelisieren jetzt überhaupt noch lohnt. Also war ja ein riesen Murks und habs nie hingekriegt. Ich meine, jetzt gehts ziemlich flott. Da frage ich mich, ob Parallelisieren überhaupt noch was bringt.

Freestaler
2023-12-12, 01:30:58
22 sekunden auf den Test daten, wieviel grösser sind den die echten Daten? Ich denke die Blumen gehören ua. #44 mit seiner Unterstützung kammst du weit.

Platos
2023-12-12, 01:52:56
22 sekunden auf den Test daten, wieviel grösser sind den die echten Daten?

"Nur" ca. 10x grösser. Ich habe den ganzen /rom ordner mal getestet und habe nur 6min 19s gebraucht (früher so ~27-28min).

Also was ich aber schonmal sagen kann: Die Verlangsamung ist Geschichte. Das Skript ist jetzt eig. durchgehend gleich schnell.

Die Speicherauslastung ist ca ~3-3.5GB, was voll ok ist für mich
Die SSD Last war im Durchschnitt bie ca ~20% beim lesen und beim schreiben weniger.
Die CPU hald wieder eher niedrig ausgelastetet. Habe aber nicht mit einem Thread getestet. Das mache ich jetzt auch noch mit einem testordner.

Eigentlich ist ja eine parallelisierung nicht mehr notwendig, aber es juckt mich jetzt schon in den Fingern, das noch weiter zu optimieren :D

Die anderen Ordner sind übrigens auch "echte" Daten. Es sind originale subfolder von /rom. ALso z.B /rom/eq oder /rom/enemy

Edit: Jetzt sehe ich aber ein Fehler in der Ordnerstruktur. Irgendwie gibt es gar keine _color Ordner, fällt mir auf. Die sind jetzt irgendwie in den _UltraLowRes ordnern. Muss ich untersuchen...

Edit2: Der Fehler ist schon bei Version 16_1. Muss ich also weiter suchen ^^

Edit3: Copy-paste fehler. Habe bei "$parentFolder = $godFatherFolder + "_UltraLowRes"" einfach copy paste gemacht für die _color version, ohne den Namen anzupassen ich depp.

Ich denke die Blumen gehören ua. #44 mit seiner Unterstützung kammst du weit.

Auf jeden Fall!

Daher mal ein richtig fettes Dankeschön an #44. Ich glaube, ich wäre noch ewigs am "optimieren" gewesen.

Das witzige ist ja, wenn man sich diese while-schleife mal genauer angesehen hätte, wäre es einem vlt. irgendwann mal aufgefallen. Was die gemacht hat, ist so hinrverbrannt. Beim 300-sten Duplikat hätte diese Schleife einfach mal den $destinationFolder gebildet, dann geschaut, ob er da ist, wenn ja, geschaut ob $destinationFolder_2 da ist, wenn ja, geschaut ob $destinationFolder_3 da ist usw. Bis 301 :D

Freestaler
2023-12-12, 02:06:33
Powershell 7 möglich= Dachte du sagtest mal nein. Da wurde es Foreach-Object "parallel" dann geben.
Sonst halt die zweite grosse Foreach in separaten Process raus. Dazu aber Input auftrennen und Dupletten check umbau. Schwierig.

Platos
2023-12-12, 02:11:47
Powershell 7 möglich= Dachte du sagtest mal nein. Da wurde es Foreach-Object "parallel" dann geben.
Sonst halt die zweite grosse Foreach in separaten Process raus. Dazu aber Input auftrennen und Dupletten check umbau. Schwierig.

Ja, also das Ziel war ja, ein script zu machen, dass man komplett ohne Installation von irgendwas nutzen kann.

Daher habe ich es mit diesen Jobs probiert. Aber will halt einfach nicht funktionieren. Es gäbe dann noch irgendwelche .net befehle (oder wie man das nennt), wo man selbst in älteren Powershell-versionen jobs an einer bestimmten stelle (im job) warten lassen kann.

Aber habs ja zuerst an der 1. schleife probiert und bin gescheitert.

Aber vlt. probiere ich es ja mal mit powershell 7 so für mich einfach

Platos
2023-12-12, 07:41:17
Neueste Version 16_5 mit leicht veränderter $resolution und $filenamecheck. Einige Zähler und debug(logging) Funktionen gelöscht. Brauche ich nicht mehr.

Gefällt mir mittlerweile relativ gut.

# Am Anfang des Skripts
$logFile = "PFAD\ZUR\LOG-DATEI\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "PFAD\ZUM\ORDNER"
$outputFolder = "PFAD\ZUM\ZIELODRNER"
$csvPath = "PFAD\ZUR\CSV-DATEI\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
if ($content[10] -match "DDSFormat=(.*)") {
@{
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
$totalDdsFiles = 0
$totalTxtFilesWithDDSFormat = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary
$duplikate = @{}
# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$totalTxtFilesWithDDSFormat++
}
if ($result.DDSFormat) {
$totalTxtFiles++
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_color"
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

#Überprüft und sortiert anhand der Auflösung
if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
# Definieren Sie die Bedingungen und zugehörigen Auflösungen
$conditions = @{
"_LowRes" = @{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) } }
"_UltraHighResSquare" = @{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height } }
"_UltraHighResWide" = @{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 } }
"_UltraHighResTall" = @{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 } }
"_HyperHighResSquare" = @{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height } }
"_HyperHighResWide" = @{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 } }
"_HyperHighResTall" = @{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 } }
"_HighResSquare" = @{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height } }
"_HighResWide" = @{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 } }
"_HighResTall" = @{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 } }
"_SuperHighResSquare" = @{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 } }
"_SuperHighResWide" = @{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 } }
"_SuperHighResTall" = @{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 } }
}

# Durchlaufen Sie die Bedingungen
foreach ($resolution in $conditions.Keys) {
# Wenn die Bedingung erfüllt ist, führen Sie die zugehörige Funktion aus und brechen Sie die Schleife ab
if (& $conditions[$resolution].Condition) {
$grandParentFolder += $resolution
break
}
}
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Überprüft und sortiert anhand der Dateinamen
$filenameChecks = @{
"BM.*dds$" = @{ FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält" }
"_CM([^M].*|)\\.dds$" = @{ FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält" }
"GM.*dds$" = @{ FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält" }
"NUKI.*dds$" = @{ FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält" }
"CMM.*dds$" = @{ FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält" }
"_MM.*dds$" = @{ FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält" }
"AM.*dds$" = @{ FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält" }
"LM.*dds$" = @{ FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält" }
"TM.*dds$" = @{ FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält" }
"HM.*dds$" = @{ FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält" }
"DM.*dds$" = @{ FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält" }
}

# Durchlaufen Sie die Bedingungen
foreach ($regex in $filenameChecks.Keys) {
# Wenn die Bedingung erfüllt ist, führen Sie die zugehörige Funktion aus und brechen Sie die Schleife ab
if ($ddsFile.Name -cmatch $regex) {
$fileNameCheck = $filenameChecks[$regex].FileName
if ($fileNameCheck -eq "_DM") {
if ($parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
} else {
$parentFolder += $fileNameCheck
}
} else {
$parentFolder += $fileNameCheck
}
}
}
}

$parentFolder += $resolution

# Definieren Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$path = $destinationFolder + $ddsFile.Name

# Wenn der Pfad bereits im Dictionary existiert, erhöhen Sie den Zähler
if ($duplikate.ContainsKey($path)) {
$duplikate[$path]++
} else {
# Wenn der Pfad noch nicht im Dictionary existiert, fügen Sie ihn hinzu und setzen Sie den Zähler auf 1
$duplikate[$path] = 1
}

$duplikatZahl = $duplikate[$path]

# Fügen Sie die DuplikatZahl am DestinationFolder an, wenn der Zähler größer als 1 ist
if ($duplikatZahl -gt 1) {
$destinationFolder += "_$duplikatZahl"
}

if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
}
}

if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien, der gefundenen .txt-Dateien mit "DDSFormat=" und der gefundenen .txt dateien mit "DDSFormat=", "Width=" und "Height="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $totalTxtFiles .txt-Dateien mit 'DDSFormat=' gefunden."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

Evtl. tausche ich bei der Dateinamen-Prüfung einige regex durch was perfomanteres aus. Muss aber noch schauen, was perfomanter ist.

#44
2023-12-12, 07:57:09
Ich habe jetzt beim Profilen aber eine komische Sache entdeckt. Ich habe am Anfang des skripts das mit den Zeilen umgesetzt. Also dass ich nur noch in zeile 4,5 und 11 suche. Jetzt habe ich habe festgestellt, dass ich die doppelte Anzahl an matches habe (sagt PSProfiler). Die Anzahl kopierter Dateien ist am Ende aber gleich. Also funktionieren tut alles.

Hier der auszug mit der relevanten codestell:

0 17 00:00.0000000 function Search-DDSFormat {
0 18 00:00.0000000 param (
0 19 00:00.0000000 [string]$filePath
0 20 00:00.0000000 )
7341 21 00:03.1821279 $content = Get-Content -Path $filePath
14682 22 00:00.1644072 $match = if ($content[10] -match "DDSFormat=(.*)") { $Matches[1] } else { $null }
14682 23 00:00.0870273 $width = if ($content[3] -match "Width=(.*)") { $Matches[1] } else { $null }
14682 24 00:00.0788778 $height = if ($content[4] -match "Height=(.*)") { $Matches[1] } else { $null }
0 25 00:00.0000000 if ($match) {
6189 26 00:00.0377528 @{
0 27 00:00.0000000 'DDSFormat' = $match
0 28 00:00.0000000 'Width' = $width

0 29 00:00.0000000 'Height' = $height

0 30 00:00.0000000 }
0 31 00:00.0000000 }
0 32 00:00.0000000 }

Die Zahl 14682 ist genau doppelt so gross wie die Anzahl .txt dateien (7341). Warum passiert das? Habe ich diesen Teil falsch gemacht ?

edit: Vergessen die Datei anzuhängen:

Wie ich sagte: Wer misst misst Mist. Profiler sind etwas verzwickt. Einerseits müssen die Resultate interpretiert werden (!), anderseits verändert Profiling das Laufzeitverhalten und stellt deshalb keine absolute Wahrheit dar.

Das ist so ein Fall, bei dem die Interpretation wichtig ist.

Die Zeilen davor und danach haben die erwartete Aufrufzahl. Dass Codezeilen doppelt ausgeführt werden passiert nicht.
Meine Schlussfolgerung wäre also: Auf den Zeilen sind keine simplen Statements - da werden schlicht jeweils zwei gezählte Aufrufe pro Zeile gemacht.


Die Abweichung zwischen den Files und den Matches zeigt mir außerdem, dass du den 2. Vorschlag aus Post #71 nicht integriert hast. Die arc.txt Dateien müssen nämlich nicht mit untersucht werden. Das hat zwar kaum Auswirkung auf die Performance - macht aber deutlicher was dein Code tun soll.

Die Search-DDSFormat Funktion lässt sich auch noch etwas entschlacken:

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
if ($content[10] -match "DDSFormat=(.*)") {
@{
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

Und man könnte den Parameter noch zu einem FileInfo machen, dann spart man sich etwas umkopiererei. Beispiel folgt.

Platos
2023-12-12, 08:02:35
Danke, ich konnte das mittlerweile lösen. Ich verstehe selbst nicht ganz wieso, aber so, wie ich es gemacht habe, funktioniert es.

Habe gerade die neueste Version hier gepostet. Da siehst du die version, die funktioniert (habs irgendwie hingekriegt). Das mit den .arc .txt habe ich umgesetzt.

Edit: Danke, das mit der function Search-DDSFormat sieht gut aus. Habe ich gleich oben im Kommentar mit aktualisiert :)

#44
2023-12-12, 08:34:52
Habe gerade die neueste Version hier gepostet. Da siehst du die version, die funktioniert (habs irgendwie hingekriegt). Das mit den .arc .txt habe ich umgesetzt.

Edit: Danke, das mit der function Search-DDSFormat sieht gut aus. Habe ich gleich oben im Kommentar mit aktualisiert :)
$totalTxtFiles zählt mittlerweile das Falsche. Der sollte außerhalb des if sein, damit er wirklich alle TXT-Dateien zählt. €: Eigentlich ist zählen ja auch nicht nötig... Wir haben ja ein Array mit allen Files, da können wir die Anzahl einfach abfragen.
Und die beiden If in der ersten Schleife haben die selbe Bedingung - können also auch zusammengefasst werden. Und weil die Elemente eh mitgezählt werden, wäre eine for-Schleife das Mittel der Wahl.

param ([System.IO.FileInfo]$fileInfo)
$content = Get-Content $fileInfo
if ($content[10] -match "DDSFormat=(.*)") {
@{
'File' = $fileInfo
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}


$totalTxtFiles = $files.Count
for ($i = 1; $i -le $totalTxtFiles; ++$i)
{
if ($i % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $i of $totalTxtFiles" -PercentComplete (($i / $totalTxtFiles) * 100)
}
$result = Search-DDSFormat $files[$i-1]
if ($result) {
$totalTxtFilesWithDDSFormat++
$ddsFormatFiles.Add($result)
}
}


€: Nicht zu vergessen:
$file = $item['File']
$ddsFormat = $item['DDSFormat']
$width = $item['Width']
$height = $item['Height']

Platos
2023-12-12, 16:22:38
warum spart man sich bei einem fileinfo umkopiererei?
Die For-Schleife sieht irgendwie viel komplizierter aus. Sicher,dass das perfomanter ist ?

Und ja, stimmt. Die .txt Datei-Zählung brauche ich eig. nicht mehr zwingend. Ich habe sie ja nicht mal mehr definiert, bevor der Zähler hochgeht. Habe das vergessen raus zu löschen. Nur die $totalTxtFilesWithDDSFormat und die am Ende kopierte Zahl um sicher zu gehen, dass alles geklappt hat.

ich werde die Zählung von $totalTxtFilesWithDDSFormat aber auch raus nehmen und eifnach diese variable nehmen, die sowieso definiert wird für den Fortschrittsbalken: $ddsFormatFilesCount = $ddsFormatFiles.Count

Ich habe das jetzt so gemacht:

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
if ($content[10] -match "DDSFormat=(.*)") {
@{
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
$totalDdsFiles = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary (Für die Duplikatprüfung bzw die Erstellung der $destinationFolder Variable)
$duplikate = @{}

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
$totalDdsFiles++

und dann unten die Ausgabe so definiert von den .txt Dateien mit DDSFormat:

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien und der gefundenen .txt-Dateien mit "DDSFormat="
if ($debug1) {
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
$output1 += "`nEs wurden insgesamt $ddsFormatFiles.Count .txt-Dateien mit 'DDSFormat=' gefunden."
}

weil $ddsFormatFiles.Count ist im grunde die Anzahl .txt Dateien, die "DDSFormat=" enthalten, so viel ich weiss.

Edit: Hmm nein doch nicht, irgendwie funktioniert das gar nicht :D

Edit 2:

So funktioniert es:

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien und der gefundenen .txt-Dateien mit "DDSFormat="
if ($debug1) {
$output1 += "`nEs wurden insgesamt " + $ddsFormatFiles.Count + " .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
}

Muss man anscheinend so schreiben (habe noch die Zeile verschoben).

#44
2023-12-12, 16:33:51
warum spart man sich bei einem fileinfo umkopiererei?
Die For-Schleife sieht irgendwie viel komplizierter aus. Sicher,dass das perfomanter ist ?
Search-DDSFormat erzeugt ein neues Objekt
@{
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}

Das wird in der Foreach-Schleife dann nochmal in ein neues Objekt verpackt, um es in die Liste aufzunehmen:

if ($result.DDSFormat) {
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}

Das spart man sich, wenn Search-DDSFormat direkt das Objekt so erzeugt, wie man es hinterher haben möchte.

Da geht es auch weniger um Performance, als um Lesbarkeit.

$totalTxtFiles = $files.Count
for ($i = 1; $i -le $totalTxtFiles; ++$i)
{
if ($i % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $i of $totalTxtFiles" -PercentComplete (($i / $totalTxtFiles) * 100)
}
$result = Search-DDSFormat $files[$i-1]
if ($result) {
$totalTxtFilesWithDDSFormat++
$ddsFormatFiles.Add($result)
}
}

vs

$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

Weniger Umwege, weniger Code der gelesen und verstanden werden muss, wenn sich das jemand ansieht. Oder kannst du jemandem, der das liest, erklären wieso es das Umkopieren braucht? :wink:

Über for vs foreach kann man streiten. Ich bin aber der Meinung wenn man explizit eine Zählvariable haben will, macht es keinen Sinn foreach zu wählen. Der clou des foreach ist ja, dass man diese nicht hat. Die wieder dran zu bauen ist mmn. so ein bisschen das Eingeständnis, dass man eigentlich ja doch eine for-Schleife haben will.
Rein technisch gesehen sollte for auch performanter sein als foreach. Aber da sprechen wir in diesem Fall vmtl. von wenigen Nanosekunden.

Platos
2023-12-12, 17:16:22
Ah ok, ich verstehe. Lustigerweise hat es mich gestern schon so verwirrt, warum da 2x das gleiche steht.

Aber warum muss ich dafür von Param auf das andere wechseln (oben in der funktions-definition)?

den zähler lasse ich dann aber jetzt weg. Das mache ich ja mit der $ddsFormatFilesCount Variable, die ja weiter unten sowieso genutzt wird.

Aber warum mache ich es nicht so?:

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
if ($content[10] -match "DDSFormat=(.*)") {
@{
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

und:

$k = 0
foreach ($file in $files)
{
$k++
if ($k % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $totalTxtFiles" -PercentComplete (($k / $totalTxtFiles) * 100)
}
$result = Search-DDSFormat $file
if ($result) {
$ddsFormatFiles.Add($result)
}
}


also wo ist hier das mit dem fileinfo besser? Das verstehe ich nicht so ganz...

Also da ich ja jetzt keine Zählvariable mehr habe, ist eine foreach-schleife wieder ok oder? Oder war das auf den Fortschrittsbalken bezogen ?

Editiert

#44
2023-12-12, 17:48:20
Ah ok, ich verstehe. Lustigerweise hat es mich gestern schon so verwirrt, warum da 2x das gleiche steht.

Aber warum muss ich dafür von Param auf das andere wechseln (oben in der funktions-definition)?

den zähler lasse ich dann aber jetzt weg. Das mache ich ja mit der $ddsFormatFilesCount Variable, die ja weiter unten sowieso genutzt wird.

Aber warum mache ich es nicht so?:

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
if ($content[10] -match "DDSFormat=(.*)") {
@{
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

und:

$k = 0
foreach ($file in $files)
{
$k++
if ($k % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $totalTxtFiles" -PercentComplete (($k / $totalTxtFiles) * 100)
}
$result = Search-DDSFormat $file
if ($result) {
$ddsFormatFiles.Add($result)
}
}


also wo ist hier das mit dem fileinfo besser? Das verstehe ich nicht so ganz...
In deinem Beispiel fehlt in der Rückgabe der Funktion die Referenz auf die Datei.
Die willst du später ja mit $file = $item['file'] wieder bekommen.

Natürlich könnte man das auch ohne die Umstellung auf FileInfo machen und einfach den Dateipfad als String mitspeichern. Aber so macht man sich die Macht des Typensystems zu nutze. Damit können bei zukünftigen Umbauten gewisse Fehler leichter vermieden werden. Ein String ist ein String. Wenn da durch einen Fehler "asöklj" hinein geschrieben wird, musst du den Fehler aufwändig analysieren. Ein FileInfo ist nicht so flexibel. Da ist schonmal die Intention klar gemacht, dass es hier um Dateien gehen soll, nicht um Strings die beliebig sind. Außerdem - aber das ist nur eine Hoffnung von mir - kann der Get-Content Aufruf womöglich eine besser optimierte Zugriffstrategie nutzen, wenn er weiß, dass er da ein File bekommt, statt nur einen String. Get-Content kann nämlich mehr, als nur Dateien lesen. Wenn du einen String übergibst, muss es deshalb aber erst einmal herausfinden, was da nun gelesen werden soll.
Auch hier: Macht sicher eher nur Sekundenbruchteile aus. Aber die Summe der Vorteile ist ganz klar: Intention des Codes deutlich ausgedrückt. Typensystem unterstützt, wenn mal ein Fehler passiert. Und vielleicht sogar ein bisschen schneller.

Aus u.A. diesen Gründen ist ein "stringly typed system" (Verballhornung von strictly typed system) ein Anti-Pattern.


Also da ich ja jetzt keine Zählvariable mehr habe, ist eine foreach-schleife wieder ok oder? Oder war das auf den Fortschrittsbalken bezogen ?
Es war die Zählvariable der Schleife gemeint - also die Variable, die die Anzahl der Schleifendurchläufe mitzählt. Im Skript also das $k (in for-Schleifen nennt man die konventionell $i oder $j - wenn man Namen als diese Abkürzung für Index findet).

Monger
2023-12-12, 17:52:31
Rein technisch gesehen sollte for auch performanter sein als foreach. Aber da sprechen wir in diesem Fall vmtl. von wenigen Nanosekunden.
Theoretisch kann der Iterator einer Liste besser optimiert sein als ein Index-Zugriff. Ich hab vor langer Zeit mal Collection Typen implementiert, die als Fassade für ne sehr schwergewichtige Operation im Hintergrund dienten (Stells dir als Datenbank vor). Da der Iterator über die ganze Iteration lebt, konnte ich dort Dinge cachen die mir nen schnelleren Zugriff erlaubten. Beim Index weiß ich nicht ob grad Element für Element iteriert wird, oder sich nur einzelne Elemente gepickt werden, also kann ich nicht optimieren.

Das ist zugegebenermaßen megaexotisch. Ich wollte nur deutlich machen, dass foreach gegenüber for kein Nachteil sein muss, im Gegenteil.

Platos
2023-12-12, 17:55:21
Ah ok.

Ok, also das hier funktioniert nicht:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-ARCtool-entpackte\eq-ARCtool\ARCtool\eq"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param ([System.IO.FileInfo]$fileInfo)
$content = Get-Content $fileInfo
if ($content[10] -match "DDSFormat=(.*)") {
@{
'File' = $fileInfo
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
$totalDdsFiles = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary (Für die Duplikatprüfung bzw die Erstellung der $destinationFolder Variable)
$duplikate = @{}

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$totalTxtFiles = $files.Count
for ($i = 1; $i -le $totalTxtFiles; ++$i)
{
if ($i % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $i of $totalTxtFiles" -PercentComplete (($i / $totalTxtFiles) * 100)
}
$result = Search-DDSFormat $files[$i-1]
if ($result) {
$totalTxtFilesWithDDSFormat++
$ddsFormatFiles.Add($result)
}
}

Es hat jetzt auf einmal nur noch 3 Ordner im outputfolder und einer davon ist genau so benannt, wie der outputfolder.

Wenn ich die Definition der Funktion durch meine Version ersetze (die alte) und dann unten in der schleife deine version nehme, kriege ich Fehlermeldung dann in der Zeile, wo die .dds datei geholt werden sollte:

Split-Path : Das Argument kann nicht an den Parameter "Path" gebunden werden, da es NULL ist.
In Zeile:12 Zeichen:48
+ $ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filte ...
+ ~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Split-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.Spl itPathCo
mmand


Und wenn ich meine version von oben nehme, die du gesagt hast, geht nicht....Dann gehts nicht ^^

Also kurz gesagt: Es geht nur das, was ich ursprünglich hatte. Also in der funktion und in der schleife 2x das machen mit dem hinzufügen.

ALso nur für ein bisschen übersicht, lasse ich es lieber bei dem:

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\1Steam-upscaling-ordner-ARCtool-entpackte\eq-ARCtool\ARCtool\eq"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
if ($content[10] -match "DDSFormat=(.*)") {
@{
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
$totalDdsFiles = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary (Für die Duplikatprüfung bzw die Erstellung der $destinationFolder Variable)
$duplikate = @{}

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_color"
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

#Überprüft und sortiert anhand der Auflösung
if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
# Definieren Sie die Bedingungen und zugehörigen Auflösungen
$conditions = @{
"_LowRes" = @{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) } }
"_UltraHighResSquare" = @{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height } }
"_UltraHighResWide" = @{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 } }
"_UltraHighResTall" = @{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 } }
"_HyperHighResSquare" = @{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height } }
"_HyperHighResWide" = @{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 } }
"_HyperHighResTall" = @{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 } }
"_HighResSquare" = @{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height } }
"_HighResWide" = @{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 } }
"_HighResTall" = @{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 } }
"_SuperHighResSquare" = @{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 } }
"_SuperHighResWide" = @{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 } }
"_SuperHighResTall" = @{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 } }
}

# Durchlaufen Sie die Bedingungen
foreach ($resolution in $conditions.Keys) {
# Wenn die Bedingung erfüllt ist, führen Sie die zugehörige Funktion aus und brechen Sie die Schleife ab
if (& $conditions[$resolution].Condition) {
$grandParentFolder += $resolution
break
}
}
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

# Überprüft und sortiert anhand der Dateinamen
$filenameChecks = @{
"BM.*dds$" = @{ FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält" }
"_CM([^M].*|)\\.dds$" = @{ FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält" }
"GM.*dds$" = @{ FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält" }
"NUKI.*dds$" = @{ FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält" }
"CMM.*dds$" = @{ FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält" }
"_MM.*dds$" = @{ FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält" }
"AM.*dds$" = @{ FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält" }
"LM.*dds$" = @{ FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält" }
"TM.*dds$" = @{ FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält" }
"HM.*dds$" = @{ FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält" }
"DM.*dds$" = @{ FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält" }
}

# Durchlaufen Sie die Bedingungen
foreach ($regex in $filenameChecks.Keys) {
# Wenn die Bedingung erfüllt ist, führen Sie die zugehörige Funktion aus und brechen Sie die Schleife ab
if ($ddsFile.Name -cmatch $regex) {
$fileNameCheck = $filenameChecks[$regex].FileName
if ($fileNameCheck -eq "_DM") {
if ($parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
} else {
$parentFolder += $fileNameCheck
}
} else {
$parentFolder += $fileNameCheck
}
}
}
}

$parentFolder += $resolution

# Definieren Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$path = $destinationFolder + $ddsFile.Name

# Wenn der Pfad bereits im Dictionary existiert, erhöhen Sie den Zähler
if ($duplikate.ContainsKey($path)) {
$duplikate[$path]++
} else {
# Wenn der Pfad noch nicht im Dictionary existiert, fügen Sie ihn hinzu und setzen Sie den Zähler auf 1
$duplikate[$path] = 1
}

$duplikatZahl = $duplikate[$path]

# Fügen Sie die DuplikatZahl am DestinationFolder an, wenn der Zähler größer als 1 ist
if ($duplikatZahl -gt 1) {
$destinationFolder += "_$duplikatZahl"
}

if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
}
}

if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien und der gefundenen .txt-Dateien mit "DDSFormat="
if ($debug1) {
$output1 += "`nEs wurden insgesamt " + $ddsFormatFiles.Count + " .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

#44
2023-12-12, 17:59:15
Das ist zugegebenermaßen megaexotisch. Ich wollte nur deutlich machen, dass foreach gegenüber for kein Nachteil sein muss, im Gegenteil.
Stimmt. Ich bin vom einfachen Fall ausgegangen, dass wir einen Iterator über eine Collection haben, die sowieso wahlfreien Zugriff unterstützt.

Die von dir genannten Operationen würde man eher nicht mit einem Index Operator ausstatten und so aussehen lassen, als ob wahlfreier Zugriff billig wäre. Also hoffentlich. Ich würd's jedenfalls nicht tun. Am Ende kommt noch jemand auf die Idee und nutzt das :biggrin:

@Platos: Hast du das Naming beim Auslesen des Results auch entsprechend angepasst? Die Felder des Objekts heißen dann ja auch anders - nämlich so, wie von Search-DDSFormat definiert.

Diese Stelle nach der Berechnung der 2. Progressbar meine ich:
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']

Ich frage, weil den Teil sieht man in deinem Codeschnipsel nicht mehr.

Platos
2023-12-12, 18:13:10
Ach, die Version 16_5 funktioniert ja auch nicht...
Das funktioniert allgemein nicht.

Und ja ok, habe das jetzt so angepasst (und analog auch bei 16_5):

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param ([System.IO.FileInfo]$fileInfo)
$content = Get-Content $fileInfo
if ($content[10] -match "DDSFormat=(.*)") {
@{
'File' = $fileInfo
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
$totalDdsFiles = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary (Für die Duplikatprüfung bzw die Erstellung der $destinationFolder Variable)
$duplikate = @{}

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$totalTxtFiles = $files.Count
for ($k = 1; $k -le $totalTxtFiles; ++$k)
{
if ($k % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $totalTxtFiles" -PercentComplete (($k / $totalTxtFiles) * 100)
}
$result = Search-DDSFormat $files[$k-1]
if ($result) {
$ddsFormatFiles.Add($result)
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 200 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['File']
$ddsFormat = $item['DDSFormat']
$width = $item['Width']
$height = $item['Height']

Das ist ja noch schlimmer. jetzt heissen meine Ordner "System.Collections.Hashtable"

Die letzte version, die funktioniert, ist 16_4

myMind
2023-12-12, 18:13:52
Theoretisch kann der Iterator einer Liste besser optimiert sein als ein Index-Zugriff. Ich hab vor langer Zeit mal Collection Typen implementiert, die als Fassade für ne sehr schwergewichtige Operation im Hintergrund dienten (Stells dir als Datenbank vor). Da der Iterator über die ganze Iteration lebt, konnte ich dort Dinge cachen die mir nen schnelleren Zugriff erlaubten. Beim Index weiß ich nicht ob grad Element für Element iteriert wird, oder sich nur einzelne Elemente gepickt werden, also kann ich nicht optimieren.

Das ist zugegebenermaßen megaexotisch. Ich wollte nur deutlich machen, dass foreach gegenüber for kein Nachteil sein muss, im Gegenteil.
Würde ich auch sagen. Eigentlich so:

$k = 0
Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt |
ForEach-Object {
$file = $_
$k++

Spart das Aufbauen der vollständige Collection. Damit lässt sich zwar kein Progress mehr anzeigen, dafür sollte das auch bei einer gigantomanisch großen Dateimenge performant bleiben...
...allerdings hat die Powershell wohl ihre Tücken: https://powershell.one/tricks/performance/pipeline

>>> Skriptsprachen sind schon speziell.

#44
2023-12-12, 18:17:43
Würde ich auch sagen. Eigentlich so:

$k = 0
Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt |
ForEach-Object {
$file = $_
$k++

Spart das Aufbauen der vollständige Collection. Damit lässt sich zwar kein Progress mehr anzeigen, dafür sollte das auch bei einer gigantomanisch großen Dateimenge performant bleiben...
Den Ansatz das hatte ich mir auch schon überlegt. Progressbar ließe sich schon auch machen - wir haben ja ein $k mit dem wir arbeiten können.

Das finde Ich dann aber für einen Einsteiger wirklich nicht mehr gut lesbar.

Platos
2023-12-12, 18:32:52
Ok, also diese Version (heisst jetzt 16_7) funktioniert.

# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param ([string]$filePath)
$content = Get-Content -Path $filePath
$match = if ($content[10] -match "DDSFormat=(.*)") { $Matches[1] } else { $null }
if ($match) {
$width = $content[3]
$height = $content[4]
@{
'DDSFormat' = $match
'Width' = $width -replace "Width="
'Height' = $height -replace "Height="
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
$totalDdsFiles = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary (Für die Duplikatprüfung bzw die Erstellung der $destinationFolder Variable)
$duplikate = @{}

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$result = Search-DDSFormat -filePath $file.FullName
if ($result.DDSFormat) {
$ddsFormatFiles.Add(@{
'file' = $file
'format' = $result.DDSFormat
'width' = $result.Width
'height' = $result.Height
})
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Running Script" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['file']
$result = $item['format']
$width = $item['width']
$height = $item['height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $result

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_color"
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if (& $resolutionCheckItem.Condition) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
break
}
}
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$fileNameCheck = $filenameCheckItem.FileName
if ($fileNameCheck -eq "_DM" -and $parentFolder -match "NM$") {
$parentFolder = $parentFolder -replace "NM$", "DM"
} else {
$parentFolder += $fileNameCheck
}
break
}
}
}

$parentFolder += $resolution

# Definieren Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$path = $destinationFolder + $ddsFile.Name

# Wenn der Pfad bereits im Dictionary existiert, erhöhen Sie den Zähler
if ($duplikate.ContainsKey($path)) {
$duplikate[$path]++
} else {
# Wenn der Pfad noch nicht im Dictionary existiert, fügen Sie ihn hinzu und setzen Sie den Zähler auf 1
$duplikate[$path] = 1
}

$duplikatZahl = $duplikate[$path]

# Fügen Sie die DuplikatZahl am DestinationFolder an, wenn der Zähler größer als 1 ist
if ($duplikatZahl -gt 1) {
$destinationFolder += "_$duplikatZahl"
}

if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $result
})
$totalCopiedFiles++
}
}

if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe des Ergebnisses des `Set-Content`-Befehls
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath
if ($debug2) { $output2 += "`n`$result is: $result" }

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien und der gefundenen .txt-Dateien mit "DDSFormat="
if ($debug1) {
$output1 += "`nEs wurden insgesamt " + $ddsFormatFiles.Count + " .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

Ok, der Grund, warum 16_5 nicht geht, ist die änderung bei den fileNameChecks und ResolutionChecks. Irgendwas ist da vermurkst in der Version 16_5. In der Version 16_7 geht es.

Ändert aber nichts daran, dass das mit der for schleife und dem System.Io.File info nicht geht. Da kommen auch mit der Version 16_7 als grundlage die selben Fehler raus.

Also das ist mir dann wirklich zu viel trouble nur für bisschen bessere Lesbarkeit. Ich nehme dann jetzt die Version 16_7

Die sollte nun wirklich funktionieren. Habe es mit einer älteren Version abgeglichen und alle Ordnernamen verglichen. Sollte jetzt stimmen. Und ich persönlich finde das eigentlich nicht schlecht lesbar so. Die andere Version finde ich fast komplizierter, da ich nie damit gearbeitet habe jetzt.

Ich brauche mal ne pause von dem Skript...
Ich arbeite dann mal an der FInalisierung des Skript 2 xD

Auf jeden Fall ein grosses Dankeschön an alle hier, besonders natürlich #44 für die Lösung des grossen Problems.

patermatrix
2023-12-12, 20:27:17
Noch ein dringender Tipp, abseits des Codes: Verwalte deinen Code mit git (https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).

Am besten via gratis Online-Repo (GitHub (https://github.com/), GitLab (https://gitlab.com/)), mindestens aber bei dir lokal. Dann hast du komplette Kontrolle über alle Änderungen und kannst jederzeit zu einem vorherigen Stand zurückkehren. Auch das Teilen des Codes mit den Leuten hier im Thread wird massiv vereinfacht.

Platos
2023-12-12, 23:41:35
gute Idee. Wobei ich Upload bei nicht fertigem Code eher nicht so mag. Vor allem, wenn es möglicherweise nicht funktionsfähig ist (unbewusst) usw. Kann man da bei Github was drehen?

Was ist denn eig. der Unterschied von Github und Gitlab?

Aber Beim Skript 1 lasse ich das jetzt mal so. To much trouble für mich, wenns jetzt eh nur noch um bisschen "hübsch machen" geht. Aber ist jetzt nicht so, dass ich die ganzen Verbesserungsvorschläge nicht zu würdigen weiss. Aber das Skipt1 muss ich mal ruhen lassen :D

Also nochmals Danke an alle :)

Jetzt gehts mal an das Skipt 2. Das besteht quasi nur aus Double-Check Feature, damit man beim Zurück-Kopieren bei Fehlern direkt eine Liste kriegt.

Aber vlt. kann ich das auch so optimieren, wie jetzt das erste skript. Weil das frisst hald auch ordentlich Perfomance dieses drölffache abfragen um auf Nummer Sicher zu gehen.

#44
2023-12-13, 08:59:40
Also das ist mir dann wirklich zu viel trouble nur für bisschen bessere Lesbarkeit. Ich nehme dann jetzt die Version 16_7
Das kann nur jemand sagen, der noch nie nach einem halben Jahr zu seinem eigenen Code zurückgekehrt ist und dann dachte "WTF?" :freak:


Ich hab es mal eingebaut. Außerdem den Fortschrittsbalken einen besseren Text verpasst. Den Sonderfall im Filenamecheck vereinfacht. Und einer von drei $result Variablen einen anderen Namen verpasst.

Output ist für eq identisch zu V14 - ich habe die Mapping.csv verglichen.


# Am Anfang des Skripts
$logFile = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\log.txt"
Start-Transcript -Path $logFile -Append

# Geben Sie Ihre Pfade an
$rootFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\ARCtool\enemy"
$outputFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled"
$csvPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Debugging-Flags
$debug1 = $true # Aktiviert die von Ihnen angegebenen Debugging-Ausgaben
$debug2 = $false # Deaktiviert alle anderen Debugging-Ausgaben
$debug3 = $true # Deaktiviert die Zählfunktion von totalDdsFilesInOutputFolder (Es wird ausgelesen, wie viele .dds Dateien sich am Ende im outputFolder befinden)

# Erstellen Sie leere Strings für die Ausgaben
$output1 = ""
$output2 = ""
$output3 = ""

function Search-DDSFormat {
param ([System.IO.FileInfo]$fileInfo)

$content = Get-Content $fileInfo

if ($content[10] -match "DDSFormat=(.*)") {
@{
'FileInfo' = $fileInfo
'DDSFormat' = $Matches[1]
'Width' = $content[3] -replace "Width="
'Height' = $content[4] -replace "Height="
}
}
}

# Definition einiger Variablen
$mapping = [System.Collections.Generic.List[Object]]::New()
$ddsFormatFiles = [System.Collections.Generic.List[Object]]::New()

$files = Get-ChildItem -Path $rootFolder -Recurse -Filter *.txt -Exclude *.arc.txt
$totalDdsFiles = 0
$totalCopiedFiles = 0

# Erstellen Sie ein leeres Dictionary (Für die Duplikatprüfung bzw die Erstellung der $destinationFolder Variable)
$duplikate = @{}

# Durchsuchen des Inhalts der .txt Dateien im rootFolder
$filesCount = $files.Count
$k = 0
foreach ($file in $files) {
$k++
if ($k % 100 -eq 0) {
Write-Progress -Activity "Reading dds info" -Status "Processing File $k of $filesCount" -PercentComplete (($k / $filesCount) * 100)
}
if ($debug2) { $output2 += "`nÜberprüfe Datei: $($file.FullName)" } # Debugging-Ausgabe
$ddsInfo = Search-DDSFormat $file
if ($ddsInfo.DDSFormat) {
$ddsFormatFiles.Add($ddsInfo)
}
}

# Filterung und Kopiervorgang der dds Dateien
$ddsFormatFilesCount = $ddsFormatFiles.Count
$j = 0
foreach ($item in $ddsFormatFiles) {
$j++
$resolution = ""
$fileNameCheck = ""
if ($j % 100 -eq 0) {
Write-Progress -Activity "Sorting dds files" -Status "Processing Item $j of $ddsFormatFilesCount" -PercentComplete (($j / $ddsFormatFilesCount) * 100)
}
$file = $item['FileInfo']
$ddsFormat = $item['DDSFormat']
$width = $item['Width']
$height = $item['Height']
$ddsFile = Get-ChildItem -Path (Split-Path $file.FullName) -Filter ($file.BaseName + ".dds")
if ($ddsFile) {
$totalDdsFiles++
$godFatherFolder = Join-Path -Path $outputFolder -ChildPath $ddsFormat

# Prüfen, ob die Breite und die Höhe kleiner oder gleich 16 sind
if ([int]$width -le 16 -and [int]$height -le 16) {
$parentFolder = $godFatherFolder + "_UltraLowRes"
}
# Prüfen, ob der Dateiname der .dds-Datei dem Muster "*col*_ID.dds" entspricht
elseif ($ddsFile.Name -match ".*col.*_ID.dds$") {
$parentFolder = $godFatherFolder + "_color"
}
else {
# Prüfen, ob der Dateiname der .dds-Datei "NM" oder "DM" enthält
if ($ddsFile.Name -cmatch "NM.*dds$") {
$godFatherFolder += "_NM"
} elseif ($ddsFile.Name -cmatch "DM.*dds$") {
$godFatherFolder += "_NM"
}

# Definieren Sie den Pfad für den GrandParentFolder innerhalb des GodFatherFolder
$grandParentFolder = Join-Path -Path $godFatherFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$resolutionCheckList = @(
@{ Condition = { [int]$width -le 64 -and [int]$height -le 64 -and ([int]$width -gt 16 -or [int]$height -gt 16) }; Resolution = "_LowRes"; Description = "Prüfen, ob die Auflösung niedrig ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -eq [int]$height }; Resolution = "_UltraHighResSquare"; Description = "Prüfen, ob die Auflösung ultra hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1536 -and [int]$width -le 2048 -and [int]$height -le 1536 }; Resolution = "_UltraHighResWide"; Description = "Prüfen, ob die Auflösung ultra hoch und breit ist" }
@{ Condition = { [int]$height -gt 1536 -and [int]$height -le 2048 -and [int]$width -le 1536 }; Resolution = "_UltraHighResTall"; Description = "Prüfen, ob die Auflösung ultra hoch und hoch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -gt 2048 -and [int]$width -eq [int]$height }; Resolution = "_HyperHighResSquare"; Description = "Prüfen, ob die Auflösung hyper hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 2048 -and [int]$height -le 2048 }; Resolution = "_HyperHighResWide"; Description = "Prüfen, ob die Auflösung hyper hoch und breit ist" }
@{ Condition = { [int]$height -gt 2048 -and [int]$width -le 2048 }; Resolution = "_HyperHighResTall"; Description = "Prüfen, ob die Auflösung hyper hoch und hoch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -eq [int]$height }; Resolution = "_HighResSquare"; Description = "Prüfen, ob die Auflösung hoch und quadratisch ist" }
@{ Condition = { [int]$width -gt 1024 -and [int]$width -lt 1536 -and [int]$height -le 1024 }; Resolution = "_HighResWide"; Description = "Prüfen, ob die Auflösung hoch und breit ist" }
@{ Condition = { [int]$height -gt 1024 -and [int]$height -lt 1536 -and [int]$width -le 1024 }; Resolution = "_HighResTall"; Description = "Prüfen, ob die Auflösung hoch und hoch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -eq 1536 }; Resolution = "_SuperHighResSquare"; Description = "Prüfen, ob die Auflösung super hoch und quadratisch ist" }
@{ Condition = { [int]$width -eq 1536 -and [int]$height -lt 1536 }; Resolution = "_SuperHighResWide"; Description = "Prüfen, ob die Auflösung super hoch und breit ist" }
@{ Condition = { [int]$height -eq 1536 -and [int]$width -lt 1536 }; Resolution = "_SuperHighResTall"; Description = "Prüfen, ob die Auflösung super hoch und hoch ist" }
)

if ([int]$width -le 1024 -and [int]$height -le 1024 -and [int]$width -gt 64 -and [int]$height -gt 64) {
# Keine der Sonderauflösungen trifft zu
} else {
foreach ( $resolutionCheckItem in $resolutionCheckList ) {
if (& $resolutionCheckItem.Condition) {
$resolution = $resolutionCheckItem.Resolution
$grandParentFolder += $resolution
break
}
}
}

# Definieren Sie den Pfad für den ParentFolder innerhalb des GrandParentFolder
$parentFolder = Join-Path -Path $grandParentFolder -ChildPath (Split-Path $godFatherFolder -Leaf)

$filenameCheckList = @(
@{ Regex = "BM.*dds$"; FileName = "_BM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'BM' enthält"}
@{ Regex = "_CM([^M].*|)\\.dds$"; FileName = "_CM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CM' (aber nicht CMM) enthält"}
@{ Regex = "GM.*dds$"; FileName = "_GM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'GM' enthält"}
@{ Regex = "NUKI.*dds$"; FileName = "_NUKI"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'NUKI' enthält"}
@{ Regex = "CMM.*dds$"; FileName = "_CMM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'CMM' enthält"}
@{ Regex = "_MM.*dds$"; FileName = "_MM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'MM' enthält"}
@{ Regex = "AM.*dds$"; FileName = "_AM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'AM' enthält"}
@{ Regex = "LM.*dds$"; FileName = "_LM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'LM' enthält"}
@{ Regex = "TM.*dds$"; FileName = "_TM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'TM' enthält"}
@{ Regex = "HM.*dds$"; FileName = "_HM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'HM' enthält"}
@{ Regex = "DM.*dds$"; FileName = "_DM"; Description = "Prüfen, ob der Dateiname der .dds-Datei 'DM' enthält"}
)

foreach ( $filenameCheckItem in $filenameCheckList ) {
if ($ddsFile.Name -cmatch $filenameCheckItem.Regex) {
$parentFolder += $filenameCheckItem.FileName
break
}
}
$parentFolder = $parentFolder -replace "NM_DM$", "DM"
}

$parentFolder += $resolution

# Definieren Sie den DestinationFolder innerhalb des ParentFolder
$destinationFolder = Join-Path -Path $parentFolder -ChildPath (Split-Path $parentFolder -Leaf)

$path = $destinationFolder + $ddsFile.Name

# Wenn der Pfad bereits im Dictionary existiert, erhöhen Sie den Zähler
if ($duplikate.ContainsKey($path)) {
$duplikate[$path]++
} else {
# Wenn der Pfad noch nicht im Dictionary existiert, fügen Sie ihn hinzu und setzen Sie den Zähler auf 1
$duplikate[$path] = 1
}

$duplikatZahl = $duplikate[$path]

# Fügen Sie die DuplikatZahl am DestinationFolder an, wenn der Zähler größer als 1 ist
if ($duplikatZahl -gt 1) {
$destinationFolder += "_$duplikatZahl"
}

if (-not (Test-Path $destinationFolder)) {
$newItemResult = New-Item -ItemType Directory -Path $destinationFolder
}

Copy-Item -Path $ddsFile.FullName -Destination $destinationFolder

# Erstellen Sie den Pfad zur kopierten .dds-Datei
$newDdsFilePath = Join-Path -Path $destinationFolder -ChildPath $ddsFile.Name

$mapping.Add([PSCustomObject]@{
OldDdsFilePath = $ddsFile.FullName
NewDdsFilePath = $newDdsFilePath
DDSFormat = $ddsFormat
})
$totalCopiedFiles++
}
}

if ($debug2) {
$output2 += "`n`$mapping has $(($mapping | Measure-Object).Count) items before Export-Csv"
$output2 += "`n`$csvPath is: $csvPath"
}

# Versuch, die $mapping-Variable in eine Datei mit der Add-Content-Funktion anstelle von Export-Csv zu schreiben
try {
$result = $mapping | ConvertTo-Csv -NoTypeInformation -Delimiter "," | Out-String | Set-Content -Path $csvPath -ErrorAction Stop
if ($debug2) { $output2 += "`n`$result is: $result" }
} catch {
if ($debug2) { $output2 += "`nFehler beim Schreiben der CSV-Datei: $($_.Exception.Message)" }
}

# Ausgabe der Gesamtanzahl der gefundenen .dds-Dateien, der kopierten .dds-Dateien und der gefundenen .txt-Dateien mit "DDSFormat="
if ($debug1) {
$output1 += "`nEs wurden insgesamt " + $ddsFormatFilesCount + " .txt-Dateien mit 'DDSFormat=' gefunden."
$output1 += "`nEs wurden insgesamt $totalDdsFiles .dds-Dateien gefunden."
$output1 += "`nEs wurden insgesamt $totalCopiedFiles .dds-Dateien kopiert."
}

# Durchsuchen Sie den Output-Ordner und alle seine Unterordner nach .dds-Dateien und zählen Sie diese
if ($debug3) {
$ddsFilesInOutputFolder = Get-ChildItem -Path $outputFolder -Recurse -Filter *.dds
$totalDdsFilesInOutputFolder = ($ddsFilesInOutputFolder | Measure-Object).Count
$output3 += "`nEs wurden insgesamt $totalDdsFilesInOutputFolder .dds-Dateien im Ausgabeordner gefunden."
}

# Geben Sie die Debugging-Ausgaben am Ende des Skripts aus
Write-Host $output2
Write-Host $output1
Write-Host $output3

# Am Ende des Skripts
Stop-Transcript

Asaraki
2023-12-13, 09:14:13
Was ist denn eig. der Unterschied von Github und Gitlab?


Darunter ist immer GIT. Und oben drauf kann man verschiedene Tools setzen wie GitLab, GitHub, Sourcetree, Tortoise GIT. Manche sind nur ein GUI und andere eher dafür gedacht in grossen Firmen eingesetzt zu werden.

Wir arbeiten z.B. mit GitLab, das unterstützt halt so Dinge wie Reviews und Approvals damit nicht ein Junior irgendwas in den release branch schmeisst :D

Platos
2023-12-13, 12:57:38
@ #44: Danke, ich nehm's gerne an. Ich werde es dann später mal testen/einbauen. Mir wars einfach zu blöd, da noch weiter Zeit zu investieren. Ich optimiere ja gerne, aber nur, wenns mir was bringt (und reine optik im Code interrssiert mich (noch?) nicht).

Bin gerade am Skript 2 drann. Ich stelle es gerne hier rein, wenn ich fertig bin, wenn jemand Lust hat, drüber zu schauen :)

Deine Hernagehensweise hat mir auf jeden Fall geholfen. Vor allem, dass man am besten nur 1x etwas einliest und dann immer schön alles irgendwie versucht aus variablen/listen auszulesen. Mir war am Anfang gar nicht klar, dass Variablen/Listen bedeutet, das das Zeug im RAM ist und beim Rest muss man vlt. auf die SSD zugreifen und "echte" Dateizugriffe ausführen, was langsam ist. So in etwa hab ich das verstanden zumindest.

@Asaraki: Danke, ich werde es mir mal ansehen. Also Github habe ich schon. Wird dann vlt. das

Platos
2023-12-14, 23:17:19
Falls jemand Lust hat, mir mit meinem Skript 2 zu helfen, dann wäre (ab) jetzt der richtige Zeitpunkt:biggrin:

Ne mal im ernst, falls jemand mal kurz drüber blicken könnte, wäre ich froh. es geht "nur" um ein Feature, das nicht läuft. Ich versuche es schon seit Stunden und es klappt einfach nicht (ich erkläre gleich unten, um was es geht)

Bevor ihr euch das Skript ansieht und gleich ne Krise kriegt: Es geht nur um eine Stelle (siehe unten).
Aber hier mal skript2 Version 11_2 (ich habe das mit Github noch nicht umgesetzt sooorry):

# Lesen der CSV-Datei mit den Zuordnungen von .txt- zu .dds-Dateien
$mapping = Import-Csv -Delimiter "," -Path "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Definieren Sie eine Variable, um das Feature zum Auslassen von Ordnern beim Kopieren ein- und auszuschalten
$excludeFoldersFeature = $true # Setzen Sie dies auf $false, um das Feature auszuschalten

# Definieren Sie eine Variable, um zu steuern, welches Muster verwendet werden soll
$useAlternativeExcludePattern = $true # Setzen Sie dies auf $false, damit das normale Muster ($excludedFolderPattern) aktiv ist (wenn $excludedFoldersFeature auf $true ist)

# Definieren Sie den Basispfad und das Regex-Muster für die auszulassenden Ordner
$baseFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled" # Ersetzen Sie dies durch den tatsächlichen Basispfad
$excludedFolderPattern = ".*(_UltraLowRes|_color)$" # Ersetzen Sie dies wenn gewünscht durch ein eigenes Regex-Muster

# Definieren Sie das Regex-Muster für die Ordner, die nicht ausgeschlossen werden sollen (also alles, bis auf das Muster, wird ausgeschlossen)
$excludedInverseFolderPattern = ".*(_UltraLowRes|_color)$" # Ersetzen Sie dies wenn gewünscht durch ein eigenes Regex-Muster

# Ermitteln Sie alle Ordner im Basisverzeichnis, die mit dem ausgewählten Regex-Muster übereinstimmen, wenn das Feature aktiviert ist
$excludedFolders = @{}
Get-ChildItem -Path $baseFolder -Directory | ForEach-Object {
if ($excludeFoldersFeature) {
if ($useAlternativeExcludePattern) {
if ($_.Name -notmatch $excludedInverseFolderPattern) {
$excludedFolders[$_.FullName] = $true
}
} else {
if ($_.Name -match $excludedFolderPattern) {
$excludedFolders[$_.FullName] = $true
}
}
}
}
$excludedFolderNames = $excludedFolders.Keys | ForEach-Object { $_.Replace($baseFolder, "") }

# Definieren Sie den Pfad zur Debug-Logdatei
$debugLogPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\debuglog.txt"

# Fügen Sie die absoluten Pfade der $excludedFolders zur Debug-Logdatei hinzu
Add-Content -Path $debugLogPath -Value "Excluded folders:"
$excludedFolders.Keys | ForEach-Object { Add-Content -Path $debugLogPath -Value $_ }

# Initialisieren Sie die Zählvariablen und die Gruppen
$unchangedFiles = @()
$largerSizeFiles = @()
$nonExistentFiles = @{} # Initialisieren Sie die Hashtable für nicht existierende Dateien

# Durchlaufen Sie alle Einträge in der CSV-Datei
foreach ($entry in $mapping) {

# Überprüfen Sie, ob die Dateien existieren
if ((Test-Path $entry.OldDdsFilePath) -and (Test-Path $entry.NewDdsFilePath)) {
# Überprüfen Sie, ob der Pfad der Datei in der Liste der auszulassenden Ordner enthalten ist, wenn das Feature aktiviert ist
$parentFolder = (Get-Item (Split-Path $entry.NewDdsFilePath -Parent)).FullName
if ($excludeFoldersFeature -and ($excludedFolders.Keys | Where-Object { $parentFolder -like "$_*" })) {
continue
}
# Vergleichen Sie die Dateigrößen
$oldSize = (Get-Item $entry.OldDdsFilePath).Length
$newSize = (Get-Item $entry.NewDdsFilePath).Length

if ($newSize -gt $oldSize) {
$largerSizeFiles += [PSCustomObject]@{
OldDdsFilePath = $entry.OldDdsFilePath
NewDdsFilePath = $entry.NewDdsFilePath
NewSize = $newSize
}
} elseif ($newSize -eq $oldSize) {
$unchangedFiles += [PSCustomObject]@{
OldDdsFilePath = $entry.OldDdsFilePath
NewDdsFilePath = $entry.NewDdsFilePath
NewSize = $newSize
}
}
} else {
# Fügen Sie die Pfade der nicht existierenden Dateien zur Hashtable hinzu
$nonExistentFiles[$entry.OldDdsFilePath] = $entry.NewDdsFilePath
}

# Aktualisieren Sie die Fortschrittsanzeige alle 200 Dateien
if ($i % 200 -eq 0) {
Write-Progress -Activity "Kopieren von Dateien" -Status "$i von $($mapping.Count) Dateien verarbeitet" -PercentComplete ($i / $mapping.Count * 100)
}

# Erhöhen Sie die Zählvariable für die Fortschrittsanzeige
$i++
}

# Fügen Sie eine Warnung hinzu, falls Dateien nicht gefunden werden konnten
$notFoundMessage = if ($nonExistentFiles.Count -eq 0) { "Alle Dateien wurden gefunden." } else { "$($nonExistentFiles.Count) Dateien wurden nicht gefunden." }
$notFoundText = if ($nonExistentFiles.Count -gt 0) { " oder nicht gefunden wurden" } else { "" }

# Fragen Sie den Benutzer, ob er die Pfade und die Anzahl der Dateien in eine Logdatei schreiben möchte
$writeToLog = Read-Host "$notFoundMessage Es wurden $($unchangedFiles.Count) Dateien mit gleicher Dateigröße und $($largerSizeFiles.Count) Dateien mit unterschiedlicher Dateigröße gefunden. `nMöchten Sie die Pfade und die Anzahl der Dateien, die die gleiche Größe haben$notFoundText, in eine Logdatei schreiben? Die Logfile würde im baseFolder erstellt werden (J/N)"

if ($writeToLog -eq 'J') {
$logPath = Join-Path -Path $baseFolder -ChildPath "beforeCopyLog.txt"
# Schreiben Sie die Überschrift für die nicht gefundenen Dateien in die Logdatei
Add-Content -Path $logPath -Value "Nicht gefundene Dateien"
Add-Content -Path $logPath -Value "OldDdsFilePath,NewDdsFilePath"

# Schreiben Sie die Pfade der nicht gefundenen Dateien in die Logdatei
foreach ($file in $nonExistentFiles.GetEnumerator()) {
Add-Content -Path $logPath -Value "$($file.Key),$($file.Value)"
}

# Fügen Sie eine zusätzliche Leerzeile hinzu
Add-Content -Path $logPath -Value "`r`n"

# Schreiben Sie die Überschrift für die Dateien mit der gleichen Größe in die Logdatei
Add-Content -Path $logPath -Value "Dateien mit gleicher Größe"
Add-Content -Path $logPath -Value "OldDdsFilePath,NewDdsFilePath"

# Schreiben Sie die Pfade der Dateien mit der gleichen Größe in die Logdatei
foreach ($file in $unchangedFiles) {
Add-Content -Path $logPath -Value "$($file.OldDdsFilePath),$($file.NewDdsFilePath)"
}

# Fügen Sie eine zusätzliche Leerzeile hinzu
Add-Content -Path $logPath -Value "`r`n"
}

# Fügen Sie die ausgeschlossenen Ordner in die Meldung ein
$excludedFoldersMessage = if ($excludeFoldersFeature) { "Es wurden folgende Pfade vom Kopiervorgang ausgenommen: $excludedFolderNames." } else { "" }

# Fragen Sie den Benutzer, ob den Kopiervorgang starten will
$readHost = Read-Host "$excludedFoldersMessage Es wurden $($unchangedFiles.Count) Dateien mit gleicher Dateigröße und $($largerSizeFiles.Count) Dateien mit unterschiedlicher Dateigröße gefunden. `nMöchten Sie den Kopiervorgang jetzt starten? (J/N)"
if ($readHost -eq 'J') {

# Initialisieren Sie die Zählvariable und die Liste der kopierten Dateien
$successfulCopies = 0
$notSameSizeFiles = @()
$totalFiles = $mapping.Count

for ($i = 0; $i -lt $totalFiles; $i++) {
$entry = $mapping[$i]
$oldDdsFile = $entry.OldDdsFilePath
$newDdsFile = $entry.NewDdsFilePath

# Überprüfen Sie, ob der Pfad der Datei in der Hashtable der auszuschließenden Ordner enthalten ist
$parentFolder = (Get-Item (Split-Path $newDdsFile -Parent)).FullName
if ($excludeFoldersFeature -and ($excludedFolders.Keys | Where-Object { $parentFolder -like "$_*" })) {
# Fügen Sie Debug-Informationen zur Logdatei hinzu
Add-Content -Path $debugLogPath -Value "Skipping file $newDdsFile because it is in excluded folder $parentFolder"
continue
}

# Prüfen Sie die Größe der neuen dds Datei (NewDdsFilePath) vor dem Zurückkopieren
$newSizeBeforeCopy = (Get-Item $newDdsFile).Length

# Kopieren Sie die Datei und fangen Sie Fehler mit Try/Catch
try {
Copy-Item -Path $newDdsFile -Destination $oldDdsFile -ErrorAction Stop
} catch {
Write-Host "Fehler beim Kopieren der Datei: $newDdsFile"
continue
}

# Prüfen Sie die Größe der dds Datei im originalen Verzeichnis (also die Datei am gleichen absoluten Pfad) nach dem Zurückkopieren.
$newSizeAfterCopy = (Get-Item $oldDdsFile).Length

# Überprüfen Sie, ob die Datei erfolgreich kopiert wurde ( Anhand der grösseren Dateigrösse)
if ($newSizeBeforeCopy -eq $newSizeAfterCopy) {
$successfulCopies++
} elseif ($newSizeBeforeCopy -ne $newSizeAfterCopy) {
$notSameSizeFiles += [PSCustomObject]@{
OldDdsFilePath = $oldDdsFile
NewDdsFilePath = $newDdsFile
}
}

# Aktualisieren Sie die Fortschrittsanzeige alle 200 Dateien
if ($i % 200 -eq 0) {
Write-Progress -Activity "Kopieren von Dateien" -Status "$i von $totalFiles Dateien verarbeitet" -PercentComplete ($i / $totalFiles * 100)
}
}

if ($notSameSizeFiles.Count -gt 0) {
Write-Host "Die folgenden Dateien wurden vermutlich nicht richtig kopiert:"
$notSameSizeFiles | ForEach-Object { Write-Host "OldDdsFilePath: $($_.OldDdsFilePath), NewDdsFilePath: $($_.NewDdsFilePath)" }
}

} else {
Write-Host "Kopiervorgang wurde abgebrochen."
}

# Geben Sie die Anzahl der erfolgreich kopierten Dateien aus
Write-Host "Es wurden $successfulCopies Dateien kopiert"


Kurz erklärt, was das Skript macht: Es kopiert Die Dateien, die ich im Skript1 kopiert habe, wieder zurück an den ursprungsort. Natürlich wäre das relativ einfach, aber ich habe unzählige features eingebaut. Eins will nicht funktionieren:

Es geht um das $excludedFoldersFeature bzw. genauer gesagt geht es um diese Zeile in der "kopier-schleife":

# Überprüfen Sie, ob der Pfad der Datei in der Hashtable der auszuschließenden Ordner enthalten ist
$parentFolder = (Get-Item (Split-Path $newDdsFile -Parent)).FullName
if ($excludeFoldersFeature -and ($excludedFolders.Keys | Where-Object { $parentFolder -like "$_*" })) {
# Fügen Sie Debug-Informationen zur Logdatei hinzu
Add-Content -Path $debugLogPath -Value "Skipping file $newDdsFile because it is in excluded folder $parentFolder"
continue
}

Die $excludedFolders werden nämlich alle korrekt gebildet. Ich bin mir eig. zu 99% sicher, dass das Problem an der falschen Pfadvergleichen liegt. Ich kriege es einfach nicht hin, dass getestet wird, ob der die $newDdsFile im $excludedFolder vorkommt. In worten gefasst, ist das ziemlich einfach, aber ich habe probleme:

Wenn ich -eq nehme, dann wird die $newDdsFile nur erkannt, wenn die Datei direkt im $excludedFolder ist (die sind aber einfach irgendwie im $excludedFolder, also in einem subfolder). Das geht also nicht.

wenn ich -like nehme, dann werden aber die falschen Ordner gematcht. Hier muss man vlt. erwähnen, dass das $excludedFoldersFeature funktioniert, wenn das $useAlternativeExcludePattern nicht auf $true ist. Das liegt ganz einfach daran, dass die Namen dieser Ordner zufälligerweise nicht mehrdeutig sind, sonst würde auch das nicht funktionieren.

Wenn das $useAlternativeExcludePattern aktiviert ist, funktioniert es nicht. Dieses Pattern ist ja im Grunde "Alle Ordner, die nicht folgendes Muster haben". Und wenn ich nun als Ordner den Orden "baseFolder\DXT5" kriege, dann werden nachher mit dem -like Operator auch alle Ordner gematcht, die so aussehen: "baseFolder\DXT5_color"

Mein Problem jetzt: ich weiss nicht, was es für Befehle gibt, die (möglichst perfomant) prüfen, was ich will. Ich will(versuche) folgendes (Beispiel):

$excludedFolder-Ordner: "baseFolder\$excludedFolder"
$newDdsFile: "baseFolder\$excludedFolder\Irgendetwas\Irgendetwas....\$newDdsFile"

Das Beispiel soll matchen. Ich denke, ihr versteht schon (sorry, bin ChatGPT gewöhnt xD).

Aber wie setze ich das um?

Edit: Falls jemand Dateien zum testen will, habe ich hier einen kleinen Ordner:

Nicht-Upgescaled:https://uploadnow.io/f/7SQtJhK

Upgescaled:https://uploadnow.io/f/JqxyWYb

falls ihr testen wollt, dann könnt ihr das so machen: Der Ordner "dl1" mit dem Skript 1 irgendwohin kopieren (man braucht die mapping.csv) und dort dann die entstandenen Ordner löschen und die Ordner aus dem Upscaled-Ordner rein kopieren. Dann sollte das funktionieren (habe ich auch so gemacht). Geht aber sicher, dass im "dl1" ordner direkt die gamedaten sind und nicht noch ein weiterer "dl1" ordner, wegen dem entpacken der zip-file.
ich kann euch ja nicht meine mapping.csv geben, da die ja auf meinen Pfaden basieren.

Ansonsten: Vlt. wollt ihr ja gar nicht testen und jemand ihr weiss direkt, wie man das macht, was bei mir nicht funktioniert.

Platos
2023-12-15, 01:26:48
Update: Ich habs geschafft nach ner Pause und nem "neustart".

Das sollte soweit funktionieren:

Version 12.0:

# Lesen der CSV-Datei mit den Zuordnungen von .txt- zu .dds-Dateien
$mapping = Import-Csv -Delimiter "," -Path "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Definieren Sie eine Variable, um das Feature zum Auslassen von Ordnern beim Kopieren ein- und auszuschalten
$excludeFoldersFeature = $true # Setzen Sie dies auf $false, um das Feature auszuschalten

# Definieren Sie eine Variable, um zu steuern, welches Muster verwendet werden soll
$useAlternativeExcludePattern = $false # Setzen Sie dies auf $false, damit das normale Muster ($excludedFolderPattern) aktiv ist (wenn $excludedFoldersFeature auf $true ist)

# Definieren Sie den Basispfad und das Regex-Muster für die auszulassenden Ordner
$baseFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled" # Ersetzen Sie dies durch den tatsächlichen Basispfad
$excludedFolderPattern = ".*(_UltraLowRes|_color)$" # Ersetzen Sie dies wenn gewünscht durch ein eigenes Regex-Muster

# Definieren Sie das Regex-Muster für die Ordner, die nicht ausgeschlossen werden sollen (also alles, bis auf das Muster, wird ausgeschlossen)
$excludedInverseFolderPattern = ".*(_UltraLowRes|_color)$" # Ersetzen Sie dies wenn gewünscht durch ein eigenes Regex-Muster

# Ermitteln Sie alle Ordner im Basisverzeichnis, die mit dem ausgewählten Regex-Muster übereinstimmen, wenn das Feature aktiviert ist
$excludedFolders = @{}
Get-ChildItem -Path $baseFolder -Directory | ForEach-Object {
if ($excludeFoldersFeature) {
if ($useAlternativeExcludePattern) {
if ($_.Name -notmatch $excludedInverseFolderPattern) {
$excludedFolders[$_.FullName] = $true
}
} else {
if ($_.Name -match $excludedFolderPattern) {
$excludedFolders[$_.FullName] = $true
}
}
}
}
$excludedFolderNames = $excludedFolders.Keys | ForEach-Object { $_.Replace($baseFolder, "") }

# Definieren Sie den Pfad zur Debug-Logdatei
$debugLogPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\debuglog.txt"

# Fügen Sie die absoluten Pfade der $excludedFolders zur Debug-Logdatei hinzu
Add-Content -Path $debugLogPath -Value "Excluded folders:"
$excludedFolders.Keys | ForEach-Object { Add-Content -Path $debugLogPath -Value $_ }

# Initialisieren Sie die Zählvariablen und die Gruppen
$unchangedFiles = @()
$largerSizeFiles = @()
$nonExistentFiles = @{} # Initialisieren Sie die Hashtable für nicht existierende Dateien

# Durchlaufen Sie alle Einträge in der CSV-Datei
foreach ($entry in $mapping) {

# Überprüfen Sie, ob die Dateien existieren
if ((Test-Path $entry.OldDdsFilePath) -and (Test-Path $entry.NewDdsFilePath)) {
# Überprüfen Sie, ob der Pfad der Datei in der Liste der auszulassenden Ordner enthalten ist, wenn das Feature aktiviert ist
$parentFolder = (Get-Item (Split-Path $entry.NewDdsFilePath -Parent)).FullName
if ($excludeFoldersFeature -and ($excludedFolders.Keys | Where-Object { $parentFolder.StartsWith("$_\") })) {
continue
}

# Vergleichen Sie die Dateigrößen
$oldSize = (Get-Item $entry.OldDdsFilePath).Length
$newSize = (Get-Item $entry.NewDdsFilePath).Length

if ($newSize -gt $oldSize) {
$largerSizeFiles += [PSCustomObject]@{
OldDdsFilePath = $entry.OldDdsFilePath
NewDdsFilePath = $entry.NewDdsFilePath
NewSize = $newSize
}
} elseif ($newSize -eq $oldSize) {
$unchangedFiles += [PSCustomObject]@{
OldDdsFilePath = $entry.OldDdsFilePath
NewDdsFilePath = $entry.NewDdsFilePath
NewSize = $newSize
}
}
} else {
# Fügen Sie die Pfade der nicht existierenden Dateien zur Hashtable hinzu
$nonExistentFiles[$entry.OldDdsFilePath] = $entry.NewDdsFilePath
}

# Aktualisieren Sie die Fortschrittsanzeige alle 200 Dateien
if ($i % 200 -eq 0) {
Write-Progress -Activity "Kopieren von Dateien" -Status "$i von $($mapping.Count) Dateien verarbeitet" -PercentComplete ($i / $mapping.Count * 100)
}

# Erhöhen Sie die Zählvariable für die Fortschrittsanzeige
$i++
}


# Fügen Sie eine Warnung hinzu, falls Dateien nicht gefunden werden konnten
$notFoundMessage = if ($nonExistentFiles.Count -eq 0) { "Alle Dateien wurden gefunden." } else { "$($nonExistentFiles.Count) Dateien wurden nicht gefunden." }
$notFoundText = if ($nonExistentFiles.Count -gt 0) { " oder nicht gefunden wurden" } else { "" }

# Fragen Sie den Benutzer, ob er die Pfade und die Anzahl der Dateien in eine Logdatei schreiben möchte
$writeToLog = Read-Host "$notFoundMessage Es wurden $($unchangedFiles.Count) Dateien mit gleicher Dateigröße und $($largerSizeFiles.Count) Dateien mit unterschiedlicher Dateigröße gefunden. `nMöchten Sie die Pfade und die Anzahl der Dateien, die die gleiche Größe haben$notFoundText, in eine Logdatei schreiben? Die Logfile würde im baseFolder erstellt werden (J/N)"

if ($writeToLog -eq 'J') {
$logPath = Join-Path -Path $baseFolder -ChildPath "beforeCopyLog.txt"
# Schreiben Sie die Überschrift für die nicht gefundenen Dateien in die Logdatei
Add-Content -Path $logPath -Value "Nicht gefundene Dateien"
Add-Content -Path $logPath -Value "OldDdsFilePath,NewDdsFilePath"

# Schreiben Sie die Pfade der nicht gefundenen Dateien in die Logdatei
foreach ($file in $nonExistentFiles.GetEnumerator()) {
Add-Content -Path $logPath -Value "$($file.Key),$($file.Value)"
}

# Fügen Sie eine zusätzliche Leerzeile hinzu
Add-Content -Path $logPath -Value "`r`n"

# Schreiben Sie die Überschrift für die Dateien mit der gleichen Größe in die Logdatei
Add-Content -Path $logPath -Value "Dateien mit gleicher Größe"
Add-Content -Path $logPath -Value "OldDdsFilePath,NewDdsFilePath"

# Schreiben Sie die Pfade der Dateien mit der gleichen Größe in die Logdatei
foreach ($file in $unchangedFiles) {
Add-Content -Path $logPath -Value "$($file.OldDdsFilePath),$($file.NewDdsFilePath)"
}

# Fügen Sie eine zusätzliche Leerzeile hinzu
Add-Content -Path $logPath -Value "`r`n"
}

# Fügen Sie die ausgeschlossenen Ordner in die Meldung ein
$excludedFoldersMessage = if ($excludeFoldersFeature) { "Es wurden folgende Pfade vom Kopiervorgang ausgenommen: $excludedFolderNames." } else { "" }

# Fragen Sie den Benutzer, ob den Kopiervorgang starten will
$readHost = Read-Host "$excludedFoldersMessage Es wurden $($unchangedFiles.Count) Dateien mit gleicher Dateigröße und $($largerSizeFiles.Count) Dateien mit unterschiedlicher Dateigröße gefunden. `nMöchten Sie den Kopiervorgang jetzt starten? (J/N)"
if ($readHost -eq 'J') {

# Initialisieren Sie die Zählvariable und die Liste der kopierten Dateien
$successfulCopies = 0
$notSameSizeFiles = @()
$totalFiles = $mapping.Count

for ($i = 0; $i -lt $totalFiles; $i++) {
$entry = $mapping[$i]
$oldDdsFile = $entry.OldDdsFilePath
$newDdsFile = $entry.NewDdsFilePath

# Überprüfen Sie, ob der Pfad der Datei in der Hashtable der auszuschließenden Ordner enthalten ist
$parentFolder = (Get-Item (Split-Path $newDdsFile -Parent)).FullName
if ($excludeFoldersFeature -and ($excludedFolders.Keys | Where-Object { $parentFolder.StartsWith("$_\") })) {
# Fügen Sie Debug-Informationen zur Logdatei hinzu
Add-Content -Path $debugLogPath -Value "Skipping file $newDdsFile because it is in excluded folder $parentFolder"
continue
}


# Prüfen Sie die Größe der neuen dds Datei (NewDdsFilePath) vor dem Zurückkopieren
$newSizeBeforeCopy = (Get-Item $newDdsFile).Length

# Kopieren Sie die Datei und fangen Sie Fehler mit Try/Catch
try {
Copy-Item -Path $newDdsFile -Destination $oldDdsFile -ErrorAction Stop
} catch {
Write-Host "Fehler beim Kopieren der Datei: $newDdsFile"
continue
}

# Prüfen Sie die Größe der dds Datei im originalen Verzeichnis (also die Datei am gleichen absoluten Pfad) nach dem Zurückkopieren.
$newSizeAfterCopy = (Get-Item $oldDdsFile).Length

# Überprüfen Sie, ob die Datei erfolgreich kopiert wurde ( Anhand der grösseren Dateigrösse)
if ($newSizeBeforeCopy -eq $newSizeAfterCopy) {
$successfulCopies++
} elseif ($newSizeBeforeCopy -ne $newSizeAfterCopy) {
$notSameSizeFiles += [PSCustomObject]@{
OldDdsFilePath = $oldDdsFile
NewDdsFilePath = $newDdsFile
}
}

# Aktualisieren Sie die Fortschrittsanzeige alle 200 Dateien
if ($i % 200 -eq 0) {
Write-Progress -Activity "Kopieren von Dateien" -Status "$i von $totalFiles Dateien verarbeitet" -PercentComplete ($i / $totalFiles * 100)
}
}

if ($notSameSizeFiles.Count -gt 0) {
Write-Host "Die folgenden Dateien wurden vermutlich nicht richtig kopiert:"
$notSameSizeFiles | ForEach-Object { Write-Host "OldDdsFilePath: $($_.OldDdsFilePath), NewDdsFilePath: $($_.NewDdsFilePath)" }
}

} else {
Write-Host "Kopiervorgang wurde abgebrochen."
}

# Geben Sie die Anzahl der erfolgreich kopierten Dateien aus
Write-Host "Es wurden $successfulCopies Dateien kopiert"


Jetzt muss ich mir nur noch eine bessere "erfolgreich kopiert" Prüfung ausdenken.

Edit2: Habe mal auf Version 12_3 aktualisiert:

# Lesen der CSV-Datei mit den Zuordnungen von .txt- zu .dds-Dateien
$mapping = Import-Csv -Delimiter "," -Path "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\mapping.csv"

# Definieren Sie eine Variable, um das Feature zum Auslassen von Ordnern beim Kopieren ein- und auszuschalten
$excludeFoldersFeature = $true # Setzen Sie dies auf $false, um das Feature auszuschalten

# Definieren Sie eine Variable, um zu steuern, welches Muster verwendet werden soll
$useAlternativeExcludePattern = $false # Setzen Sie dies auf $false, damit das normale Muster ($excludedFolderPattern) aktiv ist (wenn $excludedFoldersFeature auf $true ist)

# Definieren Sie den Basispfad und das Regex-Muster für die auszulassenden Ordner
$baseFolder = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled" # Ersetzen Sie dies durch den tatsächlichen Basispfad
$excludedFolderPattern = ".*(_UltraLowRes|_color)$" # Ersetzen Sie dies wenn gewünscht durch ein eigenes Regex-Muster

# Definieren Sie das Regex-Muster für die Ordner, die nicht ausgeschlossen werden sollen (also alles, bis auf das Muster, wird ausgeschlossen)
$excludedInverseFolderPattern = ".*(_UltraLowRes|_color)$" # Ersetzen Sie dies wenn gewünscht durch ein eigenes Regex-Muster

# Ermitteln Sie alle Ordner im Basisverzeichnis, die mit dem ausgewählten Regex-Muster übereinstimmen, wenn das Feature aktiviert ist
$excludedFolders = @{}
Get-ChildItem -Path $baseFolder -Directory | ForEach-Object {
if ($excludeFoldersFeature) {
if ($useAlternativeExcludePattern) {
if ($_.Name -notmatch $excludedInverseFolderPattern) {
$excludedFolders[$_.FullName] = $true
}
} else {
if ($_.Name -match $excludedFolderPattern) {
$excludedFolders[$_.FullName] = $true
}
}
}
}
$excludedFolderNames = $excludedFolders.Keys | ForEach-Object { $_.Replace($baseFolder, "") }

# Definieren Sie den Pfad zur Debug-Logdatei
$debugLogPath = "C:\Users\Gaming-Tower\Pictures\Textur-Upscaling\DDDA\1-scripttest\upscaled\debuglog.txt"

# Fügen Sie die absoluten Pfade der $excludedFolders zur Debug-Logdatei hinzu
Add-Content -Path $debugLogPath -Value "Excluded folders:"
$excludedFolders.Keys | ForEach-Object { Add-Content -Path $debugLogPath -Value $_ }

# Erstellung der $filesToCopy Variable, die bei aktiviertem $excludedFoldersFeature nur diejenigen Dateien beinhaltet, die NICHT exkludiert werden.
if ($excludeFoldersFeature) {
$filesToCopy = @()
foreach ($entry in $mapping) {
$parentFolder = (Get-Item (Split-Path $entry.NewDdsFilePath -Parent)).FullName
if ($excludedFolders.Keys | Where-Object { $parentFolder.StartsWith("$_\") }) {
continue
}
$filesToCopy += $entry
}
} else {
# Wenn $excludeFoldersFeature nicht aktiviert ist, verwenden Sie $mapping
$filesToCopy = $mapping
}

# Erstellen Sie eine "ausgeschlossenen Ordner"-Meldung, die nur deren Ordnernamen ausgibt.
$excludedFoldersMessage = if ($excludeFoldersFeature) { "Es wurden folgende Pfade vom Kopiervorgang ausgenommen: $excludedFolderNames." } else { "" }

# Fragen Sie den Benutzer, ob er auf Diagnose und Sicherheitschecks verzichten möchte
$skipChecks = Read-Host "$excludedFoldersMessage. Es wurden $($filesToCopy.Count) Einträge in Ihrer .csv-Datei gefunden. `nWollen Sie NICHT prüfen, ob die Dateien wirklich existieren und auf jegliche Diagnose und Sicherheitschecks verzichten und einfach nur die Dateien kopieren (NICHT EMPFOHLEN)? (J/N)"

# Setzen Sie eine Variable, die bestimmt, ob die Checks ausgeführt werden sollen oder nicht
$performChecks = $skipChecks -ne 'J'

# Führen Sie die foreach-Schleife und die Checks nur aus, wenn $performChecks auf $true gesetzt ist
if ($performChecks) {

# Initialisieren Sie die Zählvariablen und die Gruppen
$unchangedFiles = @()
$largerSizeFiles = @()
$nonExistentFiles = @{} # Initialisieren Sie die Hashtable für nicht existierende Dateien

# Durchlaufen Sie alle Einträge in der CSV-Datei (bzw der $filesToCopy Variable)
foreach ($entry in $filesToCopy) {

# Überprüfen Sie, ob die Dateien existieren
if ((Test-Path $entry.OldDdsFilePath) -and (Test-Path $entry.NewDdsFilePath)) {

# Vergleichen Sie die Dateigrößen
$oldSize = (Get-Item $entry.OldDdsFilePath).Length
$newSize = (Get-Item $entry.NewDdsFilePath).Length

if ($newSize -gt $oldSize) {
$largerSizeFiles += [PSCustomObject]@{
OldDdsFilePath = $entry.OldDdsFilePath
NewDdsFilePath = $entry.NewDdsFilePath
NewSize = $newSize
}
} elseif ($newSize -eq $oldSize) {
$unchangedFiles += [PSCustomObject]@{
OldDdsFilePath = $entry.OldDdsFilePath
NewDdsFilePath = $entry.NewDdsFilePath
NewSize = $newSize
}
}
} else {
# Fügen Sie die Pfade der nicht existierenden Dateien zur Hashtable hinzu
$nonExistentFiles[$entry.OldDdsFilePath] = $entry.NewDdsFilePath
}

# Aktualisieren Sie die Fortschrittsanzeige alle 200 Dateien
if ($i % 200 -eq 0) {
Write-Progress -Activity "Kopieren von Dateien" -Status "$i von $($filesToCopy.Count) Dateien verarbeitet" -PercentComplete ($i / $filesToCopy.Count * 100)
}

# Erhöhen Sie die Zählvariable für die Fortschrittsanzeige
$i++
}


if ($nonExistentFiles.Count -gt 0 -or $unchangedFiles.Count -gt 0) {
# Erstellen Sie eine Warnung, falls Dateien nicht gefunden werden konnten
$notFoundMessage = if ($nonExistentFiles.Count -eq 0) { "Alle Dateien wurden gefunden." } else { "$($nonExistentFiles.Count) Dateien wurden nicht gefunden." }
$notFoundText = if ($nonExistentFiles.Count -gt 0) { " oder nicht gefunden wurden" } else { "" }

# Fragen Sie den Benutzer, ob er die Pfade und die Anzahl der Dateien in eine Logdatei schreiben möchte, bevor er kopiert
$writeToLog = Read-Host "$notFoundMessage Es wurden $($unchangedFiles.Count) Dateien mit gleicher Dateigröße und $($largerSizeFiles.Count) Dateien mit unterschiedlicher Dateigröße gefunden. `nMöchten Sie die Pfade und die Anzahl der Dateien, die die gleiche Größe haben$notFoundText, in eine Log-Datei schreiben und im baseFolder speichern, bevor sie die Dateien kopieren? (J/N)"

if ($writeToLog -eq 'J') {
$logPath = Join-Path -Path $baseFolder -ChildPath "beforeCopyLog.txt"
# Schreiben Sie die Überschrift für die nicht gefundenen Dateien in die Logdatei
Add-Content -Path $logPath -Value "Nicht gefundene Dateien"
Add-Content -Path $logPath -Value "OldDdsFilePath,NewDdsFilePath"

# Schreiben Sie die Pfade der nicht gefundenen Dateien in die Logdatei
foreach ($file in $nonExistentFiles.GetEnumerator()) {
Add-Content -Path $logPath -Value "$($file.Key),$($file.Value)"
}

# Fügen Sie eine zusätzliche Leerzeile hinzu
Add-Content -Path $logPath -Value "`r`n"

# Schreiben Sie die Überschrift für die Dateien mit der gleichen Größe in die Logdatei
Add-Content -Path $logPath -Value "Dateien mit gleicher Größe"
Add-Content -Path $logPath -Value "OldDdsFilePath,NewDdsFilePath"

# Schreiben Sie die Pfade der Dateien mit der gleichen Größe in die Logdatei
foreach ($file in $unchangedFiles) {
Add-Content -Path $logPath -Value "$($file.OldDdsFilePath),$($file.NewDdsFilePath)"
}

# Fügen Sie eine zusätzliche Leerzeile hinzu
Add-Content -Path $logPath -Value "`r`n"
}
}

# Fragen Sie den Benutzer, ob er die Pfade und die Anzahl der zu kopierenden Dateien in eine separate Logdatei schreiben möchte, bevor er kopiert
$writeToCopyLog = Read-Host "Wollen Sie die Anzahl und Pfade der zu kopierenden Dateien in eine separate Log-Datei schreiben und im baseFolder speichern, bevor sie die Dateien kopieren? (J/N)"

if ($writeToCopyLog -eq 'J') {
$copyLogPath = Join-Path -Path $baseFolder -ChildPath "FilesToCopyLog.txt"
# Schreiben Sie die Überschrift für die zu kopierenden Dateien in die Logdatei
Add-Content -Path $copyLogPath -Value "Zu Kopierende Dateien"

# Schreiben Sie den Subheader und die Pfade der Dateien mit unterschiedlicher Größe in die Logdatei
Add-Content -Path $copyLogPath -Value "`r`nDateien unterschiedlicher Grösse zwischen"
Add-Content -Path $copyLogPath -Value "OldDdsFilePath,NewDdsFilePath"
foreach ($file in $largerSizeFiles) {
Add-Content -Path $copyLogPath -Value "$($file.OldDdsFilePath),$($file.NewDdsFilePath)"
}

# Schreiben Sie den Subheader und die Pfade der Dateien mit der gleichen Größe in die Logdatei
Add-Content -Path $copyLogPath -Value "`r`nDateien gleicher Grösse"
Add-Content -Path $copyLogPath -Value "OldDdsFilePath,NewDdsFilePath"
foreach ($file in $unchangedFiles) {
Add-Content -Path $copyLogPath -Value "$($file.OldDdsFilePath),$($file.NewDdsFilePath)"
}

# Fügen Sie eine zusätzliche Leerzeile hinzu
Add-Content -Path $copyLogPath -Value "`r`n"
}

# Fragen Sie den Benutzer, ob er den Kopiervorgang starten will
$readHost = Read-Host "$excludedFoldersMessage `nEs wurden $($filesToCopy.Count) Dateien zum Kopieren, $($unchangedFiles.Count) Dateien mit gleicher Dateigröße und $($largerSizeFiles.Count) Dateien mit unterschiedlicher Dateigröße gefunden. `nMöchten Sie den Kopiervorgang jetzt starten? (J/N)"
}
if ($readHost -eq 'J' -or $skipChecks -eq 'J') {

# Initialisieren Sie die Zählvariable und die Liste der kopierten Dateien
$successfulCopies2 = 0
$notSameSizeFiles = @()
$totalFiles = $filesToCopy.Count
$successfulCopies = 0

for ($i = 0; $i -lt $totalFiles; $i++) {
$entry = $filesToCopy[$i]
$oldDdsFile = $entry.OldDdsFilePath
$newDdsFile = $entry.NewDdsFilePath

# Prüfen Sie die Größe der neuen dds Datei (NewDdsFilePath) vor dem Zurückkopieren
$newSizeBeforeCopy = (Get-Item $newDdsFile).Length

# Kopieren Sie die Datei und fangen Sie Fehler mit Try/Catch
try {
Copy-Item -Path $newDdsFile -Destination $oldDdsFile -ErrorAction Stop

# Erhöhen Sie den Zähler
$successfulCopies++
} catch {
Write-Host "Fehler beim Kopieren der Datei: $newDdsFile"
continue
}


# Prüfen Sie die Größe der dds Datei im originalen Verzeichnis (also die Datei am gleichen absoluten Pfad) nach dem Zurückkopieren.
$newSizeAfterCopy = (Get-Item $oldDdsFile).Length

# Überprüfen Sie, ob die Datei erfolgreich kopiert wurde ( Anhand der grösseren Dateigrösse)
if ($newSizeBeforeCopy -eq $newSizeAfterCopy) {
$successfulCopies2++
} elseif ($newSizeBeforeCopy -ne $newSizeAfterCopy) {
$notSameSizeFiles += [PSCustomObject]@{
OldDdsFilePath = $oldDdsFile
NewDdsFilePath = $newDdsFile
}
}

# Aktualisieren Sie die Fortschrittsanzeige alle 200 Dateien
if ($i % 200 -eq 0) {
Write-Progress -Activity "Kopieren von Dateien" -Status "$i von $totalFiles Dateien verarbeitet" -PercentComplete ($i / $totalFiles * 100)
}
}

if ($notSameSizeFiles.Count -gt 0) {
Write-Host "Die folgenden Dateien wurden vermutlich nicht richtig kopiert:"
$notSameSizeFiles | ForEach-Object { Write-Host "OldDdsFilePath: $($_.OldDdsFilePath), NewDdsFilePath: $($_.NewDdsFilePath)" }
}

} else {
Write-Host "Kopiervorgang wurde abgebrochen."
}

# Geben Sie die Anzahl der erfolgreich kopierten Dateien aus
Write-Host "Es wurden $successfulCopies Dateien kopiert"

# Geben Sie die Anzahl der erfolgreich kopierten Dateien aus
Write-Host "Es wurden $successfulCopies2 Dateien kopiert"