Jammer-Thread

Hier kann über allgemeine Themen diskutiert werden, die sonst in kein Forum passen.
Insbesondere über Szene, Games, Kultur, Weltgeschehen, Persönliches, Recht, Hard- und Software.
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Schrompf »

Naja. Wenn Du zwei Seiten konstruiertem Code brauchst, nur um zu demonstrieren, dass Implizite Int-Zu-Enum-Konvertierung in Ärger enden kann, dann solltest Du schonmal darüber nachdenken, was für einen Punkt Du gerade klären willst. Speziell in C ist die Version mit nem Enum eine alltägliche Aktion und gegenüber reinen Ints oder #define CONSTANT zu bevorzugen.

Das gesagt: Ich hab die Statische Code-Analyse gelegentlich ausprobiert, und noch nie hat das Ding irgendwas von Relevanz gefunden. Alles, was es bietet, sind Style-Nörgeleien. Soll Microsoft ihren Codern also Steine in den Weg legen, wie sie wollen. Ich muss es ja nicht mitmachen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

C++ implementiert enum anscheinend als signed int (was ich auch nicht wusste, aber ich programmiere auch nicht den ganzen Tag C++) und der Artikel zeigt dass es relativ leicht (wenn auch dadurch nicht weniger falsch) ist jeden beliebigen anderen integer wert in ein enum zu casten.

Jeder außerhalb des C++ Ökosystems fragt sich an dem Punkt, warum er überhaupt ein enum benutzen soll, wenn er dann doch einen Teil der Typprüfung von Hand machen soll weil enum eben (anscheinend?) nur so tut als wäre es ein Typ.
Ja, absolut richtig. Ich finde die Art, wie enum in C/C++ umgesetzt wurde, auch sehr zweifelhaft.

Nun gibt es zwei Möglichkeiten, das Ganze anzugehen:
  1. Wir bauen unsere Software auf Modulen auf. Jedes Modul wird von jemandem geschrieben, der sich in C/C++ auskennt. Der validiert alle Parameter bei der Ein-/Ausgabe; schreibt ordentliche Tests (idealerweise *beweist* man die Tüchtigkeit); kümmert sich um vernünftige Fehlerbehandlung. Innendrin ist das Modul old-School-C, und man muss zur Wartung eben C/C++ verstehen. Aber von außen lassen sich die Module zusammenstecken und sind nahezu idiotensicher, auch von nicht-C/C++-Experten.
     
    Das ist die Schiene, die ich bevorzuge. Scheint auch Linux-Kernel-Methode zu sein(?)
     
  2. Wir ersetzen alle Legacy-Aspekte von C++ durch moderne Wrapper, die sich ungefähr so verhalten, wie man es von modernen Sprachen erwartet. Im Speziellen: Keine Arrays mehr, sondern nur noch std::vector oder gsl::span. Keine C-Strings mehr, sondern nur noch std::string. Und – ganz vor allem – keine Zeiger mehr, nur noch Smart Pointer!
     
    Das ist die Schiene, die Microsoft seit rund zehn Jahren fährt.
     
    Fun Fact: Diese Hilfsklassen und Wrapper für Unbedarfte sind von Experten entwickelte Module, wie ich sie oben beschrieben habe.
Enter Static Code Analysis, wie von Schrompf beschrieben.

Damit Code-Analyse (und Compiler-Warnungen) brauchbar auf Fehler hinweisen, müssen False Positives minimiert werden. Echte Fehler dürfen nicht im Rauschen unberechtigter Warnungen untergehen. Das ist wirklich die eine allerwichtigste Eigenschaft!

… aber genau das wird hier forciert. Es gibt völlig legitime Einsatzzwecke für enum als Index, die Experten mit gutem Gewissen geschrieben haben. Mehr noch – sie haben den Code verifiziert und getestet.

… und nun kommt die neue Version der statischen Analyse und schmeißt 100 Warnungen. Obwohl der Experte sich des ganzen völlig bewusst ist und vorher sicher gestellt hat, dass der Fall nicht eintreten kann. Und mit der nächsten Version kommen wieder 100 neue. Was tut man wohl?
Ich hab die Statische Code-Analyse gelegentlich ausprobiert, und noch nie hat das Ding irgendwas von Relevanz gefunden. Alles, was es bietet, sind Style-Nörgeleien. Soll Microsoft ihren Codern also Steine in den Weg legen, wie sie wollen. Ich muss es ja nicht mitmachen.
Bei mir ist es eigentlich schlimmer: Das Ding hat fünf Mal was von Relevanz gefunden (sogar von hoher!) und dabei um die 500 False Positives geschmissen. Das war um 2010. Mittlerweile bin ich bei 8000 False Positives und hab’s aufgegeben. Es kommt nichts anderes mehr als „benutz hier gsl::span anstelle Zeiger“ und „vermeide static_cast.

Ich war hier einer der ersten, die statische Analyse pushen wollten. Mittlerweile scheiße ich drauf; es ist nur noch Rauschen. Selbst clang-tidy steckt viel zu tief im Arsch von Googles Style Guides.

Anstatt dass Microsoft nun ein Memo durch die Firma schickt, „Bitte in allen Projekten enum ersetzen durch $Tolle_Wrapper_Klasse, dispatchen sie an Millionen Entwickler weltweit: „Yo, euer Code ist ab jetzt unsicher! Weil in der Funktion nur die Obergrenze getestet wird! Obwohl kein einziger Aufruf die Untergrenze brechen kann, was wir nicht wissen, obwohl ganz genau das der Zweck ist, wofür Code Analysis erfunden wurde!”

Und dazu konstruieren sie ein zwei-Seiten-Beispiel.

Also gibt man nach und baut den Test ein, und dann können plötzlich ganz viele Funktionen, die vorher beweisbar nicht fehlschlagen konnten, fehlschlagen. Aber niemand weiß, wie man den entsprechenden Pfad testet oder Testfälle dafür schreibt, denn er ist ja nicht erreichbar. Und nun hat Software, die immer etwas ganz Simples getan hat, plötzlich 2^20 neue Ausführungspfade, die aber in der Praxis unerreichbar sind und nur eingeführt wurden, um die False Positives loszuwerden. Also, weil enum als Index gefährlich ist.

Und weil wir uns dran gewöhnt haben, dass jetzt niemand mehr unsere Programme versteht und niemand mehr alle Pfade testen kann, testen und verifizieren wir auch nichts mehr. Wozu auch! Durch die zusätzlichen Tests sollte ja jetzt alles sicher sein, oder?

(Das Ergebnis ist Windows 10.)

Und nächste Woche hören wir dann, dass int unsicher ist und es darüber zu wenige Blog Posts gibt. Ist es ja wirklich – undefinierter Überlauf, unsigned Infection, die absurde Type Propagation – aber die Lösung ist nicht, jede int-Addition auf der Welt als Warnung zu markieren.

tl;dr: Microsofts Code-Analyse verwechselt Code-Analyse mit Style-Nörgeleien und Kultur-Memos. Das ist idiotisch.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Alexander Kornrumpf
Moderator
Beiträge: 2106
Registriert: 25.02.2009, 13:37

Re: Jammer-Thread

Beitrag von Alexander Kornrumpf »

Krishty hat geschrieben: 29.10.2020, 12:41 Wir bauen unsere Software auf Modulen auf. Jedes Modul wird von jemandem geschrieben, der sich in C/C++ auskennt. Der validiert alle Parameter bei der Ein-/Ausgabe; schreibt ordentliche Tests (idealerweise *beweist* man die Tüchtigkeit); kümmert sich um vernünftige Fehlerbehandlung. Innendrin ist das Modul old-School-C, und man muss zur Wartung eben C/C++ verstehen. Aber von außen lassen sich die Module zusammenstecken und sind nahezu idiotensicher, auch von nicht-C/C++-Experten.
 
Anstatt dass Microsoft nun ein Memo durch die Firma schickt, „Bitte in allen Projekten enum ersetzen durch $Tolle_Wrapper_Klasse, dispatchen sie an Millionen Entwickler weltweit: „Yo, euer Code ist ab jetzt unsicher! Weil in der Funktion nur die Obergrenze getestet wird! Obwohl kein einziger Aufruf die Untergrenze brechen kann, was wir nicht wissen, obwohl ganz genau das der Zweck ist, wofür Code Analysis erfunden wurde!”
Die beiden Punkte scheinen einander zu widersprechen. "Obwohl kein einziger Aufruf die Untergrenze brechen kann," ist eine Aussage, die nur innerhalb eines Moduls wahr sein kann. Der Artikel ist nach meinem Verständnis so gemeint, dass der Check an der Modulgrenze fehlt. Das Beispiel-Modul ist halt sehr klein, damit es in den Artikel passt?!

Ich weiß nicht ob ich die statische Code-Analyse, die da beworben wird, überschätze. Ich hatte es erstmal so verstanden, dass das Tool versteht was da passiert und einigermaßen korrekt checkt. Wenn das nicht der Fall ist, ist natürlich blöd, aber es ist ein langer Weg vom ursprünglichen "wer glaubt dass enum nur seinen eigenen Wertebereich annehmen kann ist ein Idiot" zu "Microsofts statische Code-Analyse ist kaputt"

Und irgendwie ist es natürlich auch ironisch, weil eines der Hauptargumente für C++ ja immer ist, dass es mehr statisch checken kann als jede andere Sprache (tm) und das hier ist ein klares Gegenbeispiel.

Oder anders gesagt, die beiden Aussagen, dass C++ das beste Typsystem weltweit überhaupt hat und dass eine pauschale Warnung bei static_cast falsch ist, weil der Programmierer ja wissen könnte was er tut, sind sicher nicht im selben Universum wahr.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Jammer-Thread

Beitrag von Spiele Programmierer »

Alexander Kornrumpf hat geschrieben: 29.10.2020, 16:30 Und irgendwie ist es natürlich auch ironisch, weil eines der Hauptargumente für C++ ja immer ist, dass es mehr statisch checken kann als jede andere Sprache (tm) und das hier ist ein klares Gegenbeispiel.
Äh, wo hast du das gehört? Das ist doch offensichtlicher Unsinn, wenn man sich jemals mit funktionalen Sprachen wie Haskell oder wenigstens Rust beschäftigt hat.

Möchte übrigens Rust für die Sprache mit den besten Enums nominieren. :)
100% sicher, oft ohne Overhead. Und sowas ist einfach mal super praktisch:

Code: Alles auswählen

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
Krishty hat geschrieben: 29.10.2020, 12:412. Wir ersetzen alle Legacy-Aspekte von C++ durch moderne Wrapper, die sich ungefähr so verhalten, wie man es von modernen Sprachen erwartet. Im Speziellen: Keine Arrays mehr, sondern nur noch std::vector oder gsl::span. Keine C-Strings mehr, sondern nur noch std::string. Und – ganz vor allem – keine Zeiger mehr, nur noch Smart Pointer!
Dieses Gerede mit "keine Raw Pointer mehr!" ist auch Unsinn. Smart-Pointer sind nützliche Abstraktionen, aber deshalb will ich natürlich trotzdem Objekte merken und zu Unterfunktionen durchreichen. Diese Hilfsklassen für automatischen Prüfungen für die C++ Core Guidelines funktionieren Berichten zufolge auch nicht sehr gut in der Praxis. Smart-Pointer können Speicherlecks vermeiden, aber eine speichersichere Sprache wird C++ damit noch lange nicht.

Sowas wie gsl::span finde ich aber übrigens sehr praktisch. Es ist sehr angenehm, Array-Zeiger und Länge zusammenzufassen und habe das schon seit längerem in meiner eigenen Basisbibliothek. Gibt es inzwischen ja auch als std::span, wobei man das nach typischer STL-Art noch mit mehrdimensionaler Indizierung verkompliziert hat und es eine zweite Version speziell für Strings gibt.

Um den Jammer-Thread aber noch gerecht zu werden: Man muss den Scheiß jedesmal inkludieren und mit der Microsoft Calling Convention werden "Span"/"ArrayView"-Objekte immer auf dem Stack übergeben. :(
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

Alexander Kornrumpf hat geschrieben: 29.10.2020, 16:30"Obwohl kein einziger Aufruf die Untergrenze brechen kann," ist eine Aussage, die nur innerhalb eines Moduls wahr sein kann. Der Artikel ist nach meinem Verständnis so gemeint, dass der Check an der Modulgrenze fehlt. Das Beispiel-Modul ist halt sehr klein, damit es in den Artikel passt?!

Ich weiß nicht ob ich die statische Code-Analyse, die da beworben wird, überschätze. Ich hatte es erstmal so verstanden, dass das Tool versteht was da passiert und einigermaßen korrekt checkt. Wenn das nicht der Fall ist, ist natürlich blöd
Ich gehe ziemlich sicher davon aus, dass das nicht der Fall ist. Ja, statische Analyse ist wirklich für ganz exakt diesen Fall entworfen – aber in der Praxis ist es Grütze. Vielleicht haben sie es stark verbessert (dann hätten sie dafür einen Blog-Post machen sollen!), aber ich benutze bspw. oft eine Funktion isWithin(lower, x, upper) anstelle von if(lower <= x && x <= upper) und kann dir sagen, dass das ausreicht, die Analyse entgleisen zu lassen. (Mit Makros wäre es kein Problem, LOL!)
aber es ist ein langer Weg vom ursprünglichen "wer glaubt dass enum nur seinen eigenen Wertebereich annehmen kann ist ein Idiot" zu "Microsofts statische Code-Analyse ist kaputt"
Höh? Mein ursprüngliches war „Wer sagt, dass enum zu int gefährlich ist und dass man es nur unter strengster Aufsicht durch eine hirntote Code-Analyse verwenden sollte, ist ein Idiot“.
Und irgendwie ist es natürlich auch ironisch, weil eines der Hauptargumente für C++ ja immer ist, dass es mehr statisch checken kann als jede andere Sprache (tm) und das hier ist ein klares Gegenbeispiel.

Oder anders gesagt, die beiden Aussagen, dass C++ das beste Typsystem weltweit überhaupt hat und dass eine pauschale Warnung bei static_cast falsch ist, weil der Programmierer ja wissen könnte was er tut, sind sicher nicht im selben Universum wahr.
Schließe mich Spiele an; das beste Typsystem ever ist es sicher nicht. Bzw. nur unter der Auflage, dass man User-Defined Types betrachtet, und keine built-ins wie int, enum, oder Vererbung, denn deren Regeln sind purer Brainfuck.

Und ja, Rust ist bekannt für statische Memory Safety. Ich kann’s leider mangels Erfahrung nicht mit C++ vergleichen.
Spiele Programmierer hat geschrieben: 29.10.2020, 17:14Sowas wie gsl::span finde ich aber übrigens sehr praktisch. Es ist sehr angenehm, Array-Zeiger und Länge zusammenzufassen und habe das schon seit längerem in meiner eigenen Basisbibliothek. Gibt es inzwischen ja auch als std::span, wobei man das nach typischer STL-Art noch mit mehrdimensionaler Indizierung verkompliziert hat und es eine zweite Version speziell für Strings gibt.

Um den Jammer-Thread aber noch gerecht zu werden: Man muss den Scheiß jedesmal inkludieren und mit der Microsoft Calling Convention werden "Span"/"ArrayView"-Objekte immer auf dem Stack übergeben. :(
Ja; ich hatte vor zehn Jahren mal eine eigene Span bzw. Range und alles drauf aufgebaut. Der Code ist wirklich gut lesbar, sogar wenn ich heute drauf zurückblicke. Habe aber alles rausgeschmissen weil Compiler und Calling Conventions die Optimierung nach Strich und Faden verkackt haben :(
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Schrompf »

Krishty hat geschrieben: 29.10.2020, 21:24Ja; ich hatte vor zehn Jahren mal eine eigene Span bzw. Range und alles drauf aufgebaut. Der Code ist wirklich gut lesbar, sogar wenn ich heute drauf zurückblicke. Habe aber alles rausgeschmissen weil Compiler und Calling Conventions die Optimierung nach Strich und Faden verkackt haben :(
Wie machst Du das heute? Immer nen Doppelpack Arguments aus Ptr* und size_t count übergeben?
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

Joa. Wobei size_t mit Anzahl der Elemente halt nur brauchbar ist, wenn man tatsächlich mit Zählern arbeitet. Sonst ist ein size_t mit der Größe in Bytes oder ein Iterator zum Ende besser.

Meistens habe ich begin * und end *, aber in den letzten Jahren kommen mir zunehmend Zweifel, ob meine Game Engine wirklich mehr als 2.147.483.648 Avatare rendern muss, und nicht ein int einfacher wäre.

Probleme, die man hat, wenn alle Projekte zwanghaft unter 100 B bleiben müssen ;)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Jonathan »

Thunderbird 78 hat jetzt auch zweifarbige, unfassbar hässliche und komplett gleich aussehende Icons. Bei anderen dummen Trends wie Account-Zwang oder permanente Zwangsupdates kann ich ja noch verstehen wieso man Hersteller eine Motivation hat die es rechtfertigt seine Kunden derart zu ärgern. Aber wie zum *** profitiert man von hässlichen Icons? Ich muss also schlussfolgern, dass da nur pure Bösheit hinter stecken kann. Man bin ich genervt.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
starcow
Establishment
Beiträge: 523
Registriert: 23.04.2003, 17:42
Echter Name: Mischa Schaub
Kontaktdaten:

Re: Jammer-Thread

Beitrag von starcow »

Jonathan hat geschrieben: 02.11.2020, 19:28 Thunderbird 78 hat jetzt auch zweifarbige, unfassbar hässliche und komplett gleich aussehende Icons. Bei anderen dummen Trends wie Account-Zwang oder permanente Zwangsupdates kann ich ja noch verstehen wieso man Hersteller eine Motivation hat die es rechtfertigt seine Kunden derart zu ärgern. Aber wie zum *** profitiert man von hässlichen Icons? Ich muss also schlussfolgern, dass da nur pure Bösheit hinter stecken kann. Man bin ich genervt.
Musste schmunzeln :-) Ist mir auch aufgefallen. Hab mich dann gefragt, ob ihnen möglicherweise die Arbeit auszugehen droht...
Freelancer 3D- und 2D-Grafik
mischaschaub.com
Benutzeravatar
Tiles
Establishment
Beiträge: 1990
Registriert: 11.01.2003, 13:21
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Tiles »

Das ist der Zwang modern aussehen zu müssen. Pfeif auf die Usability. Aber sei froh, immerhin zwei Farben, und nicht nur eine ^^
Free Gamegraphics, Freeware Games https://www.reinerstilesets.de
Die deutsche 3D Community: https://www.3d-ring.de
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4254
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Irgendwo gab's meine ich auch eine Studie, die zumindest experimentell farbige Icons als besser nutzbar feststellt. Ich muss bei monochromen Icons immer an meine Schwiegermutter denken, die eine Email schreiben wollte (auf dem Tablet) und den Material-Design schwebenden Stift-Knopf für ein schräg gestelltes "i" gehalten hat. Sie hat das natürlich nicht für einen Knopf gehalten, sondern für irgendwas, an dem man vorbei schauen muss... Kann ich irgendwie total verstehen. Ich hasse Schwebeknöpfe, wobei ich ihren Sinn ja verstehen kann...
Bild
Benutzeravatar
Tiles
Establishment
Beiträge: 1990
Registriert: 11.01.2003, 13:21
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Tiles »

Das ist auch so. Der Mensch ist ein Augentier. Und Farbe gehört genauso dazu wie Form. Stell dir einfach mal vor dir würde jemand das Syntaxhilighting im Code komplett abklemmen, mit der Begründung dass es mit monochromen Farben moderner aussieht. Das passiert grade mit den ganzen monochromen Icons :)

In deinem Shot hast du ja noch Glück, die sind wenigstens farbig und nicht reinweiss ...
Free Gamegraphics, Freeware Games https://www.reinerstilesets.de
Die deutsche 3D Community: https://www.3d-ring.de
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Jonathan »

Es gibt zwei Gründe, warum ich meinen Glauben an die Menschheit verloren habe: Der Umgang mit der Coronapandemie, und solche Icons:
firefox_2020-11-03_11-22-39.png
(In dem Fall wären sogar monochrome Icons besser gewesen, da die Farben hier absolut nichts beitragen, sondern nur das Bild unruhig machen...)
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Schrompf »

Ich stelle mir immer vor, dass sowas die Folge von nem Brand Management-Team sind, dass nach Jahren der Corporate Communication Strategy und "Die Firmenfarbe ist jetzt 0x8f3d46, bitte in allen Briefköpfen, Präsentationen und EMails verwenden" irgendwann partout keine Ahnung mehr haben, was sie noch tun sollen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Tiles
Establishment
Beiträge: 1990
Registriert: 11.01.2003, 13:21
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Tiles »

Ich bin inzwischen an vier von sieben Tagen damit beschäftigt den Code von Blender in Bforartists reinzumergen und anschliessend die Manual an die Neuerungen anzupassen. Dabei hab ich einen rappelvollen Tracker der auch mal abgearbeitet werden will -.-
Free Gamegraphics, Freeware Games https://www.reinerstilesets.de
Die deutsche 3D Community: https://www.3d-ring.de
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Jammer-Thread

Beitrag von Spiele Programmierer »

Laut dem Thunderbird-Team ist die Änderung, weil man jetzt die Farben aller Ordner einzeln einstellen kann.

Das Problem: Man kann immer nur einen Ordner auswählen und die Farbe ändern. Wenn man es farbenfroh haben will, ist das natürlich ein sehr langwieriges Unterfangen.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Jonathan »

Hm. Also ich habe glaube ich über 100 Ordner und Thunderbird auf 4 Rechnern installiert, ich mach das ganz bestimmt nicht von Hand.
Aber sowas müsste man ja eigentlich über Plugins automatisieren können. Muss man mal nachverfolgen.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Jonathan »

Uagh, C++ Standardbibliothek. Wobei, eigentlich eher ein Bücherregal. Oder Ein kurzes Brett an der Wand mit ein paar Heftchen drauf.

Eine Funktion um Whitespaces am Anfang und Ende eines Strings zu entfernen? Haha, hättest du wohl gerne. Bisher hab ich eine handvoll Funktionen von Stackoverflow runterkopiert, weil das natürlich einfacher ist, als boost einzubinden:

Code: Alles auswählen

static inline void ltrim(std::string &s) {
	s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
		return !std::isspace(ch);
	}));
}
Das tut auch im Prinzip, jetzt will ich aber halt auch mal Sonderzeichen haben. Und zwar UTF8, weil das macht man halt heute so. Eigentlich ist das ja auch bequem zu machen, man kann überall ganz unbekümmert seine std::strings verwenden, nur die Anzeigeroutine muss halt gucken ob irgendetwas UTF8 ist, der Rest der Logik bleibt gleich. Bis auf die trim Funktion natürlich.

Das Problem im Algorithmus oben ist nicht nur die isspace-Funktion, sondern natürlich auch das Iterieren über die Buchstaben. Das beste was ich dazu momentan gefunden habe ist den String in einen wstring zu kopieren, dort zu trimmen und dann zurück zu kopieren. Ja geil! Wie kann es eigentlich sein, dass ein knapp 30 Jahre alter Standard weder von der Standardbibliothek noch von Boost auf benutzbare Weise unterstützt wird? Das ist doch albern.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Jammer-Thread

Beitrag von Spiele Programmierer »

Die C++ Standardbibliothek kann sehr viel, aber ist besonders gut bei Dingen, die niemand braucht. Sie ist zwar wahnsinnig kompliziert, aber das, was du wirklich brauchst, werden dann oft nicht unterstützt.

Ich habe die Hoffnung in die STL schon vor einer Weile aufgegeben.

Deine Routine funktioniert übrigens weitgehend auch mit UTF-8, solange du keine nicht-ASCII-Leerzeichen entfernen willst. Weitgehend, da isspace natürlich auch noch von der global gesetzten locale abhängt... Solltest du mal in die Situation geraten, dass du eine Bibliothek verwendest, die intern die locale umstellt, geht der Spaß richtig los...
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

Thunderbird 78
Der Dark Mode, der im Changelog steht, war doch schon vor einem Jahr drin?!

Außerdem:
https://www.mozilla.org/en-US/privacy/thunderbird/ hat geschrieben:Thunderbird receives data about your interactions with the application, such as whether calendars and filters are being used, and how many email accounts a user has.
Das öffnen sie als Hintergrund-Tab und gehen dann davon aus, dass ich damit meine Einwilligung erteilt habe?!

Die Beschreibung, wie man das abschaltet, ist völlig veraltet und funktioniert mit dem neuen großen modernen Optionsmenü nicht.

Ich hasse außerdem das schleichende Wachsen der Schriftgröße und der Margins. Wenn ich größeren Text haben möchte, stelle ich die DPI-Skalierung in Windows ein. Da muss nicht jede Anwendung selber vorpreschen und von Schriftgröße 10 auf 11 gehen, mit dem nächsten Update auf 12, … ich habe jetzt nur noch Mischmasch aus verschiedenen Schriftgrößen auf dem Monitor :(
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: Jammer-Thread

Beitrag von xq »

Jonathan hat geschrieben: 07.11.2020, 17:38 Das tut auch im Prinzip, jetzt will ich aber halt auch mal Sonderzeichen haben. Und zwar UTF8, weil das macht man halt heute so. Eigentlich ist das ja auch bequem zu machen, man kann überall ganz unbekümmert seine std::strings verwenden, nur die Anzeigeroutine muss halt gucken ob irgendetwas UTF8 ist, der Rest der Logik bleibt gleich. Bis auf die trim Funktion natürlich.

Das Problem im Algorithmus oben ist nicht nur die isspace-Funktion, sondern natürlich auch das Iterieren über die Buchstaben. Das beste was ich dazu momentan gefunden habe ist den String in einen wstring zu kopieren, dort zu trimmen und dann zurück zu kopieren. Ja geil! Wie kann es eigentlich sein, dass ein knapp 30 Jahre alter Standard weder von der Standardbibliothek noch von Boost auf benutzbare Weise unterstützt wird? Das ist doch albern.
So einfach isses leider nicht mit trim :(
Zum einen: Nach wstring und zurück ist nur eine Krücke und hilft nicht, wenn du mal sowas wie 🤮 wegtrimmen willst. Das ist nämlich f0 9f a4 ae in utf-8 und 3e d8 2e dd in utf-16, also auch nur ggf. single char mit wchar abbildbar (wchar kann ja auch ein utf-32 char sein)

Richtig kacke wirds dann bei sowas wie 👨‍👩‍👦‍👦 (f0 9f 91 a8 e2 80 8d f0 9f 91 a9 e2 80 8d f0 9f 91 a6 e2 80 8d f0 9f 91 a6), welches nicht mehr als ein einzelner Codepoint dargestellt werden kann. Das einzige, wie du sowas sauber machen kannst, ist wohl, über die Codepoints oder Grapheme Cluster zu iterieren und dabei das mitzunehmen/zu ignorieren, was du nicht haben willst.

TL;DR: Text auf Unicode-Ebene zu bearbeiten ist kacke.
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Jonathan »

Jo. Aber genau weil UTF8 so kompliziert ist wäre es doch so, so wichtig dass es dafür umfassenden und guten Support gibt. Ein trim auf ANSI Strings schreib ich mir schnell selber, kein Problem. Aber für UTF8 hab ich nicht eine Woche Zeit das gemäß aller Spezifikationen korrekt zu implementieren. Was dazu führt das vermutlich die allermeisten Programme UTF8 nicht richtig unterstützen sondern nur so weit wie es in den Tests aufgefallen ist oder so.
Bei Rust sind Strings einfach mal UTF8 und dann ist halt vieles schonmal einfach so richtig. Man kann dann zwar nicht mehr per s[2] auf das dritte Zeichen zugreifen (weil nicht klar ist ob der Rückgabewert ein Byte oder ein Zeichen sein sollte, wie groß dieses Zeichen wäre und weil man erwarten würde, dass es eine effiziente Operation ist), aber dadurch wird die Komplexität wenigstens nicht versteckt und man weiß womit man es zu tun hat und verhindert so Fehler die auftreten wenn die Welt mal gerade nicht dem eigenen zu simplen Bild entspricht.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
xq
Establishment
Beiträge: 1581
Registriert: 07.10.2012, 14:56
Alter Benutzername: MasterQ32
Echter Name: Felix Queißner
Wohnort: Stuttgart & Region
Kontaktdaten:

Re: Jammer-Thread

Beitrag von xq »

Das hat aber nichts mit UTF-8 zu tun. Du hast selbst bei UTF-32 (1 Codepoint == 1 Code Unit) das Problem, dass du mit s[1] nicht ein Zeichen bekommst, sondern in ein Zeichen reinindexierst.
Das Beispiel hier ist ja 👨‍👩‍👦‍👦, welches als UTF-32 kodiert so aussieht: 0001f468 0000200d 0001f469 0000200d 0001f466 0000200d 0001f466
U+200d ist der zero-width joiner, welcher hier diverse Personen zu einer Familie zusammenzieht. Die Familie ist dann Graphem, was aus dem Graphem-Cluster (die utf32-codierung) besteht.

Unicode ist leider einfach fürchterlich kompliziert. Utf-8 ist nur eine Kodierung davon
War mal MasterQ32, findet den Namen aber mittlerweile ziemlich albern…

Programmiert viel in ⚡️Zig⚡️ und nervt Leute damit.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Jammer-Thread

Beitrag von Spiele Programmierer »

Man kommt ziemlich weit, wenn man Unicode ignoriert. Das wundervolle bei UTF-8 ist, dass man fast alle Operationen genauso wie ANSI implementieren kann. Das beinhaltet Suchen, Ersetzen, Substrings, Verkettung und String-Formattierung.

In Strings reinindizieren ist nützlich für die effiziente Implementierung von allen möglichen String-Operationen, aber man sollte nicht annehmen, dass ein einzelnes Zeichen eine besondere Relevanz hat. Genauso ist es auch bei wchar_t und bei UTF-32 (Siehe hierzu die Beispiele von xq). Entweder du kennst den Textinhalt und weißt, dass er ASCII ist, oder du betrachtest ihn als Black Box variabler Länge.

Wenn man diese paar simplen Regeln befolgt, kann man absolut stressfrei mit UTF-8 arbeiten und braucht keine besonderen Funktionen. Es gibt ein paar Funktionen die leider im C++-Standard kaputt sind (z.B. tolower bzw. towlower, es gibt keine Möglichkeit mit Standardmitteln eine Datei mit einem Unicode-Pfad zu öffnen, usw.), aber die Strings selbst sind nicht das große Problem.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Jonathan »

Spiele Programmierer hat geschrieben: 09.11.2020, 20:12 Man kommt ziemlich weit, wenn man Unicode ignoriert. Das wundervolle bei UTF-8 ist, dass man fast alle Operationen genauso wie ANSI implementieren kann. Das beinhaltet Suchen, Ersetzen, Substrings, Verkettung und String-Formattierung.
Aber nehmen wir an, man hat einen Format-String ("Abzahl Gebäude: %1") der aus einer Textdatei mit Übersetzungen kommt. Jetzt steht vorne irgendetwas chinesisches (Hauptsache Multi-Byte Zeichen) und eines der Bytes in einem der Zeichen ist zufällig ein %. Schon geht die Formatierung leise und überraschend kaputt.
Ich meine, ja sobald man irgendetwas längeres sucht als ein einzelnes Zeichen ist so ein Problem vermutlich viel schwerer zu konstruieren. Aber das obige Beispiel erscheint mir schon irgendwie relevant.

Ich habe eben auch nochmal den Artikel zu Unicode-Äquivalenz überflogen: https://en.wikipedia.org/wiki/Unicode_equivalence
Es ist halt irgendwie schon sehr fraglich, ob man sowas in einem == Operator haben möchte. Wenn ich sowas vorhabe fände ich es vermutlich ok eine externe Bibliothek einzusetzen.

Auf der Anwenderseite hatte ich aber neulich auch ein ähnliches Problem mit Sonderzeichen in .bib Dateien für LaTeX. Da wird Unicode auch nicht gut unterstützt und man ist immer noch angehalten alle möglichen Zeichen mit Escapesequenzen zu kodieren. In einem Programm dessen Hauptfeature hübscher Textsatz ist. Das ist nicht das selbe, als wenn sich ein Online-Game an meinem Username verschluckt. Aber klar, in bib-Dateien kommen auch Haufenweise {, }, ;, : als Steuerzeichen vor. Vermutlich fließt das durch irgendeinen Standard-Parsergenerator und dann hat man nämlich genau wieder das Problem, dass man davon nichts als ANSI-String interpretieren darf, sondern überall aufpassen muss.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Schrompf
Moderator
Beiträge: 4838
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Schrompf »

Jonathan hat geschrieben: 10.11.2020, 09:02
Spiele Programmierer hat geschrieben: 09.11.2020, 20:12 Man kommt ziemlich weit, wenn man Unicode ignoriert. Das wundervolle bei UTF-8 ist, dass man fast alle Operationen genauso wie ANSI implementieren kann. Das beinhaltet Suchen, Ersetzen, Substrings, Verkettung und String-Formattierung.
Aber nehmen wir an, man hat einen Format-String ("Abzahl Gebäude: %1") der aus einer Textdatei mit Übersetzungen kommt. Jetzt steht vorne irgendetwas chinesisches (Hauptsache Multi-Byte Zeichen) und eines der Bytes in einem der Zeichen ist zufällig ein %. Schon geht die Formatierung leise und überraschend kaputt.
Nein, UTF8 ist explizit so gestaltet, dass sowas nicht vorkommen kann. Du kannst wirklich UTF8 einfach wie einen normalen String bearbeiten. Solange Du nix per Index machst, bist Du auf der sicheren Seite.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Spiele Programmierer
Establishment
Beiträge: 426
Registriert: 23.01.2013, 15:55

Re: Jammer-Thread

Beitrag von Spiele Programmierer »

Jonathan hat geschrieben: 10.11.2020, 09:02 Ich habe eben auch nochmal den Artikel zu Unicode-Äquivalenz überflogen: https://en.wikipedia.org/wiki/Unicode_equivalence
Es ist halt irgendwie schon sehr fraglich, ob man sowas in einem == Operator haben möchte. Wenn ich sowas vorhabe fände ich es vermutlich ok eine externe Bibliothek einzusetzen.
Darüber wie String-Vergleiche funktionieren sollen, kann man geteilter Meinung sein. Die meisten Sprachen verfolgen hier meines Wissens den Ansatz, dass normale Vergleiche nur die Codepoints vergleichen und nicht dem Unicode-Algorithmus folgen (sicher bin ich mir hier z.B. bei Rust, C# bzw. allgemein Dot.Net und, wenn auch nicht bewusst so geplant, C und C++). Dieser Ansatz hat auch Vorteile. Zum einen vermeidet er die relativ hohen Kosten des vollen Unicode-Äquivalenz-Algorithmus, die sonst bei allen nicht-ASCII-Stringvergleichen auftreten würden, und zum anderen wird der Algorithmus auch oft bei fremden APIs nicht eingesetzt. Wenn man es selbst dann anders macht, kommt es zu Inkompatiblitäten. Ein Beispiel hierzu sind Windows-Dateipfade. Man kann mit Windows zwei Dateien mit den gleichen Dateinamen erzeugen, wenn der Dateiname einen Umlaut enthält den man z.B. einmal als U+00E4 und einmal als U+0061 sowie U+0308 kodiert hat. Wenn man in diesem Fall beide Pfade als identisch einstuft, führt das eher zu noch mehr Problemen.

Das Performance-Problem könnte man dadurch mildern, indem man einen String-Typ hat der garantiert bereits in einer normalisierten Darstellung ist. Allerdings braucht man dann komplizierte (und etwas langsamere) Funktionen, um Strings zusammenzufügen, da dies theoretisch bei absurden Fällen die Normalform ändern könnte. Und dann hat man ein neues Problem, nämlich dass man Indices in den String vor dem Verketten nicht mehr weiterverwenden kann, da zumindest bei der NFC-Normalform irgendwas zusammengefasst werden könnte, sodass alle Indices verrutschen. Und trotz alle dem gäbe es einen erheblichen Overhead, wenn man beim Einlesen von fremden Strings immer erst eine Normalisierung durchführen muss. Und zu guter letzt, verschlimmert es das Windows-Dateipfad-Problem. Jetzt kann man manche Dateien mit nicht kanonischer Zeichen-Darstellung überhaupt nicht mehr öffnen.

In meinen Programmen halte ich es deswegen auch so, dass normale String-Vergleiche auch wirklich nur ganz einfach die Codepoints vergleichen. Nur an einigen besonderen Stellen, wo ich dies als vorteilhaft einschätze, normalisiere ich die Strings vorher explizit zu NFD bzw. NFKD. Typischerweise mache ich das z.B. bei Suchfunktionen für den Benutzer. Hier will man in der Regel so viel wie möglich finden und es wäre idiotisch wegen einer anderen internen Darstellung der Zeichen, dann manche Dinge nicht zu finden. Der C++-Standard hilft hier leider mal wieder überhaupt nicht. Wenn man Normalisierung machen will, dann muss man entweder selbst schreiben oder externe Bibliotheken verwenden.
Jonathan hat geschrieben: 10.11.2020, 09:02 Auf der Anwenderseite hatte ich aber neulich auch ein ähnliches Problem mit Sonderzeichen in .bib Dateien für LaTeX. Da wird Unicode auch nicht gut unterstützt und man ist immer noch angehalten alle möglichen Zeichen mit Escapesequenzen zu kodieren. In einem Programm dessen Hauptfeature hübscher Textsatz ist. Das ist nicht das selbe, als wenn sich ein Online-Game an meinem Username verschluckt. Aber klar, in bib-Dateien kommen auch Haufenweise {, }, ;, : als Steuerzeichen vor. Vermutlich fließt das durch irgendeinen Standard-Parsergenerator und dann hat man nämlich genau wieder das Problem, dass man davon nichts als ANSI-String interpretieren darf, sondern überall aufpassen muss.
Ja, dieses Problem hatte ich auch schon. Wobei ein einfacher ANSI-Parser schon ganz gute Chancen hat, dass er auch mit UTF-8 praktisch vollständig funktioniert. Warum man aber hier so versagt hat und warum das nicht wenigstens seit Jahrzehnten gefixt ist, das kann ich dir jetzt aber auch nicht sagen. Wahrscheinlich schreibt die Latex-Gemeinde einfach lieber ein neues Bib/BiberPDFXetex, anstatt ein bestehendes Programm nochmal anzufassen.

EDIT:
Habs mal gerade ausprobiert. Bei mir scheinen Umlaute mit Bibtex schon zu gehen.
Benutzeravatar
Jonathan
Establishment
Beiträge: 2353
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Jonathan »

Spiele Programmierer hat geschrieben: 10.11.2020, 13:27 Habs mal gerade ausprobiert. Bei mir scheinen Umlaute mit Bibtex schon zu gehen.
Meistens funktioniert das bei mir auch. In diesem Falle hatte ich ein (U+1E93) in Kombination mit biblatex und biber als backend. Mir fällt gerade auf, dass es auch an der Schriftart liegen könnte, vielleicht ginge es mit XeLaTeX statt LaTeX besser. Die Kombination aus all dem mag etwas ausgefallen sein, aber das ist ja gerade der Punkt: Es sollte alles einfach so immer funktionieren, aber immer mal wieder gibt es dann halt doch aus komischen Gründen Probleme. Sowohl mein Browser als auch mein Texteditor haben jedenfalls kein Problem mit diesem Zeichen, mein Textsatzprogramm aber schon. Und das halte ich im Jahr 2020 für massiv unbefriedigend.

Schrompf hat geschrieben: Nein, UTF8 ist explizit so gestaltet, dass sowas nicht vorkommen kann. Du kannst wirklich UTF8 einfach wie einen normalen String bearbeiten. Solange Du nix per Index machst, bist Du auf der sicheren Seite.
Ach. Das wusste ich nicht, ich sollte mir das mal näher angucken. Ich mag Unicode von der akademischen Seite weil es ein interessantes und komplexes und sehr relevantes Problem ist, aber als Anwender will ich manchmal einfach nur, dass alles funktioniert :D
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
Benutzeravatar
Tiles
Establishment
Beiträge: 1990
Registriert: 11.01.2003, 13:21
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Tiles »

Ich glaube die Entwickler von Far Cry 5 haben Lack gesoffen als sie das Ding entwickelt haben. Du hast ein Lootsystem das sich darauf reduziert genug Kugeln für deine Waffen zu kaufen bzw einzusammeln weil der restliche Plunder überhaupt nichts brauchbares zum Game beiträgt. Eine belanglose Story die dir immer wieder ins Gameplay reinpfuscht wenn du es gar nicht brauchen kannst. Unfaire Gegner die gern auch mal drei Magazine Kugeln aus der Automatischen schlucken bevor sie dich mit einem Pistolenschuss wegputzen. Immer gleiche Missionen. Hakelige Wackelsteuerung. Und ich stosse immer wieder auf fast unlösbare Missionen die dann beim dritten oder vierten Anlauf doch mal so funktionieren wie sie gedacht sind. Bei einer schwamm zum Beispiel ein Gegner unerreichbar unterm Terrain rum. Das heisst wenn du genug Munition findest um sie doch noch abschliessen zu können. Denn jetzt hänge ich an einer unschaffbaren Storymission die man noch nicht mal abbrechen kann, mit viel zu wenig Munition und Gegnern aus allen Richtungen. Und ich spiele schon auf Leicht weil mich das unfaire Gaming mit unpräziser Steuerung ankotzt. Das wars dann wohl mal wieder mit meinem Ausflug in das Shootergenre. Denn jetzt müsste ich erst mal von vorn anfangen. Hach ja, Gamebalance.

Wie kann man nur ein Multimillionendollargame so in den Sand setzen ...
Free Gamegraphics, Freeware Games https://www.reinerstilesets.de
Die deutsche 3D Community: https://www.3d-ring.de
Benutzeravatar
Krishty
Establishment
Beiträge: 8229
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

Krishty hat geschrieben: 22.06.2013, 16:05 Die Visual C++ CRT hat mit Visual Studio 2010 begonnen, die Listen der globalen Konstruktoren und Destruktoren durch EncodePointer() und DecodePointer() zu verschleiern. WTF?! Ich meine: Jeder Prozess ist voll von Zeigern. Durch Address Space Layout Randomization liegen die K’toren und D’toren sowieso immer an unterschiedlichen Adressen. Warum muss man sie noch zusätzlich obfuscaten?!

Wiedemauchsei: Die Hauptwirkung davon ist, dass Programme seit Visual C++ 2010 nicht mehr Windows 2000-kompatibel sind. Man kann sich die nötigen Dummy-Funktionen recht schnell im Assembler selber zusammentüfteln (der C++-Compiler ist keine Option, weil Sonderzeichen in den Namen vorkommen), aber … bah ist das kompliziert. Die meisten sehen das als blanke Schikane um die Leute von Windows 2000 wegzukriegen.

Anti-Jammer-Anteil: Jetzt bin ich Windows 2000-kompatibel.
… und Raymond Chen heute so: Mit Windows 10 wurde die Sicherheit von EncodePointer() rückgängig gemacht und ist nun völlig nutzlos.

Das ganze Theater, mit Windows 2000 & XP <SP2 zu brechen, war damit umsonst. Bzw. sah es eben nur damals nach einer guten Idee aus.

Microsoft. Do it for the lulz!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten